很多android开发者都赞叹Handler的方便和易用,这几天花了点时间仔细研究下,谈谈Handler的机制,刚开始写博客,写得不好还望大家多多指教。分4部分写吧:Handler的创建,Handler获得消息,发送消息,和模拟handler。
1.new Handler()
我们来看下Handler的构造方法:
public Handler(Callback callback, boolean async) {
//省略一些代码
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 = callback;
mAsynchronous = async;
}
来看下这行代码:
mLooper = Looper.myLooper();
此处先去看此线程有无创建过Looper(由ThreadLocal获去处理,ThreadLocal是一个线程内部的数据存储类,每个线程可通过ThreadLocal存储数据,数据存储之后,只有在相应线程中才可以获取到存储的数据,这就保证了一个线程只能创建一个唯一的Looper对象。如果没有创建过就会创建一个Looper),如果创建过,就直接拿出来用,没有就创建一个,包括我们在子线程创建Handler时要先Looper.prepare()也是为了先获得Looper对象,但为什么在主线程不需要呢?因为在ActivityThread(Activity应用入口)时会先调用Looper.prepareMainLooper(),为主线创建Loope对象。
再看下Looper的构造函数:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
发现它是私有的,并且在创建的时候保存了当前线程以及创建了一个消息队列MessageQueue。
所以综上我们可以看到,在一个线程里Handler,Looper,MessageQueue的数量比=n:1:1.即可以有多个Handler,但只能有一个Looper和MessageQueue。
ThreadLocal的举例:
ThreadLocal<Integer> mIntegerThreadLocal=new ThreadLocal<>();
mIntegerThreadLocal.set(10);//主线程设置为10
Log.d(TAG,"[Thread#main]mIntegerThreadLocal="+mIntegerThreadLocal.get());//主线程打印
new Thread("ThreadA"){
@Override
public void run() {
Log.d(TAG,"[Thread#ThreadA]mIntegerThreadLocal="+mIntegerThreadLocal.get());//子线程打印
mIntegerThreadLocal.set(100);//在子线程改为100
}
}.start();
new Thread("ThreadB"){
@Override
public void run() {
try {
Thread.sleep(1000);//稍稍延时一下,让前面线程先执行完
} catch (InterruptedException e) {
e.printStackTrace();
}
mIntegerThreadLocal.set(1000);//在子线程改为1000
Log.d(TAG,"[Thread#ThreadB]mIntegerThreadLocal="+mIntegerThreadLocal.get());
}
}.start();
//最后再在主线程访问一遍
Thread.sleep(2000);
Log.d(TAG,"[Thread#main]mIntegerThreadLocal="+mIntegerThreadLocal.get());
打印结果:
D/ThreadLocalTest:[Thread#main]mIntegerThreadLocal=10
D/ThreadLocalTest:[Thread#ThreadA]mIntegerThreadLocal=null
D/ThreadLocalTest:[Thread#ThreadB]mIntegerThreadLocal=1000
D/ThreadLocalTest:[Thread#main]mIntegerThreadLocal=10
从上面日志可以看出,虽然在不同线程中访问的是同一个ThreadLocal对象,但是他们通过ThreadLocal获取到的值却是不一样的,这就是ThreadLocal的奇妙之处,更具体的大家可以去看看ThreadLocal内部set和get方法,以及类内部维护的ThreadLocal.Values对象,对Looper创建的唯一性会有更好的了解。
2.通过Handler获取消息:
Handler.obtainMessage()方法,实际上调用的是Message.obtain()方法,该方法会从Message的消息池里获得Message对象,如果消息池有可用的消息对象,就直接拿过来使用,没有的话就会新建一个。改消息池初始化为0,最大数量是50。使用消息池的好处:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。这样也就推荐大家在使用handler发送消息时,尽量用handler.obtainMessage()方法获得消息对象,而不是new 一个Message对象。
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-useflag
sPoolSize--;
return m;
}
}
return new Message();
}
3.Handler发送消息.
这部分是核心。Handler发送消息实际上是把消息发送到该Handler对应的Looper所管理的MessageQueue,这里没啥可说,重要的是如何把信息又发回给Handler。这里起作用的就是Looper.loop()方法了。我们都知道,在子线程中,我们除了要Looper.prepare()之外,还要再调用Looper.loop()这个方法.在主线程中不需要,也是因为ActivityThread入口main方法帮我们调用了Looper.loop()这个方法:
public static void main(String[] args) {
//此处省略了一些代码
Looper.prepareMainLooper();//为主线程创建Looper对象
// 创建ActivityThread实例
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();//开启循环
}
那个loop()方法究竟有什么作用呢?我们看一下它的源码:
public static void loop() {
final Looper me = myLooper();
//所以在子线程中必须先prepare,再loop
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; //这里Looper对象会一直在消息队列里查看是否有消息
}
// 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);//如果有消息,会被分发到对应的Handler,target就代表要被接收此消息的handler
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();
}
}
简单来说,就是在loop方法里,Looper对象会一直在消息队列里查看是否有消息,有就分发消息,没有就继续循环,所以如果在子线程中没有调用Looper.loop()方法,handler是没有办法处理消息的。
4.总结:
Handler能在线程之间通讯,很重要的也是依赖于MessageQueue这个容器,不同的线程从容器存取东西都会影响其他线程对容器的访问结果。
自己画了一张简易的流程图:最后简单地模拟Handler对象在线程间的通讯:
private ArrayList<Integer> arrayList=new ArrayList<>();
private TextView textView=(TextView) findViewById(R.id.handler_textView);
new ThreadA().run();
try {
Thread.sleep(500);
textView.setText("获得的数字是:"+arrayList.get(0));//子线程加进去的10主线程这里就可以拿到了
} catch (InterruptedException e) {
e.printStackTrace();
}
private class ThreadA implements Runnable{
@Override
public void run() {
arrayList.add(10);
}
}
觉得写得好,点个赞吧~写得不好,还望大家多多指出