Android 多线程运行机制

进程与线程

一、进程

在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+ThreadAsyncTaskThreadhreadPoolExecutorIntentService

一、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具有多个构造函数,签名分别如下:

  1. publicHandler()
  2. publicHandler(Callback callback)
  3. publicHandler(Looper looper)
  4. 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,我们有两种方法:

  1. 向handler构造函数传入一个Handler.Callback对象,并实现Handler.Callback的handleMessage方法
  2. 无需向Handler的构造函数传入Handler.Callback对象,但是需要重写Handler本身的handleMessage方法

这一点与java中使用多线程有异曲同工之妙,java中,如果要使用多线程,有两种方法:

  1. 向Thread的构造函数传入一个Runnable对象,并实现Runnable的run方法
  2. 无需向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);
	}
}

我们来分析这段代码:

  1. 首先判断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方法。

  1. 如果我们不是通过postXXX系列方法让Message的话,那么msg.callback就是null,代码往下执行,接着判断Handler的成员字段mCallback是否存在。mCallback是Handler.Callback类型,在构造函数中,我们可以传递Handler.Callback类型的对象,该对象需要实现handlerMessage方法,如果我们在构造函数中传递了该Callback对象,那么我们会让Callback的handleMessage方法来处理Message。
  2. 如果我们在构造函数中没有传入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的注意事项
  1. 异步任务的实例必须在UI线程中创建,即AsyncTask对象必须在UI线程中创建。
  2. execute(Params… params) 方法必须在UI线程中调用。
  3. 不要手动调用onPreExecute(), doInBackground(Params… params),
    onProgressUpdate(Progress… progress), onPostExecute(Result
    result)这几个方法。
  4. 不能再doInBackground(Params… params)中更改UI组件的信息。
  5. 一个实例任务只能执行一次,如果执行第二次将会抛出异常。
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中的任务是串行执行的,即多个任务需要一个个加到该队列中,然后执行完队列头部的再执行下一个,以此类推。

在这个方法中,有两个主要步骤:

  1. 对队列中加入一个新的任务,即之前实例化后的mFuture对象
  2. 调用*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);
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值