Android源码阅读系列(四)之Android中的线程

一、引用

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就可以执行后台耗时任务了。