文章目录
- 进程与线程
- 一、进程
- 二、线程
- 多线程
- 一、Handler + Thread
- 1、MessageQueue
- 2、Looper
- 3、Handler
- 二、AsyncTask
- 1、AsyncTask的泛型参数
- 2、AsyncTask的核心方法
- 3、AsyncTask的简单实用
- 4、使用AsyncTask的注意事项
- 5、AsyncTask 的源码分析
- 6、AsyncTask 使用不当的后果
- 三、ThreadPoolExecutor
- 四、IntentService
进程与线程
一、进程
在Android中,一个应用程序就是一个独立的线程(应用运行在一个独立的环境中,可以避免其他进程的干扰)。一般来说,当我们启动一个应用程序的时候,系统会创建一个进程(从Zygote中fork出来的,这个进程会有独立的ID),并为这个进程创建一个主线程(UI线程),应用程序的组件默认运行在它的进程中,但我们可以通过制定应用的组件(四大组件)的运行进程:android:process来让组件运行在不同的进程中。
让组件运行在不同的进程中,有好处也会有坏处:
- 好处是:因为每个应用程序(进程)都会有一个内存预算,所有运行在这个进程中的程序使用的总内存不能超过这个值,让组件运行在不同的进程中,可以让主进程可以拥有更多的空间资源
- 坏处是:每个进程都会有自己的虚拟机实例,因此让在进程间共享一些数据变得困难(当然,我们可以采用多线程间的通信来实现数据的共享)
二、线程
在java中,线程有几种状态:创建,就绪,运行,阻塞,死亡。当当应用程序有组件运行时,UI线程就处于运行状态。默认情况下,所有组件的操作都在UI线程完成的,包括相应用户的操作(触摸, 点击等),组件的生命周期方法调用,UI的更新等。因此如果UI线程阻塞(在线程里做一些耗时操作),就不能响应各种操作,如果阻塞时间达到5秒,就会出现ANR(Application not respoding)。
因为Android的UI线程是非线程安全的,应用更新UI,是调用invalidate()方法来实现界面的重绘,而invalidate()方法是非线程安全的,也就是说当我们在非UI线程更新UI时,可能会有其他线程或UI线程也在更新UI,这就会导致界面更新的不同步。因此我们不能在非UI线程做更新U的操作。需要保证:
- 不能阻塞UI线程,即不能再UI线程执行耗时操作,如网络连接,文件的IO等
- 只能在UI线程更新UI
多线程
当我们启动一个APP时,Android系统会启动一个Linux Process,该Process包含一个Thread,就是UIThread。因为UI Thread是非线程安全的,所以我们需要使用其他线程去完成一些耗时操作,因此就需要用到多线程。
Android 提供了四种常用的操作多线程的方式:
Handler+Thread,AsyncTask,ThreadhreadPoolExecutor,IntentService
一、Handler + Thread
Handler是Android引入的一种让开发者参与与处理线程中消息循环的机制。我们在使用Handler的时候与Message打交道最多,Message是Handle机制向开发人员暴露出来的相关类,可以通过Message相关类完成大部分操作Handle的功能。Handler的内部实现主要涉及如下几个类:Thread、MessageQueue和Looper:
Thread是最基础的,Looper和MessageQueue都是构建在Thread之上,Handler有构建在Looper和MessageQueue之上,我们通过Handler间接的与下面几个相对底层的类打交道。
1、MessageQueue
MessageQueue源码链接 最基础最底层的是Thread,每一个线程内部都维护了一个消息队列—MessageQueue。消息队列MessageQueue,就是存放消息的队列。
假设我们在UI界面点击了某个按钮,而此时程序收到了某个广播事件,那我们如何处理这两个事件呢?因为一个线程在某一个时刻只能处理一件事情,不能同时处理多件事情,所以我们只能挨个处理。为此Android把UI界面上单机按钮的事件封装成一个Message,将其放入了MessageQueue,即将点击按钮事件的Message入栈到消息队列中,然后再将广播事件封装成Message,也入栈到消息队列中。可以说Message对象表示线程需要出的一件事情,消息队列就是一堆需要处理的Message池。线程Thread会依次取出消息队列中的消息,依次对其进行处理。
MessageQueue中有两个重要的方法,一个是enqueueMessage方法,一个是next方法:
enqueueMessage() :将一个message放入到消息队列MessageQueue中
next():从消息队列MessageQueue中阻塞式地取出一个Message
在Android中,消息队列负责管理着顶级程序对象(Activity,BroatcastReceive等)以及由其创建的所有窗口。
2、Looper
Looper源码链接 消息队列只是存储Message的地方,真正让消息循环起来的是Looper。
Looper是用来让线程中的消息循环起来的。默认情况下当我们创建一个新的线程时候,这个线程里是没有消息队列MessageQueue的。为了能够让线程绑定一个消息队列,我们需要借助与Looper:
首先我们要调用Looper的prepare方法,然后调用Looper的Loop方法
class LooperThread extends Thread{
public Handler mHandler;
public void run(){
Looper.prepare();
mHandler = new Handler(){
public void handlerMessage(Message msg){
//
}
};
Looper.loop();
}
}
需要注意的是Looper.prepare() 和 Looper.loop() 都是在新线程的run方法内调用,这两个方法都是静态方法。我们通过查看Looper的源码可以发现,Looper的构造函数是private的,也就是在该类的外部不能用new Looper() 的形式得到一个Looper对象。根据我们上面的描述,我们知道线程Thread 和 Looper 是一对一绑定的,也就是一个线程最多只能有一个Looper对象,这就解释了Looper的构造函数为什么是private得了,我们只能通过工厂方法 Looper.myLooper() 这个静态方法获取当前线程所绑定的Looper。
Looper通过如下代码保存了对当前线程的引用:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
所以在Looper对象中通过 sThreadLocal 就可以 找到其绑定的线程。
1、Looper.prepare() :该方法是让Looper做好准备,只有Looper准备好了之后才能调用Looper.loop()方法,Looper.prepare()的代码如下:
private static void prepare(boolean quitAllowed){
if(sThreadLocal.get() != null){
throw new RuntimeExecuption("only one looper may be created per thread")
}
sThreadLocal.set(new Looper(quitAllowed);
}
上面的代码,sThreadLocal.get() 拿到线程sThreadLocal所绑定的Looper对象,由于初始化情况下sThreadLocal并没有绑定Looper,所以第一次调用prepare方法时,sThreadLocal.get() 返回null,不会抛出异常。然后执行sThreadLocal.set(new Looper(quitAllowed) ,先通过私有构造函数创建一个Looper对象的实例,然后通过set方法将该Looper绑定到sThreadLocal中。
这样就通过 prepare方法完成了 线程 sThreadLocal 与 Looper的双向绑定:
- 在Looper内通过sThreadLocal可以获取Looper所绑定的线程
- 线程sThreadLoca通过sThreadLocal.get() 方法可以获取该线程所绑定的Looper对象
Looper的私有构造方法,代码如下:
private Looper(boolean quitAllowed){
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
我们可以看到构造方法中实例化了一个消息队列MessageQueue,并将其赋值给其成员字段mQueue,这样Looper也就与MessageQueue通过成员字段进行了关联。
在执行完Looper.prepare(双向绑定,线程与Looper的绑定)之后,我们就可以在外部通过Looper.myLooper() 方法获取当前线程绑定的Looper对象。
myLooper方法如下:
public static Looper myLooper(){
return mThreadLocal.get();
}
需要注意的是,在一个线程中,只能调用一次Looper.prepare(),因为在第一次调用了Looper.prepare之后,当前线程就已经绑定了Looper,在该线程内第二次调用Looper.prepare方法的时候,sThreadLocal.get()会返回第一次调用prepare的时候被绑定的Looper,不是null,这样就会抛出异常 throw new RuntimeExceition (“Only one looper may be created per thread”) ,告诉开发者一个线程只能绑定一个Looper对象。
在调用Looper.prepare方法之后,就可以调用Looper.loop方法让消息队列循环起来了。
注意:Looper.loop()应该在该Looper所绑定的线程中执行
Looper.loop()的代码如下:
public static void loop(){
final Looper me = myLooper();
if( me == null){
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread");
}
//注意下面这行
final MessageQueue queue = me.mQueu();
//Make sure the identity of this thread is that of the local process,
//and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//注意下面这行
for(;;){
//注意下面这行
Message mas = queue.next(); //might block
if(msg = null){
//No message indicates that the message queue is quitting.
return;
}
//this must be in a loca variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if(logging != null){
logging.pringln(">>>> Dispatching to "+msg.target+" "+msg.callback + “: ”+mag.what);
}
//注意下面这行
msg.target.dispatchMessage(mag);
if(logging != null){
logging.pringln("<<<< Finished to " + msg.target +" " +mag.callback);
}
//Make sure that during the course of dispatching the
//identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if(ident != newIdent){
Log.wtf(TAG,"Thread identity changed from 0x"
+ Long.toHexString(ident)+"to 0x"
+ Long.toHexString(newIdent)+ "while dispatching to "
+ msg.target.getClass().getName()+" "
+ mag.callback +" what=" + mag.what);
}
msg.recycleUnchecked();
}
}`
上面有几行代码是关键代码:
1、final MessageQueue queue = me.mQuue;
变量me 是通过静态方法myLooper()获取的当前线程所绑定的Looper(), me.mQueue是当前线程所关联的消息队列。
2、for(;?;
我们发现for循环没有设置循环终止的条件,所以这个for循环是个死循环。
3、Message msg = queue.next()
我们通过消息队列MessageQueue.next 方法从消息队列中取出一条消息,如果此时消息队列中有Message,那么next会立即返回该Message,如果此时消息队列中没有Message,那么next方法就会阻塞式地等待获取Message。
4、msg.target.diapatchMessage(msg);
msg的rarget属性时Handler,该代码的意思是让Message所关联的Handler通过dispatchMessage方法让Handler处理该Message,关于Handler的dispatchMessage方法将会在下面介绍
3、Handler
Handler源码链接 Handler 是暴露给开发者最顶层的一个类,其构建在Thread、Looper与MessageQueue之上,Handler具有多个构造函数,签名分别如下:
- publicHandler()
- publicHandler(Callback callback)
- publicHandler(Looper looper)
- publicHandler(Looper looper, Callback callback)
第一个和第二个构造函数都没有传递Looper,这两个构造函数都将通过调用Looper.myLooper()获取当前线程绑定的Looper对象,然后将该Looper对象保存到名为mLooper的成员字段中。
第二个和第四个构造函数还传递了Callback对象,Callback是Handler中的内部接口,需要实现其内部的handleMessage方法,Callback代码如下:
public interface Callback(){
public boolean handleMessage(Message msg)
}
Handler.Callback 是用来处理Message的一种手段,如果没有传递此参数,那么久应该重写Handler的handleMessage方法,也就是说为了使得Handler能够处理Message,我们有两种方法:
- 向handler构造函数传入一个Handler.Callback对象,并实现Handler.Callback的handleMessage方法
- 无需向Handler的构造函数传入Handler.Callback对象,但是需要重写Handler本身的handleMessage方法
这一点与java中使用多线程有异曲同工之妙,java中,如果要使用多线程,有两种方法:
- 向Thread的构造函数传入一个Runnable对象,并实现Runnable的run方法
- 无需向Thread的构造函数传入Runnable对象,但是需要重写Thread本身的run方法
sendMessage系列方法
我们通过sendMessageXXX系列可以向消息队列中添加消息,我们通过源码可以看到这些方法的调用顺序:
sendMessage 调用了sendMessageDelayed, sendMessageDelayed 又调用了 sendMessageAtTime。
Handler中还有一系列的sendEmptyMessageXXX方法,而这些方法在其内部有分别调用了其对应的sendMessageXXX方法。如图:
由此可见,所有的sendMessageXXX方法和sendEmptyMessageXXX方法最终都调用了sendMessageAtTime方法。
post系列方法
我们来看看postXXX方法,会发现postXXX方法在其内部又调用了对应的sendMessgeXXX方法,我们可以查看下sendMessage的源码:
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
可以看到内部调用了getPostMessage 方法,该方法传入了Runnable对象,得到了一个Message对象,getPostMessage的源码如下:
private static Message getPostMessage(Runnable r){
Message m = Message.obtain();
m.callback = r;
return m;
}
上述代码我们可以看到,在getPostMessage中,我们创建了一个Message对象,并将传入的Runnable对象赋值给Message的callback成员字段,然后返回该Message,然后在post方法中将携带有Runnable信息的Message传入到sendMessageDelayed方法中。由此我们可以看到所有的postXXX方法内部都需要借助sendMessageXXX方法来实现,所以postXXX与sendMessageXXX并不是对立关系,而是postXXX依赖sendMessageXXX,所以postXXX方法可以通过sendMessageXXX方法向消息队列中传入消息,只不过通过postXXX方法想消息队列中传入的消息都携带有Runnable对象(Message.callback)
我们可以通过关系图看清楚postXXX与sendMessageXXX方法之间的调用关系:
通过分析sendEmptyMessageXXX,postXXX方法与sendMessageXXX方法之间的关系,我们可以看到Handler中所有可以直接或间接向消息队列发送Message的方法最终都调用了sendMessageAtTime方法,该方法的源码如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis){
MessageQueue queue = mQueue;
if(queue == null){
RuntimeException e = new RuntimeException(
this +" sendMessageAtTime() called with no mQueue");
Log.w("Looper",e.getMessage(),e);
return false;
}
//注意下面这行代码
return enqueueMessage(queue, msg, uptimeMillis);
}
该方法内部调用了enqueueMessage方法,该方法的源码如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis){
//注意下面这行
msg.target = this;
if(mAsynchronous){
msg.setAsynchronous(true);
}
//注意下面这行代码
return queue.enqueueMessage(msg, uptimeMillis);
}
该方法中有两件事需要注意:
1,msg.target = this
该代码将Message的target绑定为当前的Handler
2.queue.enqueueMessage
变量queue表示的是Handler所绑定的消息队列MessageQueue,通过queue.enqueueMessage(msg, uptimeMills),我们将Message放入到消息队列中
所以我们通过下图看一下完整的方法调用顺序:
我们在分析Looper.loop() 源码时知道,Looper一直不断从消息队列中通过MessageQueue的next方法获取Message,然后通过代码msg.target.diapathchMessage(msg) ,让该msg所绑定的Handler(Message.target)执行diapatchMessage方法以实现对Message的处理。
Handler的dispatchMessage的源码如下:
public void dispatchMessage(Message msg){
//注意下面这行
if(msg.callback != null){
handleCallback(msg);
}else {
//注意下面这行
if(mCallback != null){
if(mCallback.handleMessage(msg)){
return;
}
}
//注意下面这行
handleMessage(msg);
}
}
我们来分析这段代码:
- 首先判断msg.callback是否存在,msg.callback是Runnable类型,如果msg.callback存在,那么说明该Message是通过执行Handler的postXXX系列方法将Message放入消息队列中的,这种情况下会执行handleCallback(msg),
handleCallback源码如下:
private static void handleCallback(Message message){
message.callback.run();
}
这里我们可以清楚的看到,我们执行了message.callback.run 方法,也就是执行了Runnable对象的run方法。
- 如果我们不是通过postXXX系列方法让Message的话,那么msg.callback就是null,代码往下执行,接着判断Handler的成员字段mCallback是否存在。mCallback是Handler.Callback类型,在构造函数中,我们可以传递Handler.Callback类型的对象,该对象需要实现handlerMessage方法,如果我们在构造函数中传递了该Callback对象,那么我们会让Callback的handleMessage方法来处理Message。
- 如果我们在构造函数中没有传入Callback类型对象,那么mCallback为null,那么我们会调用Handler自身的handleMessage方法,该方法默认是个空方法,我们需要自己重写实现该方法
综上,我们可以看到Handler提供了三个途径来处理Message,而且处理有前后优先之分:首先尝试让postXXX中传递的Runnable执行,其次尝试让Handler构造函数中传入的Callback的handleMessage方法,最后才是让Handler自身的handleMessage方法处理Message
下面用一个大神的图 @孙群 来更形象的理解Handler机制:
转自:javascript:void(0)
二、AsyncTask
AsyncTask 是一个抽象类,它是由Android封装的一个轻量级异步类(轻量级体现在使用方法,代码简洁),它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。
AsyncTask的内部封装了两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和 Handler(InternalHandler)。
其中SerialExecutor 线程池用于任务的排队,让需要执行的多个耗时任务,按顺序排列,THREAD_POOL_EXECUTOR 线程池才真正地执行任务,InternalHandler用于从工作线程切换到主线程。
1、AsyncTask的泛型参数
AsyncTask的类声明如下:
public abstract class AsyncTask<Params, Progress, Result>
AsyncTask 是一个抽象泛型类。
其中,三个泛型类型参数的含义如下:
Params :开始异步任务执行时传入的参数类型;
Progress:异步任务执行过程中,返回下载进度值的类型;
Result:异步任务执行完成后,返回的结果类型;
如果AsyncTask确定不需要传递具体的参数,那么这三个泛型参数可以用Void代替。
2、AsyncTask的核心方法
- onPreExecute() :这个方法会在后台开始执行之前调用,在主线程执行。用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
- *doInBackground(Params…):这个方法中的所有代码都会在子线程中运行,我们应该在这里处理所有的耗时操作。 任务一旦完成,就可以return执行结果,如果AsyncTask的第三个参数是Void,就可以不返回执行结果。注意:在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress…)方法来完成
- onProgressUpdate(Progress…):当在后台任务中调用了publishProgress方法后,这个方法就很快被调用,方法中方法中携带的参数就是后台任务中传递过来的。在这个方法中可以对UI进行操作,在主线程中运行,利用参数中的数值可以对界面元素进行相应的更新
- onPostExecute(Result):当doInBackground(Params…)执行完毕并通过return语句返回时,这个方法就很快会被调用,返回的数据会作为参数传递到此方法中,可以利用返回的书生进行一些UI操作,在主线程中运行,比如说提醒任务执行的结果,以及关闭进度条对话框等
上面几个方法的调用顺序:
onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute()
如果不更新执行顺序为:
onPreExecute() -->doInBackground() --> onPostExecute()
除了上面几个方法,AsyncTask还提供了onCancelled()方法,它同样在主线程中运行,当异步任务取消时,onCancelled()会被调用,这个时候onPostExecute()则不会被调用,但是要注意的是:AsyncTask中的cancell()方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务,就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。
3、AsyncTask的简单实用
/**
* 三个泛型参数
* 第一个 Params:始异步任务执行时传入的参数类型
* 第二个 Progress:异步任务执行过程中,返回下载进度值的类型
* 第三个 Result:异步任务执行完成后,返回的结果类型
*/
public DownloadTask entends AsyncTask<Void, Integer, Boolean>{
@Override
protected void onPreExecute(){
progressDialog.show();
}
@Override
protected Boolean doInBackground(Void... params){
try{
while(true){
int downloadPercent = doDownload();
publishProgress(dowloadPercent);
if(downloadPercent >= 100){
break;
}
}
}catch(Exception e){
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values){
progressDialog.setMessage("当前下载进度:"+values[0]+"%");
}
@Override
protected void onPostExecute(Boolean result){
progressDialog.dismiss();
if(result){
Toast.makeText(context, "下载成功", Toast.LENGT_SHORT).show();
}else {
Toast..................."下载失败".................
}
}
}
这里我们模拟了一个下载任务,在doInBackground()方法中执行具体的下载逻辑,在onProgressUpdate方法中显示当前的下载进度,在onPostExecute方法中来提示任务的执行结果。如果想要启动这个个任务,只需要简单的调用下列代码即可:
new DownloadTask().execute();
4、使用AsyncTask的注意事项
- 异步任务的实例必须在UI线程中创建,即AsyncTask对象必须在UI线程中创建。
- execute(Params… params) 方法必须在UI线程中调用。
- 不要手动调用onPreExecute(), doInBackground(Params… params),
onProgressUpdate(Progress… progress), onPostExecute(Result
result)这几个方法。 - 不能再doInBackground(Params… params)中更改UI组件的信息。
- 一个实例任务只能执行一次,如果执行第二次将会抛出异常。
5、AsyncTask 的源码分析
先从初始化一个AsyncTask时,调用的构造函数开始分析:
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;
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
这段代码比较长,但实际上没有任何具体的逻辑会得到执行,只是初始化了两个变量,mWorker 和 mFuture,并在初始化mFuture的时候将mWorker作为参数传入。mWorker是一个Cllable对象,mFuture是一个FutureTask对象,这两个变量会暂时保存在内存中,稍后才会用到它们。FutureTask实现了Runnable接口,关于这部分内容,可以看这篇文章
mWorker中的call()方法执行了耗时操作,即 result = doInBackground(mParams);然后把执行结果通过postResult(result),传递给内部的Handler跳转到主线程中。这里实例化了两个变量,并没有开启执行任务。
那么mFuture对象是怎么加载到线程池中,进行执行的呢?
接着如果想要启动某一个任务,就需要调用该任务的execute()的方法,因此现在我们来看一看 execute()方法的源码:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
调用了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;
}
可以看出,先执行了onPreExecute()方法,然后具体执行耗时操作任务的是exec.execte(mFuture) ,把构造函数中实例化的mFuture传递进去了。
exec具体是什么
从上面可以看出具体是sDefaultExecutor,再追溯看到是SerialExecutor类,源码如下 :
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);
}
}
}
我们可以看到execute方法。SerialExecutor是个静态内部类,是所有实例化的AsyncTask对象共有的,SerialExecutor内部维持了一个队列,通过锁使得该队列保证AsyncTask中的任务是串行执行的,即多个任务需要一个个加到该队列中,然后执行完队列头部的再执行下一个,以此类推。
在这个方法中,有两个主要步骤:
- 对队列中加入一个新的任务,即之前实例化后的mFuture对象
- 调用*scheduleNext()方法**,调用THREAD_POOL_EXECUTOR执行队列头部的任务
由此可见,SerialExecutor类仅仅为了保持任务执行是串行的,实际执行交给了THREAD_POOL_EXECUTOR。
THREAD_POOL_EXECUTOR 又是什么?
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
实际上是一个线程池,开启了一定数量的核心线程和工作线程。然后调用线程池的execute() 方法,执行具体的耗时任务,即开头构造函数中中mWorker中call()方法de的内容。先执行完 doInBackground() 方法,又执行postResult()方法,下面具体看这个方法:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
该方法向Handler对象发送了一个消息,下面来看AsyncTask中实例化的Handler对象的源码:
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中,如果收到的消息是MESSAGE_POST_RESULT, 即执行完了doInBackground() 方法并传递结果,那么就调用finish()方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
如果任务取消了,回调onCancelled() 方法,否则回调onPostExecute() 方法。
如果收到的消息是MESSAGE_POST_PROGRESS, 回调onProgressUpdate() 方法,更新进度。
InternalHandler是一个静态类,为了能够将执行环境切换到主线程,因此这个类必须在主线程中加载,所以变相要求AsyncTask的类必须在主线程中加载
6、AsyncTask 使用不当的后果
- 生命周期
AsyncTask不与任何组件绑定生命周期,所以在Activity或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment
的onDestory() 方法中调用 cancel(boolean); - 内存泄漏
如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Acitvity销毁了,AsyncTask所隐藏Task的后台线程还在执行,它将会继续在内存中保留这个引用,导致Activity无法被回收,引起内存泄漏 - 结果丢失
屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前的Activity的引用,这个引用已经无效,这时调用onPostExecute()
方法再去更新界面将不再生效
三、ThreadPoolExecutor
线程池实现;
四、IntentService
IntentService 具有Service 一样的生命周期,也提供了后台线程的异步处理机制。
public class MyIntentService extends IntentService {
public MyIntentService() {
super("");
}
@Override
protected void onHandleIntent(Intent intent) {
try {
Thread.sleep(3*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Intent intentresult = new Intent(IntentServiceActivity.RESULT);
sendBroadcast(intentresult);
}
public void onDestroy()
{
super.onDestroy();
}
}
这里我们通过广播,将结果返回到源Activity进行界面更新的,这样的处理方式感觉很重,如非设计流程需要使用,不建议经常使用。
在看一下如何启动:
Intent intent=new Intent(IntentServiceActivity.this,MyIntentService.class);
startService(intent);