这一章主要讲了以Handler 为中心的 Android 消息机制。这要是 Handler , MessageQueue, Looper
Handler : 用于发送消息和处理消息;
MessageQueue: 用于描述消息队列;
Looper : 创建消息队列以及进入消息循环。
一、 Handler
1、 创建时采用系统当前线程的 Looper 来构建消息循环系统;
2、主要作用是将一个任务切换到某一个指定的线程中运行;
3、通过 ThreadLocal 获取当前线程的 Looper;
4、解决在子线程中无法访问 UI 线程的矛盾
UI 线程, ActivityThread, 被创建时会初始化 Looper, 所以主线程中默认有Looper, 即可以默认使Handler;
其他线程没有默认的的 Looper, 所以在使用 Handler 前必须为该线程创建 Looper, 否则会抛出 java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare();
解决方法:
通过 Looper.prepare() 可为当前线程创建一个 Looper, 接着通过 Looper.loop() 开启消息循环;
Looper.prepare(); Handler handler = new Handler(); Looper.loop();
延伸: Android 提供 Handler 这种消息机制的原因:
Android 规定访问 UI 线程只能在主线程中进行,如果在子线程中访问,程序抛出异常;
子线程不能访问UI 线程的原因:
Android 的 UI 控件 不是线程安全的,多线程访问会导致 UI 控件处于不可预期的状态。
1、可以对 UI 控件访问加锁。产生的缺点,会使访问 UI 逻辑变得复杂;加锁会阻塞某些线程的执行,加锁机制降低UI 访问的效率。
2、采用单线程模型,简单高效。所以采用了 Handler.
二、MessageQueue
在MessageQueue 类中,成员变量 Message mMassage 描述当前线程需要处理的消息。
在MessageQueue 内部中, 调用 boolean enqueueMassage(Massage msg, long when) 方法进行消息单链表的插入操作;
Massage next() 方法实现消息的读取;改方法是一个无限循环的方法。如果消息队列中没有消息,next 方法会一直堵塞。当有新消息到来时,next 方法会返回这条消息,并从消息单链表中移除。
三、Looper
在Looper 类中,静态成员变量 sThreadLocal = new ThreadLocal<Looper>; 保存线程中 Looper 对象。
Looper 启动时,如果是主线程,主线程会提供主线程的 Looper.prepareMainLooper() 方法; 如果是在普通线程则提供 Looper.prepare() 供调用。
退出 Looper, quit 方法直接退出; quitSafely 方法则是通过设置标志位,等消息队列中已有消息处理完毕后才安全退出。
如果在子线程中,手动为该线程创建 Looper, 在事情执行完成后,应用调用quit 方法终止消息循环。
四、ThreadLocal
不同线程访问用一个ThreadLocal 对象,取到的值时不一样的。因为 ThreadLocal 的 set 和 get 放法操作的对象时当前线程的locavalues 对象的 table 数组。因此在不同线程访问同一个ThreadLocal 的set 和 get 方法,它们对ThreadLocal 所做的读写操作仅限在各自线程的内部。
应用场景:
1、当某些数据是以线程为作用域并且不同线程具有不同数据副本的时候,可以考虑使用;
2、复杂逻辑下的对象传递;
五、整个消息机制远行的过程
拓展:
一、 Handler 出来除了在子线程更新视图为,也可以用来定时执行一些任务。下面是官网的解析。
Two main uses to Handler
1、to schedule message and runnables to executed as some point int he furture.
2、to enqueue an action to be performed on a different thread than your own.
二、在 Handler.post() 方法中的任务 是运行在主线程中,所以,避免在任务中进线大量的工作,避免主线程的堵塞
private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 0: Log.i(TAG, "in Handler Thread id is " + Thread.currentThread().getId()); break; default: break; } } }; // 启动一个新的线程 private void startNewThread(){ new Thread(new Runnable() { @Override public void run() { Log.i(TAG, "in startNewThread Thread id is " + Thread.currentThread().getId()); testHandler(); } }).start(); } private void testHandler(){ mHandler.post(new Runnable() { @Override public void run() { Log.i(TAG, "in testHandler run Thread id is " + Thread.currentThread().getId()); } }); }
运行的Log