在Activity开启的子线程并不会自动随Activity的destroy而关闭,所以必须手动去关闭子线程或者通过boolean的方式让子线程结束运行。开启的子线程有for循环的要更加注意。
1 package com.lsw;
2
3 import android.app.Activity;
4 import android.os.Bundle;
5 import android.os.Handler;
6 import android.os.Message;
7 import android.util.Log;
8 public class ThreadDemoActivity extends Activity {
9 private static final String TAG = "ThreadDemo";
10 private int count = 0;
11 private Handler mHandler = new MyHandler();
12 boolean stopThread=false;
13
14 private Runnable mRunnable = new Runnable() {
15
16 public void run() {
17
18 while (!stopThread)
19 {
20 count++;
21 try
22 {
23 Thread.sleep(2000);
24 }
25 catch (InterruptedException e)
26 {
27 // TODO Auto-generated catch block
28 e.printStackTrace();
29 }
30
31 //虽然Message的构造函数是public的,但是最好是使用Message.obtain( )或Handler.obtainMessage( )函数来获取Message对象,因为Message的实现中包含了回收再利用的机制,可以提供效率。
32 Message message=mHandler.obtainMessage();
33 message.what=0;
34 message.obj=count;
35 mHandler.sendMessage(message);
36 }
37 }
38 };
39
40 @Override
41 public void onCreate(Bundle savedInstanceState) {
42 super.onCreate(savedInstanceState);
43 setContentView(R.layout.main);
44 //开启子线程
45 new Thread(mRunnable).start();
46 }
47
48 protected void onDestroy() {
49 System.out.println("-----------onDestroy------");
50 stopThread=true;
51 super.onDestroy();
52 };
53
54 class MyHandler extends Handler{
55
56 @Override
57 public void handleMessage(Message msg)
58 {
59 // TODO Auto-generated method stub
60 Log.e(TAG, Thread.currentThread().getName() + " " +msg.obj);
61 setTitle("" +msg.obj);
62 }
63 }
64
65 }
B. Android线程池ThreadPoolExecutor详解
传统的多线程是通过继承Thread类及实现Runnable接口来实现的,每次创建及销毁线程都会消耗资源、响应速度慢,且线程缺乏统一管理,容易出现阻塞的情况,针对以上缺点,线程池就出现了。
线程池是一个创建使用线程并能保存使用过的线程以达到复用的对象,简单的说就是一块缓存了一定数量线程的区域。
1.复用线程:线程执行完不会立刻退出,继续执行其他线程;
2.管理线程:统一分配、管理、控制最大并发数;
1.降低因频繁创建&销毁线程带来的性能开销,复用缓存在线程池中的线程;
2.提高线程执行效率&响应速度,复用线程:响应速度;管理线程:优化线程执行顺序,避免大量线程抢占资源导致阻塞现象;
3.提高对线程的管理度;
线程池的使用也比较简单,流程如下:
接下来通过源码来介绍一下ThreadPoolExecutor内部实现及工作原理。
线程池的最终实现类是ThreadPoolExecutor,通过实现可以一步一步的看到,父接口为Executor:
其他的继承及实现关系就不一一列举了,直接通过以下图来看一下:
从构造方法开始看:
通过以上可以看到,在创建ThreadPoolExecutor时,对传入的参数是有要求的:corePoolSize不能小于0;maximumPoolSize需要大于0,且需要大于等于corePoolSize;keepAliveTime大于0;workQueue、threadFactory都不能为null。
在创建完后就需要执行Runnable了,看以下execute()方法:
在execute()内部主要执行的逻辑如下:
分析点1:如果当前线程数未超过核心线程数,则将runnable作为参数执行addWorker(),true表示核心线程,false表示非核心线程;
分析点2:核心线程满了,如果线程池处于运行状态则往workQueue队列中添加任务,接下来判断是否需要拒绝或者执行addWorker();
分析点3:以上都不满足时 [corePoolSize=0且没有运行的线程,或workQueue已经满了] ,执行addWorker()添加runnable,失败则执行拒绝策略;
总结一下:线程池对线程创建的管理,流程图如下:
在执行addWorker时,主要做了以下两件事:
分析点1:将runnable作为参数创建Worker对象w,然后获取w内部的变量thread;
分析点2:调用start()来启动thread;
在addWorker()内部会将runnable作为参数传给Worker,然后从Worker内部读取变量thread,看一下Worker类的实现:
Worker实现了Runnable接口,在Worker内部,进行了赋值及创建操作,先将execute()时传入的runnable赋值给内部变量firstTask,然后通过ThreadFactory.newThread(this)创建Thread,上面讲到在addWorker内部执行t.start()后,会执行到Worker内部的run()方法,接着会执行runWorker(this),一起看一下:
前面可以看到,runWorker是执行在子线程内部,主要执行了三件事:
分析1:获取当前线程,当执行shutdown()时需要将线程interrupt(),接下来从Worker内部取到firstTask,即execute传入的runnable,接下来会执行;
分析2:while循环,task不空直接执行;否则执行getTask()去获取,不为空直接执行;
分析3:对有效的task执行run(),由于是在子线程中执行,因此直接run()即可,不需要start();
前面看到,在while内部有执行getTask(),一起看一下:
getTask()是从workQueue内部获取接下来需要执行的runnable,内部主要做了两件事:
分析1:先获取到当前正在执行工作的线程数量wc,通过判断allowCoreThreadTimeOut[在创建ThreadPoolExecutor时可以进行设置]及wc > corePoolSize来确定timed值;
分析2:通过timed值来决定执行poll()或者take(),如果WorkQueue中有未执行的线程时,两者作用是相同的,立刻返回线程;如果WorkQueue中没有线程时,poll()有超时返回,take()会一直阻塞;如果allowCoreThreadTimeOut为true,则核心线程在超时时间没有使用的话,是需要退出的;wc > corePoolSize时,非核心线程在超时时间没有使用的话,是需要退出的;
allowCoreThreadTimeOut是可以通过以下方式进行设置的:
如果没有进行设置,那么corePoolSize数量的核心线程会一直存在。
总结一下:ThreadPoolExecutor内部的核心线程如何确保一直存在,不退出?
上面分析已经回答了这个问题,每个线程在执行时会执行runWorker(),而在runWorker()内部有while()循环会判断getTask(),在getTask()内部会对当前执行的线程数量及allowCoreThreadTimeOut进行实时判断,如果工作数量大于corePoolSize且workQueue中没有未执行的线程时,会执行poll()超时退出;如果工作数量不大于corePoolSize且workQueue中没有未执行的线程时,会执行take()进行阻塞,确保有corePoolSize数量的线程阻塞在runWorker()内部的while()循环不退出。
如果需要关闭线程池,需要如何操作呢,看一下shutdown()方法:
以上可以看到,关闭线程池的原理:a. 遍历线程池中的所有工作线程;b. 逐个调用线程的interrupt()中断线程(注:无法响应中断的任务可能永远无法终止)
也可调用shutdownNow()来关闭线程池,二者区别:
shutdown():设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程;
shutdownNow():设置线程池的状态为STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表;
使用建议:一般调用shutdown()关闭线程池;若任务不一定要执行完,则调用shutdownNow();
总结一下:ThreadPoolExecutor在执行execute()及shutdown()时的调用关系,流程图如下:
线程池可以通过Executors来进行不同类型的创建,具体分为四种不同的类型,如下:
可缓存线程池:不固定线程数量,且支持最大为Integer.MAX_VALUE的线程数量:
1、线程数无限制
2、有空闲线程则复用空闲线程,若无空闲线程则新建线程
3、一定程度上减少频繁创建/销毁线程,减少系统开销
固定线程数量的线程池:定长线程池
1、可控制线程最大并发数(同时执行的线程数)
2、超出的线程会在队列中等待。
单线程化的线程池:可以理解为线程数量为1的FixedThreadPool
1、有且仅有一个工作线程执行任务
2、所有任务按照指定顺序执行,即遵循队列的入队出队规则
定时以指定周期循环执行任务
一般来说,等待队列 BlockingQueue 有: ArrayBlockingQueue 、 LinkedBlockingQueue 与 SynchronousQueue 。
假设向线程池提交任务时,核心线程都被占用的情况下:
ArrayBlockingQueue :基于数组的阻塞队列,初始化需要指定固定大小。
当使用此队列时,向线程池提交任务,会首先加入到等待队列中,当等待队列满了之后,再次提交任务,尝试加入队列就会失败,这时就会检查如果当前线程池中的线程数未达到最大线程,则会新建线程执行新提交的任务。所以最终可能出现后提交的任务先执行,而先提交的任务一直在等待。
LinkedBlockingQueue :基于链表实现的阻塞队列,初始化可以指定大小,也可以不指定。
当指定大小后,行为就和 ArrayBlockingQueue一致。而如果未指定大小,则会使用默认的 Integer.MAX_VALUE 作为队列大小。这时候就会出现线程池的最大线程数参数无用,因为无论如何,向线程池提交任务加入等待队列都会成功。最终意味着所有任务都是在核心线程执行。如果核心线程一直被占,那就一直等待。
SynchronousQueue :无容量的队列。
使用此队列意味着希望获得最大并发量。因为无论如何,向线程池提交任务,往队列提交任务都会失败。而失败后如果没有空闲的非核心线程,就会检查如果当前线程池中的线程数未达到最大线程,则会新建线程执行新提交的任务。完全没有任何等待,唯一制约它的就是最大线程数的个数。因此一般配合Integer.MAX_VALUE就实现了真正的无等待。
但是需要注意的是, 进程的内存是存在限制的,而每一个线程都需要分配一定的内存。所以线程并不能无限个。
C. Android 中如何停止一个线程
有三种方法可以结束线程:
1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
2. 使用interrupt()方法中断线程
3. 使用stop方法强行终止线程(不推荐使用,可能发生不可预料的结果)
前两种方法都可以实现线程的正常退出,也就是要谈的优雅结束线程;第3种方法相当于电脑断电关机一样,是不安全的方法。
D. 安卓关闭线程 ANDROID 这样的线程要怎么stop()关闭掉。跪求解决!
线程执行结束后自动关闭,可以在线程中设置条件变量来使得线程自动关闭。
E. android 启动一个线程后 怎么关闭 并释放内存
1、线程里面的代码运行完,就Game Over了。
2、如果线程中的代码是个死循环。可使用标识来确定是否然代码运行完
在子线程中运行代码如下
boolean flag = true;
while(flag){
//正常运行的代码
...
...
//更改flag标识的更改情况,false:跳出循环,线程运行完毕结束,true继续循环
....
}
F. Android 中的“子线程”解析
Android 中线程可分为 主线程 和 子线程 两类,其中主线程也就是 UI线程 ,它的主要这作用就是运行四大组件、处理界面交互。子线程则主要是处理耗时任务,也是我们要重点分析的。
首先 Java 中的各种线程在 Android 里是通用的,Android 特有的线程形态也是基于 Java 的实现的,所以有必要先简单的了解下 Java 中的线程,本文主要包括以下内容:
在 Java 中要创建子线程可以直接继承 Thread 类,重写 run() 方法:
或者实现 Runnable 接口,然后用Thread执行Runnable,这种方式比较常用:
简单的总结下:
Callable 和 Runnable 类似,都可以用来处理具体的耗时任务逻辑的,但是但具体的差别在哪里呢?看一个小例子:
定义 MyCallable 实现了 Callable 接口,和之前 Runnable 的 run() 方法对比下, call() 方法是有返回值的哦,泛型就是返回值的类型:
一般会通过线程池来执行 Callable (线程池相关内容后边会讲到),执行结果就是一个 Future 对象:
可以看到,通过线程池执行 MyCallable 对象返回了一个 Future 对象,取出执行结果。
Future 是一个接口,从其内部的方法可以看出它提供了取消任务(有坑!!!)、判断任务是否完成、获取任务结果的功能:
Future 接口有一个 FutureTask 实现类,同时 FutureTask 也实现了 Runnable 接口,并提供了两个构造函数:
用 FutureTask 一个参数的构造函数来改造下上边的例子:
FutureTask 内部有一个 done() 方法,代表 Callable 中的任务已经结束,可以用来获取执行结果:
所以 Future + Callable 的组合可以更方便的获取子线程任务的执行结果,更好的控制任务的执行,主要的用法先说这么多了,其实 AsyncTask 内部也是类似的实现!
注意, Future 并不能取消掉运行中的任务,这点在后边的 AsyncTask 解析中有提到。
Java 中线程池的具体的实现类是 ThreadPoolExecutor ,继承了 Executor 接口,这些线程池在 Android 中也是通用的。使用线程池的好处:
常用的构造函数如下:
一个常规线程池可以按照如下方式来实现:
执行任务:
基于 ThreadPoolExecutor ,系统扩展了几类具有新特性的线程池:
线程池可以通过 execute() 、 submit() 方法开始执行任务,主要差别从方法的声明就可以看出,由于 submit() 有返回值,可以方便得到任务的执行结果:
要关闭线程池可以使用如下方法:
IntentService 是 Android 中一种特殊的 Service,可用于执行后台耗时任务,任务结束时会自动停止,由于属于系统的四大组件之一,相比一般线程具有较高的优先级,不容易被杀死。用法和普通 Service 基本一致,只需要在 onHandleIntent() 中处理耗时任务即可:
至于 HandlerThread,它是 IntentService 内部实现的重要部分,细节内容会在 IntentService 源码中说到。
IntentService 首次创建被启动的时候其生命周期方法 onCreate() 会先被调用,所以我们从这个方法开始分析:
这里出现了 HandlerThread 和 ServiceHandler 两个类,先搞明白它们的作用,以便后续的分析。
首先看 HandlerThread 的核心实现:
首先它继承了 Thread 类,可以当做子线程来使用,并在 run() 方法中创建了一个消息循环系统、开启消息循环。
ServiceHandler 是 IntentService 的内部类,继承了 Handler,具体内容后续分析:
现在回过头来看 onCreate() 方法主要是一些初始化的操作, 首先创建了一个 thread 对象,并启动线程,然后用其内部的 Looper 对象 创建一个 mServiceHandler 对象,将子线程的 Looper 和 ServiceHandler 建立了绑定关系,这样就可以使用 mServiceHandler 将消息发送到子线程去处理了。
生命周期方法 onStartCommand() 方法会在 IntentService 每次被启动时调用,一般会这里处理启动 IntentService 传递 Intent 解析携带的数据:
又调用了 start() 方法:
就是用 mServiceHandler 发送了一条包含 startId 和 intent 的消息,消息的发送还是在主线程进行的,接下来消息的接收、处理就是在子线程进行的:
当接收到消息时,通过 onHandleIntent() 方法在子线程处理 intent 对象, onHandleIntent() 方法执行结束后,通过 stopSelf(msg.arg1) 等待所有消息处理完毕后终止服务。
为什么消息的处理是在子线程呢?这里涉及到 Handler 的内部消息机制,简单的说,因为 ServiceHandler 使用的 Looper 对象就是在 HandlerThread 这个子线程类里创建的,并通过 Looper.loop() 开启消息循环,不断从消息队列(单链表)中取出消息,并执行,截取 loop() 的部分源码:
dispatchMessage() 方法间接会调用 handleMessage() 方法,所以最终 onHandleIntent() 就在子线程中划线执行了,即 HandlerThread 的 run() 方法。
这就是 IntentService 实现的核心,通过 HandlerThread + Hanlder 把启动 IntentService 的 Intent 从主线程切换到子线程,实现让 Service 可以处理耗时任务的功能!
AsyncTask 是 Android 中轻量级的异步任务抽象类,它的内部主要由线程池以及 Handler 实现,在线程池中执行耗时任务并把结果通过 Handler 机制中转到主线程以实现UI操作。典型的用法如下:
从 Android3.0 开始,AsyncTask 默认是串行执行的:
如果需要并行执行可以这么做:
AsyncTask 的源码不多,还是比较容易理解的。根据上边的用法,可以从 execute() 方法开始我们的分析:
看到 @MainThread 注解了吗?所以 execute() 方法需要在主线程执行哦!
进而又调用了 executeOnExecutor() :
可以看到,当任务正在执行或者已经完成,如果又被执行会抛出异常!回调方法 onPreExecute() 最先被执行了。
传入的 sDefaultExecutor 参数,是一个自定义的串行线程池对象,所有任务在该线程池中排队执行:
可以看到 SerialExecutor 线程池仅用于任务的排队, THREAD_POOL_EXECUTOR 线程池才是用于执行真正的任务,就是我们线程池部分讲到的 ThreadPoolExecutor :
再回到 executeOnExecutor() 方法中,那么 exec.execute(mFuture) 就是触发线程池开始执行任务的操作了。
那 executeOnExecutor() 方法中的 mWorker 是什么? mFuture 是什么?答案在 AsyncTask 的构造函数中:
原来 mWorker 是一个 Callable 对象, mFuture 是一个 FutureTask 对象,继承了 Runnable 接口。所以 mWorker 的 call() 方法会在 mFuture 的 run() 方法中执行,所以 mWorker 的 call() 方法在线程池得到执行!
同时 doInBackground() 方法就在 call() 中方法,所以我们自定义的耗时任务逻辑得到执行,不就是我们第二部分讲的那一套吗!
doInBackground() 的返回值会传递给 postResult() 方法:
就是通过 Handler 将最终的耗时任务结果从子线程发送到主线程,具体的过程是这样的, getHandler() 得到的就是 AsyncTask 构造函数中初始化的 mHandler , mHander 又是通过 getMainHandler() 赋值的:
可以在看到 sHandler 是一个 InternalHandler 类对象:
所以 getHandler() 就是在得到在主线程创建的 InternalHandler 对象,所以
就可以完成耗时任务结果从子线程到主线程的切换,进而可以进行相关UI操作了。
当消息是 MESSAGE_POST_RESULT 时,代表任务执行完成, finish() 方法被调用:
如果任务没有被取消的话执行 onPostExecute() ,否则执行 onCancelled() 。
如果消息是 MESSAGE_POST_PROGRESS , onProgressUpdate() 方法被执行,根据之前的用法可以 onProgressUpdate() 的执行需要我们手动调用 publishProgress() 方法,就是通过 Handler 来发送进度数据:
进行中的任务如何取消呢?AsyncTask 提供了一个 cancel(boolean mayInterruptIfRunning) ,参数代表是否中断正在执行的线程任务,但是呢并不靠谱, cancel() 的方法注释中有这么一段:
大致意思就是调用 cancel() 方法后, onCancelled(Object) 回调方法会在 doInBackground() 之后被执行而 onPostExecute() 将不会被执行,同时你应该 doInBackground() 回调方法中通过 isCancelled() 来检查任务是否已取消,进而去终止任务的执行!
所以只能自己动手了:
AsyncTask 整体的实现流程就这些了,源码是最好的老师,自己跟着源码走一遍有些问题可能就豁然开朗了!
G. android 强行关闭线程
这个好办,这里有两个线程吧,线程A 线程mSender。
设:让线程A,监视线程mSender
第一步:
在A中定义一个变量:ExecutorService transThread = Executors.newSingleThreadExecutor();
解释:transThread 官方名称是“单一线程池变量”,他是做什么的呢,它就像我们给A的一块赛车场,用来玩遥控赛车。
第二步:
在A中再定义一个变量: Future transPending;
解释:transPending就像我给A一个用来控制遥控赛车的遥控器一样
第三步:
在A中定义个线程mSender对象:
private final Runnable mSender = new Runnable() {
public void run() {
}};
解释:这是你写的对象,他就相当于我给A的一个遥控赛车。
第四步:
在A中添加如下语句:transPending = transThread.submit(mSender);
解释:这一步就相当于把赛车(mSender)放入场地(第一步中我们定义的transThread),并且用遥控器“transPending”来控制他。
(注:这里你是否会有疑惑,我没有写"mSender.start()"之类的语句怎么让我的赛车跑起来啊(运行run()方法),放心,有了“transPending = transThread.submit(mSender);”这一步,赛车是自动跑起来的,也就是mSender会自动调用run的。)
第五步:
现在你可以用遥控器“transPending”干很多事情了
transPending.cancel(true);无论线程现在是否运行中,立刻终止。
transPending.cancel(false);当前线程若没有运行(比如挂起状态)就终止它。
transPending所做的不止这些。
H. Android中的线程状态 - AsyncTask详解
在操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源,即线程不可能无限制地产生,并且 线程的创建和销毁都会有相应的开销。 当系统中存在大量的线程时,系统会通过会时间片轮转的方式调度每个线程,因此线程不可能做到绝对的并行。
如果在一个进程中频繁地创建和销毁线程,显然不是高效的做法。正确的做法是采用线程池,一个线程池中会缓存一定数量的线程,通过线程池就可以避免因为频繁创建和销毁线程所带来的系统开销。
AsyncTask是一个抽象类,它是由Android封装的一个轻量级异步类(轻量体现在使用方便、代码简洁),它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。
AsyncTask的内部封装了 两个线程池 (SerialExecutor和THREAD_POOL_EXECUTOR)和 一个Handler (InternalHandler)。
其中 SerialExecutor线程池用于任务的排队,让需要执行的多个耗时任务,按顺序排列 , THREAD_POOL_EXECUTOR线程池才真正地执行任务 , InternalHandler用于从工作线程切换到主线程 。
1.AsyncTask的泛型参数
AsyncTask是一个抽象泛型类。
其中,三个泛型类型参数的含义如下:
Params: 开始异步任务执行时传入的参数类型;
Progress: 异步任务执行过程中,返回下载进度值的类型;
Result: 异步任务执行完成后,返回的结果类型;
如果AsyncTask确定不需要传递具体参数,那么这三个泛型参数可以用Void来代替。
有了这三个参数类型之后,也就控制了这个AsyncTask子类各个阶段的返回类型,如果有不同业务,我们就需要再另写一个AsyncTask的子类进行处理。
2.AsyncTask的核心方法
onPreExecute()
这个方法会在 后台任务开始执行之间调用,在主线程执行。 用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
doInBackground(Params...)
这个方法中的所有代码都会 在子线程中运行,我们应该在这里去处理所有的耗时任务。
任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。 注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。
onProgressUpdate(Progress...)
当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。 在这个方法中可以对UI进行操作,在主线程中进行,利用参数中的数值就可以对界面元素进行相应的更新。
onPostExecute(Result)
当doInBackground(Params...)执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中, 可以利用返回的数据来进行一些UI操作,在主线程中进行,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
上面几个方法的调用顺序:
onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute()
如果不需要执行更新进度则为onPreExecute() --> doInBackground() --> onPostExecute(),
除了上面四个方法,AsyncTask还提供了onCancelled()方法, 它同样在主线程中执行,当异步任务取消时,onCancelled()会被调用,这个时候onPostExecute()则不会被调用 ,但是要注意的是, AsyncTask中的cancel()方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务。就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。
3.AsyncTask的简单使用
这里我们模拟了一个下载任务,在doInBackground()方法中去执行具体的下载逻辑,在onProgressUpdate()方法中显示当前的下载进度,在onPostExecute()方法中来提示任务的执行结果。如果想要启动这个任务,只需要简单地调用以下代码即可:
4.使用AsyncTask的注意事项
①异步任务的实例必须在UI线程中创建,即AsyncTask对象必须在UI线程中创建。
②execute(Params... params)方法必须在UI线程中调用。
③不要手动调用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)这几个方法。
④不能在doInBackground(Params... params)中更改UI组件的信息。
⑤一个任务实例只能执行一次,如果执行第二次将会抛出异常。
先从初始化一个AsyncTask时,调用的构造函数开始分析。
这段代码虽然看起来有点长,但实际上并没有任何具体的逻辑会得到执行,只是初始化了两个变量,mWorker和mFuture,并在初始化mFuture的时候将mWorker作为参数传入。mWorker是一个Callable对象,mFuture是一个FutureTask对象,这两个变量会暂时保存在内存中,稍后才会用到它们。 FutureTask实现了Runnable接口,关于这部分内容可以看这篇文章。
mWorker中的call()方法执行了耗时操作,即result = doInBackground(mParams);,然后把执行得到的结果通过postResult(result);,传递给内部的Handler跳转到主线程中。在这里这是实例化了两个变量,并没有开启执行任务。
那么mFuture对象是怎么加载到线程池中,进行执行的呢?
接着如果想要启动某一个任务,就需要调用该任务的execute()方法,因此现在我们来看一看execute()方法的源码,如下所示:
调用了executeOnExecutor()方法,具体执行逻辑在这个方法里面:
可以 看出,先执行了onPreExecute()方法,然后具体执行耗时任务是在exec.execute(mFuture),把构造函数中实例化的mFuture传递进去了。
exec具体是什么?
从上面可以看出具体是sDefaultExecutor,再追溯看到是SerialExecutor类,具体源码如下:
终于追溯到了调用了SerialExecutor 类的execute方法。SerialExecutor 是个静态内部类,是所有实例化的AsyncTask对象公有的,SerialExecutor 内部维持了一个队列,通过锁使得该队列保证AsyncTask中的任务是串行执行的,即多个任务需要一个个加到该队列中,然后执行完队列头部的再执行下一个,以此类推。
在这个方法中,有两个主要步骤。
①向队列中加入一个新的任务,即之前实例化后的mFuture对象。
②调用 scheleNext()方法,调用THREAD_POOL_EXECUTOR执行队列头部的任务。
由此可见SerialExecutor 类仅仅为了保持任务执行是串行的,实际执行交给了THREAD_POOL_EXECUTOR。
THREAD_POOL_EXECUTOR又是什么?
实际是个线程池,开启了一定数量的核心线程和工作线程。然后调用线程池的execute()方法。执行具体的耗时任务,即开头构造函数中mWorker中call()方法的内容。先执行完doInBackground()方法,又执行postResult()方法,下面看该方法的具体内容:
该方法向Handler对象发送了一个消息,下面具体看AsyncTask中实例化的Hanlder对象的源码:
在InternalHandler 中,如果收到的消息是MESSAGE_POST_RESULT,即执行完了doInBackground()方法并传递结果,那么就调用finish()方法。
如果任务已经取消了,回调onCancelled()方法,否则回调 onPostExecute()方法。
如果收到的消息是MESSAGE_POST_PROGRESS,回调onProgressUpdate()方法,更新进度。
InternalHandler是一个静态类,为了能够将执行环境切换到主线程,因此这个类必须在主线程中进行加载。所以变相要求AsyncTask的类必须在主线程中进行加载。
到此为止,从任务执行的开始到结束都从源码分析完了。
AsyncTask的串行和并行
从上述源码分析中分析得到,默认情况下AsyncTask的执行效果是串行的,因为有了SerialExecutor类来维持保证队列的串行。如果想使用并行执行任务,那么可以直接跳过SerialExecutor类,使用executeOnExecutor()来执行任务。
四、AsyncTask使用不当的后果
1.)生命周期
AsyncTask不与任何组件绑定生命周期,所以在Activity/或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment的onDestory()调用 cancel(boolean);
2.)内存泄漏
3.) 结果丢失
屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。
自己是从事了七年开发的Android工程师,不少人私下问我,2019年Android进阶该怎么学,方法有没有?
没错,年初我花了一个多月的时间整理出来的学习资料,希望能帮助那些想进阶提升Android开发,却又不知道怎么进阶学习的朋友。【 包括高级UI、性能优化、架构师课程、NDK、Kotlin、混合式开发(ReactNative+Weex)、Flutter等架构技术资料 】,希望能帮助到您面试前的复习且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。