简述Handler机制
我会对android的消息处理有三个核心类逐步介绍,他们分别是:Looper,Handler和Message。其实还有一Message Queue(消息队列),知道它是队列即可,就像我们所熟知的数组,它是另一种数据结构,这里不对它做介绍了。
我会以尽量浅显易懂的文字去描述,逐步介绍三个核心类,这个也是安卓面试常考的题目,希望大家都能学会Handler机制,如果文章上有一些谬误,或者说你们有什么意见或者建议,欢迎留言。
概述
1、Looper,Handler,Message和MessageQueue之间的关系:
你可以这样去理解:handler是一个工人,跟他相关的有这些组件:
- Message :Handler接收和处理的对象(待处理的产品)
- MessageQueue:消息队列,他采用队列的方式管理Message(传送带)
- Looper:每个线程中只能有一个looper对象,调用它的looper方法负责读取MessageQueue中的消息;(发动机)
2、形象地描述:
1、主线程中有很多个Handler(工人),
2、他们共用一个MessageQueue(传送带),一个Looper(发动机),然后MessageQueue(传送带)上有各种Message(待处理产品),
3、Looper(发动机)会遍历MessageQueue(传送带),
4、Looper(发动机)看到了Message(待处理产品),就将它交给专门的Handler(工人)负责。
3. Handler类
Handler有两个重要的功能,一个是发送消息,一个是处理处理消息。
常用的方法:
- void handleMessage(Message msg) 用于重写,处理接收的消息
- final Boolean hasMessage(int what) 检查消息队列中是否包含what为指定值的消息
- fina Boolean hasMessage(int what, Object obj)检查消息队列中是否有what为指定值,obj为指定对象的消息
- Message、obtainMessage();获取消息(有多个重载可以获取what值为指定值得message·)
- sendEmptyMessage(int what) 发送空消息
- final Boolean sendEmptyMessageDelayed(int what,long
delayMills):指定多少毫秒后发送消息 - final Boolean sendMesage(Message msg);立即发送消息
- final Boolean sendMessageDelayed(Message msg,long mills)延时发送消息
4. Message类
android.os.Message的主要功能是进行消息的封装,同时可以指定消息的操作形式,在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。
常用变量和方法:
- public int what:变量,用于定义此Message属于何种操作
- public Object obj:变量,用于定义此Message传递的信息数据,通过它传递信息
- public int arg1:变量,传递一些整型数据时使用
- public int arg2:变量,传递一些整型数据时使用
- public Handler getTarget():取得操作此消息的Handler对象。
注意事项:
- 尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。
- 如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存
- 擅用message.what来标识信息,以便用不同方式处理message。
5. Looper类
Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。
在一个Activity中,系统会自动帮用户启动Looper对象,它负责不断地遍历MessageQueue上的消息。
用户也可以自定义Looper线程,则需要用户手工调用Looper类中的方法。
源码分析
Looper
Looper的表现形式为享元模式,线程中的所有Handler共享这个元件,而且,它的写法保证了它在线程中的单例,也就是说一个线程中,只会有一个Looper。
将普通线程升级为Looper线程
执行以下代码就好了,是不是很简单?
public class LooperThread extends Thread {
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// ...其他处理,如实例化handler
// 开始循环处理消息队列
Looper.loop();
}
}
Looper构造函数
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
在构造方法中,它创建了一个MessageQueue(消息队列)。
prepare()底层代码
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(true));
}
可以看到,在prepare()方法中,它先通过sThreadLocal.get()判断当前的ThreadLocal中是否包含Looer,如果没有就创建一个,放到ThreadLocal。
如果已经有Looper就抛出异常。也就说Looper.prepare()不能被调用两次,它保证了一个线程中只有一个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.mQueue;
// 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 msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.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() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycle();
}
}
方法还是先获取了ThreadLocal的Looper实例,如果looper为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。
紧接着,就拿到该looper实例中的mQueue(消息队列),然后开始无限循环,遍历消息队列。
它会不断地取出一条消息,如果没有消息就不断地return(阻塞)。
调用 msg.target.dispatchMessage(msg)分发消息;其中msg的target其实就是一个handler对象。
Handler
Handler构造函数
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
这是默认的Handler构造函数,它自动地为当前Handler添加了一个Looper,如果当前线程没有Looper,他就会报错。
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
而通过这个构造函数就可以在Handler中添加自己的Looper了。
sendMessage()的底层代码实现
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
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) {
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
sendMessage()调用到最后会执行enqueueMessage方法,enqueueMessage中首先为meg.target赋值为this,因此有下面结论:
1、把当前的Handler作为msg的target属性,msg也拥有了Handler的引用。
2、msg被放置到消息队列中,由Looper开始循环遍历。
3、当Looper遍历到msg,Looper直接根据msg中的target(Handler引用)找到对应的Handler。
主线程中的Looper
Android应用程序的入口为ActivityThread.main方法,此方法中首先会创建Application和默认启动的activity,并将他们关联在一起,而该应用的UI线程的Looper也是在这创建的,因此我们不用再显式地创建Looper。
public static void main(String args[]) {
//...代码省略
//1.创建消息循环Looper,也就是UI线程的消息队列,prepareMainLooper底层代码就是prepare
Looper.prepareMainLooper;
//启动ActivityThread
ActivityThread thread = new ActivityThread();
Thread.attach(false);
AsyncTask.init();
Looper.loop();//启动looper
}
Handler虽然能传Runable对象,内部实际用的还是Message
如果仅仅查看Handler的API,你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了,而且最终也是加到了MQ当中。
Handler中源码如下:
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
总结:
1、首先Looper.prepare()在本线程中创建一个Looper实例,创建Looper实例的时候,Looper构造函数中也创建了一个MessageQueue对象。
也就是说:Looper和MessageQueue一个线程只会有一个。
2、Looper.loop()会让当前线程进入一个无限循环,不断从MessageQueue的实例中读取消息。
3、Looper根据Msg的target找到对应的Handler,然后把Msg交给那个Handler去处理。
4、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue关联。
5、Handler的sendMessage方法中,Handler会将自身作为参数传给msg,msg中的target就是Handler自己,然后msg会加入到MessageQueue中。
6、在构造Handler实例时,我们会重写handleMessage方法,也就是消息分发中(msg.target.dispatchMessage(msg)),最终会执行(回调)我们handleMessage中写的方法。
最新文章
- what is difference in (int)a,(int&;)a,&;a,int(&;a) ?
- mongoDB研究笔记:复制集故障转移机制
- HDU1269 迷宫城堡
- zepto的bug2
- Recruit Coupon Purchase Winner&#39;s Interview: 2nd place, Halla Yang
- 阿里云实战之二(mysql+phpmyadmin)
- java.lang.StringBuilder源码分析
- JAVA--线程wait()、lnotify()和notifyAll()方法
- WindowsclientC/C++编程规范“建议”——前言
- 网络负载均衡环境下wsHttpBinding+Message Security+Windows Authentication的常见异常
- UML2.0
- VS2017提醒找不到MSVCR110D.dll
- day 4 - 2 数据类型练习
- poj 1789 prime
- PostgreSQL基础知识与基本操作索引页
- col-xs , col-sm , col-md , col-lg是什么意思?什么时候用?
- 让“懒惰” Linux 运维工程师事半功倍的 10 个关键技巧!
- Validate the date format
- vo、po、dto、bo、pojo、entity、mode如何区分
- 查询出结果 给其 加上序号的方法 msql