本篇博客主要为Handler-Message-looper的一些深入学习笔记。
之前学习过Handler-Message-looper的相关知识,借着AOSP的学习计划,再更加深入理解一下该方法的知识。 首先,如何理解这三者?
Handler为消息的接收者和发送者,Message为消息,当然还有一个消息队列为MessageQueue。而Looper可以理解为消息队列的控制器。
一、Looper 首先,我们来看Looper,消息控制器。 在该类中,主要方法包括prepare和loop,其中使用prepare来生成消息控制器。
1 2 3 4 5 6 private static void prepare (boolean quitAllowed ) { if (sThreadLocal.get () != null ) { throw new RuntimeException("Only one Looper may be created per thread" ); } sThreadLocal.set (new Looper(quitAllowed)); }
其作用是new一个Looper,也就是new一个消息控制器。然后将该消息控制器加入到静态变量sThreadLocal中,也就是将looper和线程进行绑定,从而和线程对应的messageQueue绑定。
1 2 3 4 5 6 7 8 public void set (T value ) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null ) { values = initializeValues(currentThread); } values.put(this , value ); }
其中的Values为Thread的内部类,
1 2 3 4 5 6 7 8 9 10 11 12 Values() { initializeTable(INITIAL_SIZE); this .size = 0 ; this .tombstones = 0 ; } private void initializeTable (int capacity) { this .table = new Object[capacity * 2 ]; this .mask = table.length - 1 ; this .clean = 0 ; this .maximumLoad = capacity * 2 / 3 ; }
this.table为一个数组,该数组存储着looper数据。
注意到上面的参数quitAllowed,该参数传给消息队列,用来告知其是否可以退出。 在基本的prepare构造函数上,有两个扩展的构造函数,一个是
1 2 3 public static void prepare () { prepare(true ); }
另一个给主线程创建消息控制器的:
1 2 3 4 5 6 7 8 9 public static void prepareMainLooper () { prepare(false ); synchronized (Looper.class) { if (sMainLooper != null ) { throw new IllegalStateException("The main Looper has already been prepared." ); } sMainLooper = myLooper(); } }
Looper.loop(); 则是让Looper开始工作,从消息队列里取消息,处理消息。 该函数是一个循环,当调用mHandler.getLooper().quit()后则退出循环。
二、Handler Handler理解为消息的处理者和发送者。 我们在编程时如果使用了
1 Handler handler = new Handler ();
无参的Handler会调用
1 mLooper = Looper.myLooper()
这里实际上是将Handler和Looper相绑定,而looper则通过调用prepare方法完成和线程的绑定。
如果当前的looper为null的话,则是因为没有调用Looper.prepare。
一般如果我们在主线程的话,则直接使用
new Handler(Looper.getMainLooper())该方法将handler和主线程的looper绑定,从而获得主线程上的消息队列绑定,而该handler则是公共的,我们即可以在主线程上调用handler.post发送消息,也可以在其他线程上调用handler.post发送消息。但是其发送的消息还是到了线程对应的消息队列中,并且该handler收消息和处理消息时还是只能在对应的线程上进行。 - 从任意线程发送消息给一个looper的消息队列; - 处理相关联的looper发过来的消息。
在其他线程中,我们在run的时候需要调用Looper.prepare用来初始化控制器。 或者可以直接使用HandlerThread,
1 2 3 4 5 6 7 8 9 10 11 12 public void run() { mTid = Process.myTid() Looper.prepare() synchronized (this) { mLooper = Looper.myLooper() notifyAll() } Process.setThreadPriority(mPriority) onLooperPrepared() Looper.loop() mTid = -1 }
它直接帮我们完成了消息控制器的初始化和启动。 有关Handler的dispatchMessage和obtainMessage的用法,可以参考下面
三、其他 1.有关Handler和Message的使用,可以参考下面的用法,在主线程上创建Handler,获取主线程的looper以及消息队列。然后在任意线程上使用该Handler发送消息,该消息加入到主线程的消息列表上了,当looper看到MessageQueue中含有Message,就将其广播出去。该handler对象收到该消息后,调用相应的handler对象的handleMessage()方法对其进行处理。同理 当我们使用handler.post方法的时候 我们会创建包含一个runnable引用的message,然后将这个message添加到这个looper的消息队列 并且其中的run方法中的内容会在与handler相关联的looper对应的线程上运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private final Handler handler = new Handler (new Handler .Callback () { @Override public boolean handleMessage(Message msg) { if (msg.what == MSG_CLEAR_CACHE_SUCCESS ) { processAfterClearCache(); } else if (msg.what == MSG_GET_CACHE_SUCCESS ) { String cacheString = msg.obj.toString(); if (cacheString.equals("0K" )) { tvCacheSize.setText("" ); } else { tvCacheSize.setText(cacheString); } } return true ; } }); private final class ShowCacheThread extends Thread { @Override public void run() { String cacheSize = "" ; cacheSize = BitmapCache .getInstance().getFsCacheSize(); Message msg = handler.obtainMessage(MSG_GET_CACHE_SUCCESS , cacheSize); handler.sendMessage(msg); } }
2.关于主线程,之所以ActivityThread叫主线程是因为如下,在main函数中会调用prepareMainLooper,并且调用looper启动消息的循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void main(String [] args) { Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false ); if (sMainThreadHandler == null ) { sMainThreadHandler = thread.getHandler(); } if (false ) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread" )); } Looper.loop (); }
参考资料 [1]https://greenrobot.me/devpost/android-psvm/