很多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); } }
觉得写得好,点个赞吧~写得不好,还望大家多多指出