Android中的线程形态
本节将对Android中的线程形态做一个全面的介绍,除了传统的Thread以外,还包含了AsyncTask,HandlerThread以及IntentService,这三者的底层实现也是线程,但是他们具有特殊的表现形式,同时在使用上也各有优缺点,下面从源码的角度来分析AsyncTask的执行过程。
一、AsyncTask
AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。从实现上来说,AsyncTask封装了Thread和Handler,通过AsyncTask我们更加方便的执行后台任务以及在主线程中访问UI,但是AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。
AsyncTask是一个抽象的泛型类,他提供了Params,Progress和Result这三个泛型参数,其中Params表示参数的类型,Progress表示后台任务的执行进度,Result表示后台任务的返回结果的类型,如果AsyncTask确实不需要传递具体的参数,那么这三个泛型可以用Void来代替。AsyncTask这个类的声明如下所示。
public abstract class AsyncTask<Params, Progress, Result>
AsyncTask提供了4个核心方法,它们的含义如下所示。
- onPreExecute()
在主线程执行,在异步任务执行之前被调用,一般可以用于做一些准备工作。
- doInBackground(Params… params)
在线程池中执行,此方法用于执行异步任务,params参数表示异步任务的输入参数。在此方法中可以通过publishProgress方法来更新任务的进度,publishProgress会调用onProgressUpdate方法。此外此方法需要返回计算结果给onPostExecute方法。
- onProgressUpdate(Progress… values)
在主线程执行,当后台任务的执行进度发生改变时此方法会被调用。
- onPostExecute(Result result)
在主线程中执行,在异步任务结束之后,此方法被调用,其中result参数是后台任务的返回值,即DoInBackground的返回值。
上面几个方法,onPreExecute先执行,接着是doInBackground,最后才是onPostExecute。除了上述四个方法以外,AsyncTask还提供了一个onCancelled()方法,它同样在主线程中执行,当异步任务被取消时,onCancelled就会被调用,这个时候onPostExecute就不会被调用。下面提供一个典型的示例(这个示例就是源码中的)
class DownloadTask extends AsyncTask<URL,Integer,Long>{
@Override
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < totalSize; i++) {
totalSize += DownLoad.downloadFile(urls[i]);
publicProgress((int)((i / (float)count)*100));
if(isCancelled()){
break;
}
}
return totalSize;
}
@Override
protected void onProgressUpdate(Integer... values) {
setProgressPercent(values[0]);
}
@Override
protected void onPostExecute(Long aLong) {
showDialog(aLong);
}
}
AsyncTask在使用的过程中也有一些条件限制的,主要有如下几点:
- 1、AsyncTask的类必须在主线程加载,也就意味着第一次访问AsyncTask必须发生在主线程,当然这个过程在Android4.1之后已经被系统自动完成了,在Android5.0的源码中,可以看到ActivityThread的main方法,它会调用AsyncTask的init方法,这就满足了AsyncTask的类必须在主线程中加载这个条件了。
- 2、AsyncTask的对象必须在主线程创建。
- 3、execute方法必须在UI线程调用。
- 4、不要再程序中直接使用onPreExecute、onPostExecute、DoInBackground和onProgressUpdate方法。
- 5、一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会运行时异常。
- 6、在Android1.6之前,AsyncTask是串行执行任务的,Android1.6之后开始采用线程池并行处理,但是从Android3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程来串行任务。尽管如此,在Android3.0以及以后的版本中,我们也可以通过AsyncTask的executteOnExecutor方法来并行的执行任务。
二、AsyncTask的工作原理
为了分析AsyncTask的工作原理,我们从它的execute方法开始分析
android.os.AsyncTask#execute
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
//private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
return executeOnExecutor(sDefaultExecutor, params);
}
android.os.AsyncTask#executeOnExecutor
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;
}
在上面的代码中,sDefaultExecutor实际上是一个串行的线程池,一个进程中所有的AsyncTask全部在这个串行的的线程池中排队执行,这个排队过程后面再进行分析。在executeOnExecutor方法中,AsyncTask的onPreExecute方法最先执行,然后线程池开始执行。下面分析下线程池的执行过程,如下所示。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
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);
}
}
}
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
在mWorker的call方法里,首先将mTaskInvoked设置为true,表示当前任务已经被调用过了,然后执行AsyncTask的doInBackgroud方法,接着将其返回值传递给postResult方法,它的实现如下所示。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private Handler getHandler() {
return mHandler;
}
private final Handler mHandler;
在下面代码赋值
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());//创建实例
}
return sHandler;
}
}
postResult方法会通过sHandler发送给一个MESSAGE_POST_RESULT的消息,这个sHandler的定义如下所示。
private static class InternalHandler extends Handler {
@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;
}
}
}
可以发现,sHandler是一个静态的Handler对象,为了能够将执行环境切换到主线程,这就是要求sHandler对象必须在主线程中创建。由于静态成员会在加载类的时候进行初始化,因此就变相要求AsyncTask的类必须在主线程加载,否则同一个进程中的AsyncTask都将无法正常工作。sHandler收到MESSAGE_POST_RESULT这个消息后会调用AsyncTask的finish方法,如下所示。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
AsyncTask的finish方法的逻辑比较简单,如果AsyncTask被取消执行了,那么就会调用onCancelled方法,否则就会调用onPostExecute方法,可以看到doInBackground的返回结果会传递给onPostExecute方法,到这里AsyncTack的整个工作过程就完毕了。
三、HandlerThread
HandlerThread继承了Thread,它是一种可以使用Handler的Thread,它的实现也很简单,就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,这样在实际的使用中就允许在HandlerThread中创建Handler了,HandlerThread的run如下:
比如我们可以使用一个HandlerThread,将我们的业务切换到子线程执行(和主线程的逻辑刚好相反)。
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
四、IntentService
@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);
}
当IntentService被第一次启动时,它的onCreate会被调用,onCreate方法会创建一个HandlerThread,然后使用它的Looper来构造一个Handler对象mServiceHandler,这样通过mServiceHandler发送的消息最终都会在HandlerThread中执行,从这个角度来看,IntentService也可以用于执行后台任务。每次启动onstartCommon就会调用一次,IntentService在onStartCommon中处理每个后台任务的intent。下面看一下onStartCommon方法是如何处理外界的intent的,onStartCommon调用了onStart,onStart方法实现如下所示。
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
public class LocalIntentService extends IntentService {
private static final String TAG = "LocalIntentService";
public LocalIntentService() {
super(TAG);
}
@Override
protected void onHandleIntent( Intent intent) {
String action = intent.getStringExtra("task_action");
Log.i(TAG,"receiver action:" + action);
SystemClock.sleep(3000);
if("com.lgl.test.ACTION".equals(action)){
Log.i(TAG,"handler action:" + action);
}
}
@Override
public void onDestroy() {
Log.i(TAG,"onDestroy");
super.onDestroy();
}
}
五、Android中的线程池
线程池有如下优点。
- 1、重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
- 2、能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
- 3、能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。
Android中的线程池的概念来源于Java中的Executor,Executor是一个接口,真正的线程池实现为ThreadPoolExecutor。ThreadPoolExecutor提供了一系列参数来配置线程池,通过不同的参数可以创建不同的线程池,从线程池的功能特性上来说,Android的线程池主要分为4类,可以通过Executors所提供的工厂方法得到的。
1、ThreadPoolExecutor
ThreadPoolExecutor是线程池的真正实现,它的构造方法提供了一系列的参数来配置,构造如下所示。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue
ThreadFactory threadFactory) {
- corePoolSize
线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个间隔由keepAliveTime指定,当等待时间超过keepAliveTime所指的时长后,核心线程就会被终止。
- maximumPoolSize
线程池所容纳最大的线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞。
- keepAliveTime
非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。当ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样会作用于核心线程。
- unit
用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有TimeUnit,毫秒,秒以及分钟等。
- workQueue
线程池的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。
- threadFactory
线程工厂,为线程池提供创建新线程的功能,ThreadFactory是一个接口,它只有一个方法,Thread new
Thread(Runnable r)。
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
@Override
public Thread newThread(Runnable runnable) {
return new Thread(runnable, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingDeque<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR =
new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

2、线程池的分类
常见的4中不同功能特性的线程池
- FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
- CachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- ScheduleThreadPool
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
- SingleTheardExecutor
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1, threadFactory));
}
下面演示下系统预置的4中线程池的典型用法。
Runnable common = new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
}
};
ExecutorService fixThreadPool = Executors.newFixedThreadPool(4);
fixThreadPool.execute(common);
ExecutorService cacheThreadPool = Executors.newCachedThreadPool();
cacheThreadPool.execute(common);
ScheduledExecutorService scheduleThreadPool = Executors.newScheduledThreadPool(4);
scheduleThreadPool.schedule(common,2000,TimeUnit.MICROSECONDS);
scheduleThreadPool.scheduleAtFixedRate(common,10,1000,TimeUnit.MICROSECONDS);
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
singleThreadPool.execute(common);
总结:线程和线程池都介绍完成了。平时开发中可以封装一个线程池工具类。这样避免随意的new Thread导致一些性能开销或内存泄漏等问题。
本文深入探讨Android中的线程形态,包括AsyncTask的执行流程、HandlerThread的原理和IntentService的应用。重点分析AsyncTask的四个核心方法,以及其在不同Android版本中的变化。同时介绍了HandlerThread如何实现使用Handler的子线程,以及IntentService的后台任务处理方式。文章最后讨论了线程池的优势和Android线程池的四种类型。













&spm=1001.2101.3001.5002&articleId=105523672&d=1&t=3&u=b3803285353647a2bae21f931b0d5cc8)
2万+

被折叠的 条评论
为什么被折叠?



