0.引入
0.1.线程间通信的目的
首先,线程间通信要交流些什么呢?
解答这个问题要从为什么要有多线程开始,需要多线程的原因大概有这些
- 最早也最基本:有的任务需要大量的时间,但其实并不占用计算资源,比如等待外界输入,比如发起网络连接,于是当然就想到完全可以在等待的时候做另一件事,而因为程序都是单线顺序执行的,如何才能同时做两件事呢,那就是开启第二个线程,也就是多线程
- 对于多核CPU,你把一个任务给多个CPU干,那就只能开多线程
- 所以就是这两种情况要多线程:一个CPU同时做多件事;多个CPU同时做一件事
因为这样的目的,所以多线程通信的内容就清楚了,就是返回结果数据,交代任务的代码一般不传入,而是写在多线程里的,但好像也可以传入数据吧,开始传入数据的时候还不需要多线程通信机制
看到这想到启动线程和调用方法好像很像,于是对比了一下,发现基本完全一样:
- 但其实类似方法,启动方法的时候可以传入数据(参数)和任务代码(回调),方法中本身也有写死的任务代码,返回时只返回数据,数据直接返回
- 线程的话,启动线程的时候可以直接传入数据(参数)和任务代码(回调),线程中本身也有写死的任务代码,返回时只返回数据,数据通过多线程通信机制返回
0.2.安卓中为什么要有线程间通信以及实现方式(消息机制)的思路
除了需要线程返回结果,在安卓中还有新的需求:当线程有权限时,A线程能干某事而B线程不能干某事,那B线程要这件事(就是执行一段代码咯)就要委托给A线程干了,这样的目的要如何通过线程间传递数据的机制实现呢?
安卓官方设计了一种机制:
- 把A线程进行改造,把这个顺序执行的线程改造成一个【先顺序执行一些代码,然后进入一个主动等待状态】的线程,在这个线程处于主动等待状态时,一收到B线程通过多线程机制发来的数据,就做一些事情
- 另外,为了防止阻塞,还构建了一个排队队列,能把发来的数据缓存起来,然后再进一步把A线程改造成【循环从排队队列中取数据】的线程,这样取到数据,再一个个处理
- 有了以上这个半成品机制之后,你要怎么实现【B线程把代码段委托给A线程执行】呢
- 一个很直接的想法是,B线程把要执行的代码段写在一个对象的方法中,然后把这个对象发送给A线程,A线程取到对象之后进行处理时就执行这个对象的这个方法
- 这个直接的想法从思路上来说很简单,但是实际上,线程代码都很短,都只是为了做某件具体的麻烦的事情,而且都是写在run()方法中,这样到时候要发送对象时,就要new一个内部类了,然后写方法,这样的方式不能说不行吧,只是下面有一种更方便的做法
- 另外好像同样是这种做法,安卓官方想了个方式。。。传一个runable对象,也就是一个线程。。。这个暂时不管
- 对的!是真的!安卓真的这么做了,下面有写:每个msg中有一个Runnable类型的成员callback,当调用post方法发送一个Runnable对象时就进行这样的处理--把Runnable对象存入msg的这个callback成员中,之后收到msg后,第一件事就是判断msg的这个成员是不是null,如果是,才调用回调方法,如果不是就意味着传来一个Ruannable对象,就会直接执行它的run方法,因为此时不是用.start()方法,所以就是在本线程直接执行,这其实也是一种回调。而且所以是Runnable对象的话,也完全不会执行handMessage钩子方法
- 所以其实是两种实现都有,但是把这种实现包装成下一种实现,而且这种不用实现handMessage钩子方法。所以下文主要用下一种实现为中心来介绍没有问题
- 于是有了一种实现起来简洁很多,思路上看起来绕了但其实也同样很简单的做法:把代码段事先就在A线程的一个方法中,并且给个序号b-1,然后B线程只要发来一个简单的数据“b-1”,然后A线程取到这个数据之后,通过比对也可以非常方便地调用序号为b-1的方法
- 这个方法还会凑巧提供一种便利,那就是,因为run()的子线程代码是直接写在当前Activity类的onCreat()方法中,由于【启动一个线程其实就是启动一个方法,成员的权限管理应该是跟在单线程一样的,只是你得知道这个方法中的代码运行在另一个线程上而已】 于是run()方法不用自己new handler对象(而且他默认new出的对象是子线程的,不是目标线程的),它可以直接调用在当前Activity类中New的handler对象(在主线程中New的,绑定主线程),这样在run()方法中传递数据又方便了很多
- OK,就是如此,A线程就是Activity线程,B线程就是A线程开启的一个网络获取数据的子线程,对A线程进行改造的就是Looper,发送者是在B线程中new的Handler对象,发去的数据是Message对象,接收者还是Looper,缓存在其中的MessageQueue对象中,取出者分发者是Looper,它调用的具体处理消息者也是Handler对象(在A线程new的),被委托的代码段被预先写在Handler对象的方法中
0.3.消息机制的基本原理和构成
在讲安卓中的多线程通信(或者说消息处理)之前先介绍一下消息机制
一般的消息系统模型的建立大致构成以下几个部分:
- 消息原型
- 消息队列
- 发送消息
- 消息循环
- 消息获取
- 消息派发
- 消息处理
大致模型图如下:
消息系统模型一般会包括以上七个部分。实际上的核心是消息队列和消息循环,其余部分都是围绕这两部分进行的
首先来研究一下消息驱动的基本模型,我使用如下的图形来表示一个消息系统最基本构成:
上面的模型代表应用程序一直查询自己的消息队列,如果有有消息进来,应用消息处理函数中根据消息类型及其参数来作相应的处理。
消息系统要运作起来,必定有消息的产生和消费。我们可以从下图看到消息生产和消费的一个基本的链条,这是一个最基本的,最简单的消息系统。
生产线程将消息发送到消息队列,消息消费者线程从消息队列取出消息进行相应的处理。但是这样简单的模型对实际运行的系统来说是不够的,例如对系统资源的消耗等不能很好的处理,我们就需要一个有旗语的消息系统模型,在上面的消息系统模型中加入了一个旗语,让消息消费者线程在没有消息队列为空时,等待旗语,进入到挂起状态,而有消息到达时,才被唤醒继续运行。当然生产者同时也可以是消费者。
Android要建立一个消息系统使用了Looper,MessageQueue,Handler等概念,从上节的原理我们可以知道这些都是概念包装,本质的东西就是消息队列中消息的分发路径的和消息分发处理方式的设计。Android巧妙的利用了对象抽象技术抽象出了Looper和Handler的概念。在Looper和Handler两个概念的基础上,通过View的处理函数框架,Android十分完美的达到消息分发的目的。
从前面文档的分析中我们知道Handler就是用来建立消息处理的系统模型,那么和这里基本消息系统模型相比,那么Handler又是如何囊括这七个部分的呢?
安卓中的多线程通信(或者说消息处理)的一个方式就是消息机制(另外利用接口回调也能实现,你知道的这个东西适合多线程)
在Android中对这7个部分进行了抽象成四个独立的部分: Handler,Message,MessageQueue,Looper,俗称四大金刚。从上节的原理我们可以知道这些都是概念包装,本质的东西就是消息队列中消息的分发路径的和消息分发处理方式的设计。Android巧妙的利用了对象抽象技术抽象出了Looper和Handler的概念。在Looper和Handler两个概念的基础上,通过View的处理函数框架,Android十分完美的达到消息分发的目的。
- Message
- 就是消息原型
- 其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
- MessageQueue
- 就是消息队列,
- 消息队列由Looper所持有,但是消息的添加是通过Handler进行;
- 用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
- Looper
- 消息泵,实现Thread的消息循环和消息派发
- 不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
- 缺省情况下Thread是没有这个消息循环的既没有Looper;需要主动去创建,然后启动Looper的消息循环loop;与外部的交互通过Handler进行;
- Handler
- 处理者,驾驭整个消息系统模型,统领Message,MessgeQueue和Looper;
- 负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
参照基本消息系统描述模型,我给出了Android消息系统整体框架,表示如下:
Android消息系统消息分发框架
最后来看看类图:
不过其实四大金刚主要还是两个主力,Looper和Handler,因为你不直接跟MessageQueue打交道,而Message虽然要打交道,但是它非常简单
首先从主力之一Looper说起:
1.Looper
1.1.Looper类介绍
- Looper类是用来使一个普通线程变成Looper线程的类
- Looper线程是什么
- 一个普通线程是从头执行到尾,然后结束
- 而Looper线程却会从头执行到某个循环起点,然后进入循环,一直执行,直到接受到某些信号,然后结束
- 在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务
- Looper线程是什么
- Looper类是怎么做到的呢
- 原理
- 非常简单,就是在普通线程的最后加一个循环,这个循环用来接收并处理来自handle的msg
- 非常简单,就是在普通线程的最后加一个循环,这个循环用来接收并处理来自handle的msg
- 具体实现(在线程中(比如run()方法中)一前一后加入两句代码,如源码一)
- Looper.prepare();
- 在线程中(这个说法挺可疑)new了一个Looper对象(如源码二)
- 这个Looper对象与某个(一般就是这个)Thread绑定了,一个Thread只能绑定一个Looper对象,因为这个Looper对象是ThreadLocal类型的
- 具体怎么做到new出的Looper对象是ThreadLocal类型的呢
- 机制其实就是 Looper对象本身就是一个ThreadLocal对象管理器
- 调用Looper对象提供的方法Looper.prepare(),它就能在线程中(这个说法挺可疑)new了一个ThreadLocal类型的 Looper对象
- 具体怎么做到new出的Looper对象是ThreadLocal类型的呢
- 这个Looper对象内有成员MessageQueue,他就是系统提供的消息队列类,loop()方法调用后线程开始不断从队列中取出消息执行
- Looper的构造函数Looper()被声明为private,也就是说,在外部,我们不能直接的使用Looper的构造函数,只能用这个prepare方法来
- 这个Looper对象与某个(一般就是这个)Thread绑定了,一个Thread只能绑定一个Looper对象,因为这个Looper对象是ThreadLocal类型的
- 在线程中(这个说法挺可疑)new了一个Looper对象(如源码二)
- Looper.loop();(如源码二)
- 当执行到这一步之后,调用loop()方法,这是个循环方法,于是线程就停在这进行循环
- 循环着做什么呢,它不断从自己的MQ中取出队头的消息(也叫任务)执行
- 写在Looper.loop()之后的代码不会被立即执行,当调用后 mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
- Looper.prepare();
- 原理
- 安卓默认把所有的activity都默认设置成了Looper(大概就是在基类中)
- 所以活动线程是这么工作的:当Android应用启动后,系统会默认创建一个主线程(Main thread)。这个主线程启动后,首先完成UI的绘制,然后会进入一个消息循环(Loop),等待和执行各种来自系统的消息和事件、各种用户点击/触摸事件、其他线程发送的消息事件等等。
- 这是线程工作的一种常见的模式,即进入一种“等待命令/消息 ”->“执行命令/消息”->“等待命令/消息”的循环
- 那么,其他非UI线程如何与进入了消息循环的主线程交互呢?这就得靠Handler了。
- Activity是一个UI线程,运行于主线程中,Android系统在启动的时候会为Activity创建一个消息队列和消息循环(Looper)。详细实现请参考ActivityThread.java文件。
- Android应用程序进程在启动的时候,会在进程中加载ActivityThread类,并且执行这个类的main函数,应用程序的消息循环过程就是在这个main函数里面实现的
1.2.Looper类用法和作用:
- Looper和Handler都在本线程中(Looper 是静态方法,Handler 是直接实例化),可以当做是在run()方法中
- Looper的prepare()方法负责改造线程
- Looper的成员(关联)中有MessageQueue ,它能做默默两件大事:1)有一个接收机制,默默接受各个Handle对象(只要地址对,从哪儿发的都行;从任意线程发到任意目标线程)发送来的msg;2)有一个队列专门把接收到的msg排列好等待循环来取
- Looper的loop()方法负责发动一个【不断取MessageQueue中的Message对象,并把它发给各个Handle对象(msg.target指定的)去处理】的循环
- Looper的方法中(依赖)实例化了Message对象,能承载信息,Message对象本身能被放在MessageQueue中(或者说它是MessageQueue类能处理的唯一指定类型)
我们如何往某个线程的Looper中的MQ上添加消息呢?那就是Handler!
2.Handler
2.1.总体介绍
- handler可以在任意线程发送消息,这些消息会被添加到关联的MQ上,handler是在它关联的looper线程中处理消息的
- 重温msg对象的发送和处理过程:【发送者】是B线程中的【绑定了A线程Looper的Handler对象】,【接收者和中介者】是A线程的Looper(被存在它里边的MQ中),然后被looper转发给【最终接收者也是处理者】--A线程中的【绑定了A线程Looper的Handler对象】(这一步的形式是looper拥有(不是直接持有,而是msg中传过来的)handler的引用,并直接调用它的处理方法)
- handler扮演了往MessageQueue上添加消息和处理消息(在被分配msg之后执行该任务)的角色(只处理由自己发出的消息),整个过程是异步的
- 为什么发送者和处理者都用Handler,其实也可以不用,只是这样looper调用对应handler的处理方法时更方便
- 两个线程中Handler使用情况对比:
- B线程中的【绑定了A线程Looper的Handler对象】new出来之后,只调用它的发送方法把数据发送出去
- A线程中的【绑定了A线程Looper的Handler对象】new出来之后,只需要写一下它的处理代码,然后等待被looper调用这个方法
- 注意
- 这个Handler对象要么是1)直接实例化Handler抽象类然后传入一个实现Handler.Callback接口的对象,要么是2)继承了Handler类并实现了其中的回调方法的子类的对象
- 其实这里的两个对象可以是同一个对象(作为参数传递来传递去的),原理上来说也可以是各自new类的对象 ,但是一般来说这个你实现了的接口或者是继承了的子类会被设置成private内部类,在外不能被调用,这样你就可能需要把同一个对象传来传去
- 直接在本线程中实例化Handler(如源码四),Handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以set的,如源码三
- 一个线程可以有多个Handler(类),也可以有多个同一个类的handler对象(N*N),但只能有1个looper对象
- PS:其实handler能做的远远不仅如此,由于它能post Runnable对象,它还能与Looper配合实现经典的Pipeline Thread(流水线线程)模式
2.2.使用handler对象发送消息
- 在本线程new出了绑定目标线程的Looper的handler对象之后,我们就可以使用handler对象的这些方法向MQ上发送消息
- 发送Runnable对象消息方法
- post(Runnable)
- postAtTime(Runnable, long)
- postDelayed(Runnable, long)
- 发送message对象消息方法
- sendEmptyMessage(int)
- sendMessage(Message)
- sendMessageAtTime(Message, long)
- sendMessageDelayed(Message, long)
- 发送Runnable对象消息方法
- 看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了,如源码五
- 后文在android举例中,有展示很多种使用Handler发送消息的方式,但是主要是三种,而这三种其实都是基于message的,所以本文讲原理时候只以message为中心的(但是所有方法都会介绍到)。一共有这三种
- 使用message
- 使用runable
- 实现Handle.Callback
- 用handle对象从从任意线程往任意目标线程任意目标handle对象发送message的过程(两个地址)
- 发到目标线程确实是通过填写目标线程地址来实现的
- 但是这个目标线程地址不是写在msg中
- 也不是发送时作为参数和msg一块传给Handle对象
- 他是在new Handle对象的时候指定的(指定的方式是绑定目标线程的Looper对象),也就是说,你要往哪个线程发送msg,你就造一个绑定了这个线程的Handle对象(默认情况下绑定当前线程),也就是说,一个Handle对象只能往某个固定的线程发送msg
- 但同时,可以有多个Handle对象绑定同一个线程,而前面说了:每个Handle对象只处理由自己发出的消息,那loop()方法分发msg的时候怎么知道是哪一个Handle对象呢(这些handle对象绑定的是同一个线程)
- 非常简单,就是靠发送msg时,系统默认会把当前的handle的引用也发过去(其实也就是靠这个引用调用对应Handle对象的处理方法,所以可以看出这是一个回调方法)
- 如源码五,msg.target为该handler对象,这确保了looper执行到该message时能找到处理它的handler
- loop中的相关代码是:msg.target.dispatchMessage(msg);
- 发到目标线程确实是通过填写目标线程地址来实现的
2.3.使用handler对象处理消息
- 在上面说的目标线程中,必须也new一个绑定本线程的handler对象,并写一个会被调用的的方法(其实不叫回调,因为这个还是你写的代码调用你写的代码)(这个有点像模板模式中的钩子方法,由某个指令决定是否执行?)
- 消息的处理是通过两个方法:dispatchMessage(Message msg);handleMessage(Message msg)完成的,见源码六
- 这里有个绕绕,就是其实是先回调dispatchMessage(Message msg) 方法,这个方法中会做一些判断(因为runable数据的问题),判断某一种情况下,再调用这个handleMessage(Message msg)
2.4.安卓中handler的使用(后边展开碉堡了)
2.4.1.发送messaage
这就解决了android最经典的不能在其他非主线程中更新UI的问题。android的主线程也是一个looper线程(looper在android中运用很广),我们在其中创建的handler默认将关联主线程MQ。因此,利用handler的一个solution就是在activity中创建handler并将其引用传递给worker thread,worker thread执行完任务后使用handler发送消息通知activity更新UI。(过程如图)如源码七
2.4.2.发送Runable
于是 onCreate 中的相关代码变成如下了:
final Handler mainThreadHandler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
}
}).start();
看起来很酷的样子嘛,一层套一层的 Runnable. mainThreadHandler 因为是在 主线程中创建的, 而 Handler创建时,绑定到当前线程。 所以 mainThreadHandler 绑定到主线程中了。
2.4.3.使用runOnUiThread方法
然后一个好消息是(也是坏消息,学了这么多没有用。但其实并不是,因为没有自己写的好用),Android 为了方便你在向主线程中安排进操作,在 Activity类(当你把run()方法另外写在其他类中就不行)中提供了 runOnUiThread 方法
于是上面的代码简化为:
new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
runOnUiThread(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
}
}).start();
你不用自己创建一个 Handler了。
而 runOnUiThread 的具体实现,也跟我们做得差不多。UI线程即主线程。
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
2.4.4.使用线程池(神展开)
现在先思考一下,我们上面的代码可能遇到的问题。 比如,我们现在要显示一个图片列表。 一百多张图片。 如果每下载一张就开一个线程的话,那一百多个线程,那系统资源估计支持不住。特别是低端的手机。
正确的做法是使用一个线程池。
final ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
runOnUiThread(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
executor.shutdown();
}
});
由于我们是在在一个局部方法中使用了一个线程池。所以处理完了之后应该将线程停止掉。 而我们上面只有一个线程,所以直接在下载完成之后,调用 executor停掉线程池。 那如果执行了多个图片的下载请求。需要怎么做呢? 那就要等他们都完成时,再停止掉线程池。 不过这样用一次就停一次还是挺浪费资源的。
不过我们可以自己保持一个应用级的线程池。 不过这就麻烦不少。然后 Android 早已经帮我们想法了这一点了。 我们直接使用 AsyncTask 类即可。
2.4.5.使用AsyncTask类(神展开)
Android 早期便有这个便利的类来让我们方便的处理 工作线程及主线程的交互及通信。 于是我们下载图片并显示图片的代码如下:
new AsyncTask<String,Void,Bitmap>(){
@Override
protected Bitmap doInBackground(String... params) {
return downloadImage(params[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
mBeautyImageView.setImageBitmap(bitmap);
}
}.execute(beautyUrl);
相比之前的代码简洁不少。
大部分的时候我会使用Thread或者Handler,因为他们比较容易写,Handler的postDelayed方法比较方便。在需要不断更新进度的时候可以考虑使用AsyncTask,例如下载更新包等等。
看一下 AsyncTask 的源代码,正是集合我们之前考虑的这些东西。
- 一个全局的线程池
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
- 一个绑定主线程的 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;
}
}
}
2.4.6.实现(也是厉害)Handle.Callback
Handler的使用方式其实有一个从原理上来说最少习惯最少约束的使用方法
- 使用message
- 准备:
- 在某处有一个类,是一个子类,【继承自Handler抽象类】,并把你要插入运行的代码写在其中的回调方法handleMessage( )中
- 发送端:
- 在【任意线程任意位置】获得(实例化/直接持有)1中这个类的一个对象,并为这个对象绑定目标线程的looper,然后使用这个对象的setmessage( )方法把它自己和要发送的message对象发送出去
- 处理端:
- 在【目标线程目标位置】获得(实例化/直接持有)1中这个类的一个对象,并为这个对象绑定目标线程的looper(当然在目标位置的话一般默认就行),然后等待被looper调用并进行处理
- 准备:
- 使用Handle.Callback
- 准备:
- 在某处有一个类,是一个实现类,【实现了Handler.Callback接口】,并且把你要插入运行的代码写在其中的handleMessage( )方法中
- 发送端:
- 在【任意线程任意位置】获得(实例化/直接持有)Handler抽象类的一个对象,并在构造方法中传入1中这个类的一个对象,并为这个对象绑定目标线程的looper,然后使用这个对象的setmessage( )方法把它自己和要发送的message对象发送出去
- 处理端:
- 在【目标线程目标位置】获得(实例化/直接持有)Handler抽象类的一个对象,并在构造方法中传入1中这个类的一个对象,并为这个对象绑定目标线程的looper,然后等待被looper调用并进行处理
- 准备:
- 使用runnable
- 准备:
- /
- 发送端:
- 在【任意线程任意位置】获得(实例化/直接持有)Handler抽象类的一个对象,并为这个对象绑定目标线程的looper,然后使用这个对象的post( )方法把它自己和要发送的runnable对象发送出去,要插入运行的代码写在runnable的run( )方法中
- 处理端:
- 在【目标线程目标位置】获得(实例化/直接持有)Handler抽象类的一个对象,并为这个对象绑定目标线程的looper,然后等待被looper调用并进行处理
- 准备:
而一般习惯会有这些小规则
- 使用默认方法绑定looper
- 在目标线程目标位置进行准备
- (处理端一定是在目标线程目标位置的)发送端也尽量在目标位置(肯定是不在目标线程的),以便直接调用handler对象
- 发送端不在目标位置,但是处理端把hander对象以参数的方式发送给发送端
对比message和handler.callback:
发送:
public
class
HandlerDemo {
private
Handler myHandler1 =
new
Handler(
new
MyHandlerCallback());
private
Handler myHandler2 =
new
MyHandler();
private
class
MyHandlerCallback
implements
Handler.Callback {
@Override
public
boolean
handleMessage(Message msg) {
// Handle messages.
return
false
;
}
}
private
class
MyHandler
extends
Handler {
@Override
public
void
handleMessage(Message msg) {
// Handle messages.
}
}
}
处理:
public
void
dispatchMessage(Message msg) {
if
(msg.callback !=
null
) {
handleCallback(msg);
}
else
{
if
(mCallback !=
null
) {
if
(mCallback.handleMessage(msg)) {
return
;
}
}
handleMessage(msg);
}
}
来分析一下处理过程:
也就是当这这个线程的Looper从MQ取出一对message-handler时,直接调用这个handler对象的dispatchMessage分流方法,传入message,进入分流方法之后,会有以下的分流:
- 首先看这个message是不是使用了runnable,也就是看这个message对象的callback成员是不是为空
- 如果不为空,而是里边有runnable,就直接调用其run()方法,然后结束
- 如果为空,就下一步判断,先不管message,而是判断本handler对象创建时是不是传入了实现了Handler.Callback接口的对象(就是本handler对象的对应属性中有没有这个接口实现对象)
- 如果不为空,而是传入了Handler.Callback接口的对象,那就调用这个接口对象的的处理方法【
mCallback
.handleMessage(msg)
】来处理msg,正常情况下这个处理方法处理完了会返还true,于是return,方法结束 - 如果为空,就直接调用本handler对象的处理方法来处理这个msg,结束
- 如果不为空,而是传入了Handler.Callback接口的对象,那就调用这个接口对象的的处理方法【
机制是这样,但是为什么要用使用Handler.Callback呢,这基本上跟使用message完全一样啊
找了很多都没找到比较靠谱的,只找到这么几种解释还不算太假
1
继承Handler还是实现Handler.Callback?
我觉得不应该用这个两个去比较
而是用以下这种情况(都使用匿名内部类的时候)去比较.其中前者在一些情况下会造成内存泄露
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
}
};
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
2
凡是发到通过这个handler发送的消息,都有callback处理,相当于一个总的集中处理
3
个人从机制上分析,觉得这样的机制可以实现这样的效果:
当你实例化了一个【继承自Handler抽象类,并把你要插入运行的《代码c-msg》写在其中的回调方法handleMessage( )中】的handler同时,又用构造方法传入了【实现了Handler.Callback接口,并且把你要插入运行的《代码c-Callback》写在其中的handleMessage( )方法中】的对象,然后在发送massage的时候又是发送了一个【要插入运行的《代码c-run》写在runnable的run( )方法中的runnable对象】
这种情况下,这个handle和它收到的msg同时承载了三段代码:《代码c-msg》、《代码c-Callback》、《代码c-run》
dispatchMessage的处理思路是:
- 只要它承载了《代码c-run》,那么就只执行它,其他的不管有没有都不执行
- 如果它没有承载《代码c-run》,如果同时承载了《代码c-Callback》和《代码c-msg》,那么《代码c-Callback》一定会被执行,《代码c-msg》会不会执行要看msg中的数据:
- 如果数据让《代码c-Callback》的执行结果为true,那么《代码c-msg》不执行
- 如果数据让《代码c-Callback》的执行结果为false,那么《代码c-msg》执行
- 对,估计我这个猜测是对的,这是基于 dispatchMessage处理时 《代码c-Callback》的执行结果会返还一个boolean值,这个值会影响那么《代码c-msg》的执行
3.Message
在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。message的用法比较简单,有这么几点需要注意:
- 如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存
- 擅用message.what来标识信息,以便用不同方式处理message。
- 获取新的Message对象时, 尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,Message提供了obtain方法:避免我们自己去分配Message新的对象, 通过obtain获取,可能从MessagePool中获取,节约开销。因为:
- 通常消息处理完毕的时候,消息也基本上处于无用状态可以释放回收了。对于需要频繁的创建释放的对象来说,创建和释放类实例都是要开销的,太频繁的使开销增大不好,像Message这种很有可能会频繁的创建
- 于是我们可以将创建的对象用完之后保存在一个Pool里面,以便再重复利用节约频繁创建释放开销。
消息原型:
public final class Message implements Parcelable {
//标识消息
//标识消息
public int what;
int flags;
long when;
//传递简单数据
long when;
//传递简单数据
public int arg1;
public int arg2;
//传递较复杂数据 对象
public Object obj;
Bundle data;
//处理消息的目标Handler
Handler target;
//消息派发时 执行的Runnable对象
Runnable callback;
//使消息形成链表
Message next;
//建立一个消息pool,回收msg,以避免重复创建节约开销
//传递较复杂数据 对象
public Object obj;
Bundle data;
//处理消息的目标Handler
Handler target;
//消息派发时 执行的Runnable对象
Runnable callback;
//使消息形成链表
Message next;
//建立一个消息pool,回收msg,以避免重复创建节约开销
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 10;
}
MessagePool建立(在消息处理完毕之后才能进行):
public static void loop() {
while (true) {
//派发消息 msg.target.dispatchMessage(msg);
//消息处理完毕 回收
msg.recycle();
}
}
public void recycle() {
//回收Message 建立全局的MessagePool
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
//派发消息 msg.target.dispatchMessage(msg);
//消息处理完毕 回收
msg.recycle();
}
}
public void recycle() {
//回收Message 建立全局的MessagePool
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
4.其他相关
- Handler与Thread及Looper的关系可以用下面图来表示:
- 注意工作线程和主线程之间的竞争关系。推荐handler对象在主线程中构造完成(并且启动工作线程之后不要再修改之,否则会出现数据不一致),然后在工作线程中可以放心的调用发送消息SendMessage等接口。
- 除了2所述的hanlder对象之外的任何主线程的成员变量如果在工作线程中调用,仔细考虑线程同步问题。如果有必要需要加入同步对象保护该变量。
- handler对象的handleMessage接口将会在主线程中调用。在这个函数可以放心的调用主线程中任何变量和函数,进而完成更新UI的任务。
- Android很多API也利用Handler这种线程特性,作为一种回调函数的变种,来通知调用者。这样Android框架就可以在其线程中将消息发送到调用者的线程消息队列之中,不用担心线程同步的问题。
- 并非每一个线程都有消息处理循环,因此 Framework 中线程可以分为两种:有 Looper 的和无 Looper 的。为了方便 app 开发,Framework 提供了一个有 Looper 的 Thread 实现:HandlerThread。两种不同 Thread 的 run() 方法有区别。
当时的几个疑问:
- Looper循环线程跟while什么的循环一样吗,它循环的到底是哪段代码
- 哈哈,其实就是while循环,没什么不一样,循环的就是循环内的代码,具体情况看我的关于Looper类的表述
- 发送的时候为什么要分msg和执行代码,直接发送执行代码不行吗
- 已在正文中解决
- 安卓中的线程进程是什么样的,跟activity有什么关系,活动切换会新建线程吗?
- 正常情况下一个APP启动就开启了一个进程,它也一直就只有这一个进程
- 然后每个这样的进程中有一个主线程,可以有很多子线程
- 这个主线程也叫UI线程,只有这个线程能绘制UI界面,对UI进行修改
- 一个 app 可以有多个 activity, 但是他们的 activity 都是在同一个线程中进行绘制的,所以只有一个主线程,也就是他们都运行在同一个线程上
- 所有耗时的、复杂的操作都不应该在主线程中运行,这样会导致程序占用主线程资源,而导致界面卡顿。
- 其实:几乎所有的GUI程序(android,javaswing,winform)都会使用一个线程来完成界面的显示。这个线程叫做主线程,或者event dispacture thread(edt ,事件派发线程)。这个概念在所有的GUI程序中都存在。
源码一:
使用Looper类创建Looper线程很简单:
public class LooperThread extends Thread {
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// ...其他处理,如实例化handler
// 开始循环处理消息队列
Looper.loop();
}
}
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// ...其他处理,如实例化handler
// 开始循环处理消息队列
Looper.loop();
}
}
源码二:
Looper类源码
public class Looper {
// 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
private static final ThreadLocal sThreadLocal = new ThreadLocal();
// Looper内的消息队列
final MessageQueue mQueue;
// 当前线程
Thread mThread;
// 。。。其他属性
// 每个Looper对象中有它的消息队列,和它所属的线程
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
private static final ThreadLocal sThreadLocal = new ThreadLocal();
// Looper内的消息队列
final MessageQueue mQueue;
// 当前线程
Thread mThread;
// 。。。其他属性
// 每个Looper对象中有它的消息队列,和它所属的线程
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
// 我们调用该方法会在调用线程的TLS中创建Looper对象,核心就是将looper对象定义为ThreadLocal,一个Thread只能有一个Looper对象
public static final void prepare() {
if (sThreadLocal.get() != null) {
// 试图在有Looper的线程中再次创建Looper将抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
if (sThreadLocal.get() != null) {
// 试图在有Looper的线程中再次创建Looper将抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());(把本looper放入系统提供的ThreadLocal保险箱中,诶,你发现它没有持有自己的引用)
} // 其他方法
//loop方法不断从自己的MQ中取出队头的消息(也叫任务)执行
public static final void loop() {
Looper me = myLooper(); //得到当前线程Looper(这个估计也是在Looper类中实现的一个方法)
MessageQueue queue = me.mQueue; //得到当前looper的MQ
MessageQueue queue = me.mQueue; //得到当前looper的MQ
// 开始循环
while (true) {
Message msg = queue.next(); // 取出message 这里好厉害,一个.next()后边其实做了两件大事:1)有一个接受机制,默默接受发送来的msg;2)有一个队列专门把接收到的msg排列好等待循环来取
if (msg != null) {
if (msg.target == null) {
// message没有target为结束信号,退出循环 或者说当target!=null时,继续往下走
return;(它就是这个循环结束的开关,所以它不是死循环!后边的quit()方法就是通过它来结束循环)
}
// 非常重要!将真正的处理工作交给message的target,即后面要讲的handler
msg.target.dispatchMessage(msg);
msg.target.dispatchMessage(msg);
// 回收message资源
msg.recycle();
}
}
msg.recycle();
}
}
}
//除了prepare()和loop()方法,Looper类还提供了一些有用的方法
//Looper.myLooper()得到当前线程looper对象
public static final Looper myLooper() {
// 在任意线程调用Looper.myLooper()返回的都是那个线程的looper
return (Looper)sThreadLocal.get();
}
//getThread()得到looper对象所属线程
public Thread getThread() {
return mThread;
}
//quit()方法结束looper循环
public void quit() {
// 创建一个空的message,它的target为NULL,表示结束循环消息
Message msg = Message.obtain();
// 发出消息
mQueue.enqueueMessage(msg, 0);
}
}
源码三:
Handler类及其默认的构造方法
public class Handler {
final MessageQueue mQueue; // 关联的MQ
final Looper mLooper; // 关联的looper
final Callback mCallback; //JAVA中的Callback接口应该就是一个JAVA内置的回调模板吧,其中有个callBack()方法,其实结构很简单,自己实现也没问题,只是因为用得多所以JAVA内置
// 其他属性
public Handler() {
public Handler() {
// 默认将关联当前线程的looper的构造方法,应该还有其他的构造方法用来绑定looper
mLooper = Looper.myLooper();
// looper不能为空,即该默认的构造方法只能在looper线程中使用
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 重要!!!直接把关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上
mQueue = mLooper.mQueue;
mCallback = null;
}
mCallback = null;
}
//由外部传入Looper:当前线程或其他线程
public Handler(Looper looper) {
//初始化构建消息系统参数
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
// 带callback的,一个handler可以设置一个callback。如果有callback的话,
//凡是发到通过这个handler发送的消息,都有callback处理,相当于一个总的集中处理
public Handler(Looper looper, Callback callback) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
}
// 其他方法
}
}
源码四:
为之前的LooperThread类加入Handler:
public class LooperThread extends Thread {
private Handler handler1;
private Handler handler2;
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// 实例化两个handler
handler1 = new Handler();
handler2 = new Handler();
// 开始循环处理消息队列
Looper.loop();
}
private Handler handler2;
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// 实例化两个handler
handler1 = new Handler();
handler2 = new Handler();
// 开始循环处理消息队列
Looper.loop();
}
}
源码五:
Handler类的发送消息方法
// 此方法用于向关联的MQ上发送Runnable对象,它的run方法将在handler关联的looper线程中执行
public final boolean post(Runnable r)
{
// 注意getPostMessage(r)将runnable封装成message,然后真正调用的是sendMessageDelayed把这个msg发送出去,不过最终还是和handler.sendMessage一样,调用了sendMessageAtTime,然后调用了enqueueMessage方法,给msg.target赋值为handler,最终加入MessagQueue.
return sendMessageDelayed(getPostMessage(r), 0);
}
private final Message getPostMessage(Runnable r) {
}
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain(); //得到空的message,产生一个Message对象,可以new ,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存
m.callback = r; //将runnable设为message的callback(每个msg中有一个Runnable类型的成员callback,当调用post方法发送一个Runnable对象时就进行这样的处理--把Runnable对象存入msg的这个callback成员中,之后收到msg后,第一件事就是判断msg的这个成员是不是null,如果是,才调用回调方法,如果不是就意味着传来一个Ruannable对象,就会直接执行它的run方法,因为此时不是用.start()方法,所以就是在本线程直接执行,这其实也是一种回调。而且所以是Runnable对象的话,也完全不会执行handMessage钩子方法)
return m;
}
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this; // message的target必须设为该handler!
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this; // message的target必须设为该handler!
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
源码六:
Handler类的处理消息方法
// 处理消息,该方法由looper调用
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
if (msg.callback != null) {
// 如果message设置了callback,即runnable消息,处理callback!
handleCallback(msg);
} else {
// 如果handler本身设置了callback,则执行callback
if (this.mCallback != null) {
return;
}
}
// 如果message没有callback,则调用handler的钩子方法handleMessage(这句在第二个else里)
handleMessage(msg);
}
}
// 处理runnable消息
}
}
// 处理runnable消息
private final void handleCallback(Message message) {
message.callback.run(); //直接调用run方法!因为此时不是用.start()方法,所以就是在本线程直接执行!
}
// 由子类实现的钩子方法
public void handleMessage(Message msg) {
}
}
另行实现的Handler.Callback接口中有:
private
class
MyHandlerCallback
implements
Handler.Callback {
@Override
public
boolean
handleMessage(Message msg) {
// Handle messages.!注意,实现这个接口覆盖这个方法的时候要返回值,这个值比较重要
return
false
;
}
}
源码七:
一个安卓的例子:
public class TestDriverActivity extends Activity {
private TextView textview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textview = (TextView) findViewById(R.id.textview);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textview = (TextView) findViewById(R.id.textview);
// 创建并启动工作线程
Thread workerThread = new Thread(new SampleTask(new MyHandler())); //干脆在主线程这里把绑定本线程的handler传到子线程中去,免得去子线程中麻烦地指定handler的线程
workerThread.start();
}
}
public void appendText(String msg) {
textview.setText(textview.getText() + "\n" + msg);
}
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
String result = msg.getData().getString("message");
// 更新UI
appendText(result);
}
}
}
}
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
String result = msg.getData().getString("message");
// 更新UI
appendText(result);
}
}
}
public class SampleTask implements Runnable {
private static final String TAG = SampleTask.class.getSimpleName();
Handler handler;
public SampleTask(Handler handler) {
super();
this.handler = handler;
}
@Override
public void run() {
try { // 模拟执行某项任务,下载等
Thread.sleep(5000);
// 任务完成后通知activity更新UI
Message msg = prepareMessage("task completed!");
// message将被添加到主线程的MQ中
handler.sendMessage(msg);
} catch (InterruptedException e) {
Log.d(TAG, "interrupted!");
}
}
private Message prepareMessage(String str) {
Message result = handler.obtainMessage();
Bundle data = new Bundle();
data.putString("message", str);
result.setData(data);
return result;
}
private static final String TAG = SampleTask.class.getSimpleName();
Handler handler;
public SampleTask(Handler handler) {
super();
this.handler = handler;
}
@Override
public void run() {
try { // 模拟执行某项任务,下载等
Thread.sleep(5000);
// 任务完成后通知activity更新UI
Message msg = prepareMessage("task completed!");
// message将被添加到主线程的MQ中
handler.sendMessage(msg);
} catch (InterruptedException e) {
Log.d(TAG, "interrupted!");
}
}
private Message prepareMessage(String str) {
Message result = handler.obtainMessage();
Bundle data = new Bundle();
data.putString("message", str);
result.setData(data);
return result;
}
}