从源码角度理解Android线程
线程分为主线程和子线程,主线程主要是做与界面相关的事,而子线程往往用于做耗时操作。android中扮演线程的角色有很多,如:asynctask、intentservice以及handlerthread。asynctask的底层使用的是线程池,其他两种直接使用线程。
asynctask封装了线程池和handler,在它的内部使用handler去更新ui线程。handlerthread是一个具有消息循环的线程。intentservice是一种服务,内部采用handlerthread来执行任务,由于是一种服务,不是service,所以不会轻易被杀死。
由于频繁的创建和销毁线程会消耗大量的系统资源,所以一般是用线程池来进行控制,android的线程池源于java。
一、主线程和子线程
主线程是进程拥有的线程,java默认只有一个主线程,主线程主要处理界面交互的逻辑,必须有较高的响应度,不然会造成界面延缓卡顿,这就要求主线程不能执行耗时任务,耗时任务交由子线程,即工作线程。
android沿用了java的线程模式,其中主线程叫ui线程,作用是运行四大以及处理它们同用户交互,而子线程执行耗时的任务,否则会出现anr现象。
二、android的线程形态
除了传统的thread以外,还包含asynctask、handlerthread以及intentservice,这三者底层实现也是线程。
1.asynctask
asynctask是一个轻量级的异步任务类,他可以在线程池中执行后台任务,然后把任务的进度和最终的结果传递给主线程并在主线程中更新ui。asynctask封装了thread和handler,但是asynctask不适合执行特别耗时的任务,虽然说内部封装了线程池,但是该线程池只有一条线程。
asynctask是一个抽象的泛型类,它提供了params、progress、result这三个泛型参数,其中params表示参数的类型,progress表示后台的执行任务的类型,result表示后台任务的执行结果类型,如果不需要传入参数,可以使用void来代替。asynctask的声明如下:
params 启动任务执行的输入参数,比如http请求的url。 progress 后台任务执行的百分比。 result 后台执行任务最终返回的结果,比如string。public abstract class asynctask,progress,result>
asynctask提供了4个核心方法,含义如下:
onpreexecute(): 在主线程中执行,在异步任务加载执行之前,此方法会调用,用于做一些任务准备工作。 doinbackground(params… params):在线程池中执行,此方法用于执行异步任务,params表示异步任务的输入参数。在此方法中可以通过publishprogress来更新任务进度,publishprogress会调用omprogressupdate方法。另外此方法需要返回结果给onpostexecute方法。 onprogressupdate(progress…values):在主线程中执行,当后台任务执行发生改变会调用此方法。 onpostexecute(result result):在主线程中执行,在异步任务执行的后面,其中result参数是后台任务的返回值,即doinbackground的返回值。上面的几个方法,onpreexecute先执行,接着是doinbackground,最后是onpostexecute。此外,asynctask还提供了oncancelled方法,它在主线程中用,当异步任务被取消,就会调用该方法,这个时候onpostexecute就不会被调用。
private class downloadfilestask extends asynctask { protected long doinbackground(url... urls) { int count = urls.length; long totalsize = 0; for (int i = 0; i < count; i++) { // totalsize += downloader.downloadfile(urls[i]); publishprogress((int) ((i / (float) count) * 100)); // escape early if cancel() is called if (iscancelled()) break; } return totalsize; } protected void onprogressupdate(integer... progress) { // setprogresspercent(progress[0]); } protected void onpostexecute(long result) { // showdialog("downloaded " + result + " bytes"); } }
该demo主要模拟文件下载的过程,输入的参数类型是url,后台任务的进程参数为integer,而后台返回的结果是long
new downloadfilestask().execute(new url("https://www.baidu.com"),new url("https://www.renyugang.cn"));
在downloadfilestask中,doinbackground用来执行具体的下载任务并通过publishprogress来更新下载的进度,同时要判断是否被外界取消。当下载完成后会调用onprogressupdate来显示结果。doinbackground是在线程池中执行的,onprogressupdate是在主线程中,当publishprogress被调用时,会顺带调用onprogressupdate。当任务执行完毕后,也会调用onpostexecute,我们可以通过这个方法告知用户下载完毕。
asynctask在具体使用过程中也有一些限制,主要如下几点:
(1)asynctask必须在主线程中加载,android4.1以上的系统已经自动完成了,在activitythread的main方法中会调用asynctask的init方法。 (2)asynctask的实例必须在主线程中创建 (3)execute方法必须在ui线程中调用 (4)不要在程序中手动调用onpreexecute、onpostexecute。doinbackground、onprogressupdate方法。 (5)一个asynctask对象只能执行一次,只能调用一次execute方法。 (6)在android 1.6之前,asynctask是串行执行任务,1.6后可以并行执行,但是3.0以后,为了避免并行带来的bug,又采用串行,虽然这样,但也可以通过asynctask的executeonexecutor方法来并行执行任务。2.asynctask的工作原理
分析asynctask的工作原理,我们从它的execute方法爱是分析,execute又会调用executeonexecutor方法,实现如下:
@mainthread public final asynctaskexecute(params... params) { return executeonexecutor(sdefaultexecutor, params); } @mainthread public final asynctask ,>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方法中,最先执行的是onpreexecute,然后线程池开始执行,下面试线程池的分析,如下所示:
private static class serialexecutor implements executor { final arraydeque mtasks = new arraydeque(); 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); } } }
从serialexecutor的实现可以看出asynctask是排队执行的过程。首先会把asynctask的params参数封转成futuretask对象,futuretask是并发类,在这里充当runnable的作用,接着futuretask交给serialexecutor的execute方法执行,serialexecutor的execute会通过mtask的offer将futuretask对象插入到mtask中,如果这个时候没有正在活动的asynctask,或者当一个asynctask任务执行完,就会调用serialexecutor的schedulenext方法来执行下一个任务,直至任务被执行完毕。
asynctask中有两个线程池(serialexecutor和thread_pool_executor)和一个handler(internalhandler),其中serialexecutor用于任务的排队,thread_pool_executor用于真正的执行任务,internalhandler用于将执行环境切换到主线程。asynctask的构造方法中有这么一段代码,由于futuretask的run方法会调用mworker的call方法,futuretask会被提交到asynctask包含的线程池中执行,因此mworker的call方法会在线程池中执行。
mworker = new workerrunnable() { 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; } }; ,>
mworker的call方法中,首先将mtaskinvoked标记位true,表示当前任务被执行过,然后执行asynctask的doinbackground方法,接着将返回值传给postresult方法,它的实现如下:
private result postresult(result result) { @suppresswarnings("unchecked") message message = gethandler().obtainmessage(message_post_result, new asynctaskresult(this, result)); message.sendtotarget(); return result; } private static handler gethandler() { synchronized (asynctask.class) { if (shandler == null) { shandler = new internalhandler(); } return shandler; } }
在上面代码中,会通过gethandler返回一个shandler,通过shandler返回一个message_post_result的消息,这个shandler的定义如下:
private static internalhandler shandler; 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; } } }
shandler是一个静态变量,为了能够将环境切换到主线程,必须要在主线程中创建shandler,这就变相要求asynctask也需要在主线程中加载。shandler收到message_post_result这个消息后,会调用asynctask的finish的方法:
private void finish(result result) { if (iscancelled()) { oncancelled(result); } else { onpostexecute(result); } mstatus = status.finished; }
adynctask的finish方法逻辑比较简单,如果asynctask被执行了,那么oncancelled就被调用,否则就执行onpostexecute方法。
3.handlerthread
handlerthread继承了thread,本质上是一个thread,只不过内部建立了looper,可以使用handler,主要是在run方法中通过looper.prepare()来创建消息队列,然后通过lopper.loop()来开启消息循环,在实际应用中就允许在handlerthread中创建handler了。handlerthread的run方法如下:
@override public void run() { mtid = process.mytid(); looper.prepare(); synchronized (this) { mlooper = looper.mylooper(); notifyall(); } process.setthreadpriority(mpriority); onlooperprepared(); looper.loop(); mtid = -1; }
从handlerthread的实现来看,handlerthread内不能创建了消息队列,这和普通的thread不一样,要通过handler的消息方式通知handlerthread去执行任务。在使用完handlerthread后,可以通过quit或者quitsafety来退出。
4.intentservice
intentservice是一种特殊的service,它继承service并且是个抽象类,所以要用它的子类。intentservice是一种服务,所以优先级比单纯的线程高,里面封装了handlerthread和handler,这一点从他的oncreate方法可以看出。
@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会被调用,然后创建一个handlerthread对象,用它的looper来构建一个handler对象,这样每次发送handler发送消息的时候都会在handlerthread线程中执行。
每次启动intenthandler,都会调用onstartcommand方法,intentservice在onstartcommand中处理每个后台任务的intent,同时调用onstart方法:
@override public int onstartcommand(@nullable intent intent, int flags, int startid) { onstart(intent, startid); return mredelivery ? start_redeliver_intent : start_not_sticky; } @override public void onstart(@nullable intent intent, int startid) { message msg = mservicehandler.obtainmessage(); msg.arg1 = startid; msg.obj = intent; mservicehandler.sendmessage(msg); }
可以看出,intentservice仅仅通过mservicehandler发送一个消息,这个消息会在handlerthread中处理,mservicehandler收到消息后,会将intent对象传递到onhandlerintent中去处理,这个intent和一开始传进来的intent是一样的。当onhandlerintent方法执行结束后,intentservice会调用stopself(int startid)来停止服务,该方法会等消息队列没有消息后才停止,而stopself()会立即就停止。
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); } }
instentservice的onhandleintent方法是一个抽象方法,我们需要在子类中实现,它的作用是通过intent参数中区分具体的任务并执行任务,执行完后通过stopself(int startid)来停止服务。另外,每次执行一个后台任务必须启动一次intenthandler,而内部通过消息的方式向handlerthread请求执行任务,handlerthread的looper是顺序执行任务,这就意味着intentservice也是顺序执行任务。