Android 多线程运行机制

xiaoxiao2025-07-06  10

文章目录

进程与线程一、进程二、线程 多线程一、Handler + Thread1、MessageQueue2、Looper3、Handler 二、AsyncTask1、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机制:

转自:https://blog.csdn.net/iispring/article/details/47180325

二、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);
转载请注明原文地址: https://www.6miu.com/read-5032642.html

最新回复(0)