一、引用
Android中线程分为主线程和子线程,主线程主要处理和UI相关的操作,而主线程中则往往用于执行耗时操作,比如网络请求和I/O操作。除了Thread之外,Android中还有一些可以用来作为线程使用的类,如AsyncTask、HandlerThread 和 IntentService。经管他们的表现形式都和Thread有区别,但是本质上依然是线程。
二、Thread用法
new Thread(){
@Override
public void run() {
super.run();
// NetWork or DataBase Operation
}
}.start();
这是最简单的创建异步线程的姿势了,但是这样处理存在一些缺点:
- 创建及销毁线程消耗性能较大;
- 缺乏统一的管理;
- 优先级与UI线程一致,抢占资源处于同一起跑线;
- 匿名内部类默认持有外部类的引用,有内存泄漏的风险;
- 需要自己处理线程切换。
App启动阶段为避免在主线程创建线程池的资源消耗,使用的时候可以加上优先级的设置
new Thread () {
@Override
public void run() {
super.run();
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// NetWork or DataBase Operation
}
}.start();
三、AsyncTask
AsyncTask是一种轻量级的异步任务类,可以在线程池中执行后台任务,然后把任务执行的进度和结果传递给主线程并在主线程中更新UI。
public abstract class AsyncTask<Params, Progress, Result>
AsyncTask是一个抽象类,它提供了Params, Progress 和 Result这三个泛型参数,其中Params表示参数的类型,Progress表示任务执行的进度的参数类型,Result则表示任务返回的结果的参数类型,实际使用中如果不需要传递具体的参数,可以使用Void。
通常我们使用execute来启动AsyncTask
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
可以看到execute方法会调用executeOnExecutor,其中exec也就是sDefaultExecutor是一个线程池
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
SerialExecutor的execute方法会把mFuture对象插入到mTasks这个队列中,如果这时mActive == null(没有正在活动的AsyncTask任务),就会调用SerialExecutor的scheduleNext方法使用线程池THREAD_POOL_EXECUTOR来执行任务,当一个任务执行完成后,会继续执行其他任务,直到所有任务都被执行。
AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR),其中SerialExecutor用于任务的排队,THREAD_POOL_EXECUTOR用于任务的执行。
从上面的分析可以看出,在默认情况下,AsyncTask是串行执行的。
其实AsyncTask不同版本是有差异的,比如Android1.5是串行的,1.6之后是并行的,但是到3.0以后的版本又改为串行了。
当然,如果我们想在3.0以后也使用并行的方式执行任务,根据上面的分析,我们可以直接调用executeOnExecutor,直接传入一个THREAD_POOL_EXECUTOR(执行任务的线程池),这样就可以直接执行,不用将任务加到SerialExecutor这个排队的线程池了。
在使用AsyncTask过程中,我们通常会在onPostExecute做一些涉及到UI的操作
@WorkerThread
protected abstract Result doInBackground(Params... params);
@MainThread
protected void onPostExecute(Result result) {
}
从源码可以得知,任务是在doInBackground这个子线程中进行的,最终任务执行结果会通过onPostExecute返回,而onPostExecute是在主线程中,那么根据Android消息机制可以推测,这中间必然会用到Handler
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
finish方法里面将result传递给了onPostExecute
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
InternalHandler的handleMessage里面调用了finish方法
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
...
}
从源码可以看到,任务在doInBackground里面执行完成后,借助InternalHandler实现了从子线程到主线程的切换。
由于getHandler方法是静态方法,返回的sHandler是一个静态对象,为了能够从工作线程切换到主线程,getHandler这个方法必须在主线程中调用了,而静态方法是在加载类的时候初始化的,所以最终结果就是AsyncTask必须在主线程中加载。
四、HandlerThread
Android中Handler一般都在主线程中使用,使用Handler需要创建Looper消息循环和MessageQueue消息队列,由于主线程里面系统已经自动帮我们执行了创建上传创建,所以我们可以很方便的使用Handler。但是如果需要在子线程里面使用Handler,就需要严格按照流程,先创建Looper消息循环和MessageQueue消息队列了。
比如像下面这样:
Thread thread = new Thread(new Runnable()
{
@Override
public void run() {
Looper.prepare();
Looper.loop();
}
});
thread.start();
Handler handler = new Handler(newThread.getLooper());
这种使用方式是有问题的,thread的looper是在线程运行之后创建的,当执行到Handler handler = new Handler(thread.getLooper())的时候,thread的looper可能还没有创建好。
为此,Android专门提供了HandlerThread类
Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.
public class HandlerThread extends Thread {
...
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
...
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
...
}
从文档说明可以看到,HandlerThread帮我们创建好了Looper,进一步分析源码,HandlerThread继承了Thread,他的run方法里面通过Looper.prepare()创建了消息队列,并通过Looper.loop()开启了消息循环,getLooper()方法里面当Looper未创建时会调用wait方法,当run方法里面Looper创建完成后调用notifyAll返回创建的Looper对象。这样就解决了上面例子中存在的异步问题。不过从源码也可以看到,HandlerThread只是帮我们处理异步问题,由于run方法是一个无限循环,因此当结束时有HandlerThread时,我们需要手动调用quit或者quitSafely方法来终止线程的执行。
五、IntentService
IntentService是一种特殊的Service,可以用来执行后台耗时的任务,由于IntentSerice是服务,所以它的优先级会比单纯的线程高很多,不容易被系统杀死,因此比较适合执行一些高优先级的后台任务。
public abstract class IntentService extends Service {
...
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
...
}
从onCreate里面可以看到IntentService使用了HandlerThread和Handler,从上面关于HandlerThread的分析可以知道这里的Handler是子线程的,里面是可以执行耗时任务的,这也是IntentService可以执行后台耗时任务的原因。当IntentService启动后执行到onStartCommand时,会再执行onStart
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
可以看到,在onStart里面,通过mServiceHandler发送了一条消息,这个消息会在HandlerThread中处理。
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
在onHandleIntent就可以执行后台耗时任务了。