标签:
①、我们不能在子线程中去访问UI空控件,这是时候只能通过Handler将更新UI的操作放到主线程中去执行
②、Handler的组成:messageQueue和Looper的支持
③、MessageQueue:作用:存储了一组消息,以队列的形式对外提供插入和删除的工作。实际上是运用单链表的数据结构来存储消息列表的。
④、Looper 作用:由于MesageQueue无法处理消息,Looper填补了这一个功能,Looper会无限循环的形式去查找是否有新消息,如果有就处理消息,没有的话就一直等待
⑤、Looper的特殊概念ThreadLocal:并不是线程,但是可以在线程中存储数据。
举例:当Handler创建的时候默认会采用当前线程的Looper来构造其循环系统。那么如何获取当前线程的Looper,或者指定线程的Looper呢?
这就要使用到ThreadLocal,ThreadLocal能够在不同线程中互不干扰地存储和提取数据。所以通过ThreadLocal能够轻松获取每个线程的Looper。
注意:线程是默认没有Looper的,需要使用Handler就必须先为线程创建Handler
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
为什么主线程就不需要呢?
因为在ActivityThread被创建是就已经初始化了Looper
Android的消息机制:主要指Handler的运行机制和Handler所附带了MessageQueue和Looper的工作过程。
Handler的主要作用:讲一个任务切换到某个指定的线程中去执行,
问:Android为什么要有这个功能呢?
Android规定访问UI只能在主线程中执行:
在ViewRootImpl中对UI验证做了判断(P373 ①)。
如果没有Handler,我们在子线程中进行一些数据处理,之后要根据数据修改UI,如果没有Handler我们就无法将访问UI的工作切换到主线程中去。
问:为什么规定UI只能在主线程中执行?
因为如果多线程中并发访问可能会导致UI控件处于不可预期状态。
Handler的运行机制
1.当Handler创建完毕,说明其内部的Looper和MessageQueue可以和Handler协同工作了
2.通过Handler.post()方法将一个Runnable投递到Handler内部的Looper中去处理,或者使用send()方法。因为post()方法最终也是通过send方法来完成的。
3.send()方法的工作流程:当Handler的send()方法被调用时,它会调用MessageQueue的enqueueMessage()方法,将消息放入消息队列中,然后Looper发现有新消息来到的时候就会处理这条消息,最终消息中的Runnable()或者Handler的handler的handleMessage()方法就会被调用。
调用图:
使用情景:当某些数据是以线程为作用域并且不同线程具有不同数据副本的时候。
举例:
private static final String TAG = "MainActivity"; private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<Boolean>(); //在mainThread中放入true类型的数据 mBooleanThreadLocal.set(true); Log.d(TAG,"MainActivity"+mBooleanThreadLocal.get()); new Thread("Thread#1"){ public void run(){ mBooleanThreadLocal.set(false); Log.d(TAG,"Thread#1"+mBooleanThreadLocal(.get()); } } new Thread("Thread#2"){ publci void run(){ Log.d(TAG,"Thread#1"+mBooleanThreadLocal(.get()); } }
返回结果:
MainActivity:true Thread#1:false Thread#2:null
说明:虽然不同线程访问的是同一个ThreadLocal但是根据线程不同,返回的数据是根据在对应线程中设置的数据。(就好像有副本一样)
工作原理:
1.查看ThreadLocal的set()方法(P377 ①)
①、首先获取当前线程
②、根据当前线程调用value()方法返回当前线程的value对象(其中存储了了该Thread的数据)。
Value类:Thread内部专门存储ThreadLocal的数据 (表示当前线程中存储了ThreadLocal的数据,而不是在ThreadLocal中存储了线程的数据)
③、将数据放入value.table这个数据结构中。
table数据结构:数组private Object[] table,ThreadLocal的值就存储在其中。
2.查看ThreadLocal的get()方法(P379 ①)
同样是取出当前线程的value.table()然后,根据索引获取数据
①、MessageQueue的操作
插入(enqueueMessage())和读取(next())(读取操作会伴随着删除操作)
②、enqueueMessage()的操作:简单的单链表的插入操作 next():寻找未被锁的Message,并返回给调用者
作用:不同的调用MessageQueue的next()方法查看是否有新的信消息,如果有新的消息就会立即处理,如果没有就一直阻塞在那。
构造方法(P383 ①)
1.保存当前Thread对象。2.创建一个MessageQueue
标签:
原文地址:http://www.cnblogs.com/rookiechen/p/5468376.html