Android中的Handler机制分析(三) Looper分析和Handler其他知识

来源:互联网 发布:什么是站外优化 编辑:程序博客网 时间:2024/06/09 14:31

在前面的博客中我们介绍了Handler相关的几个类(Handler、Message和MessageQueue),在这一篇博客中,我们介绍Handler机制中最后一个重要的类,Looper类。并介绍2个小知识点。

前两篇博客可以点击这里进入 《Android中的Handler机制分析(一) Handler和Message分析》 《 Android中的Handler机制分析(二) MessageQueue分析》


再说Looper之前,我们先来看一个相关的类:ThreadLocal类
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其它线程来说无法获取到数据。一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal。比如对于Handler来说,它需要获取当前线程的Looper,很显然Looper的作用域就是线程并且不同线程具有不同的Looper,这个时候通过ThreadLocal就可以轻松实现Looper在线程中的存取。

ThreadLocal类的几个特点:
1、每个线程都有自己的局部变量
    每个线程都有一个独立于其他线程的上下文来保存这个变量,一个线程的本地变量对其他线程是不可见的
2、独立于变量的初始化副本
    ThreadLocal可以给一个初始值,而每个线程都会获得这个初始化值的一个副本,这样才能保证不同的线程都有一份相同的拷贝
3、状态与某一个线程相关联
    ThreadLocal 是为了方便每个线程处理自己的状态而引入的一个机制 

另外,ThreadLocal有一个内部类ThreadLocalMap,这个类就是真正保存线程自己本地变量的容器。每一个线程都有自己的单独的一个ThreadLocalMap实例,其所有的本地变量都会保存到这一个map中。

简单示例:

// 创建一个ThreadLocal对象private static ThreadLocal<Integer> integerThreadLocal = new InheritableThreadLocal<Integer>(){// 重写方法,指定一个初始值@Overrideprotected Integer initialValue() {return 0;}};// 在onCreate方法中编写如下代码// 定义一个线程数组Thread[] threads = new Thread[5];for (int i = 0; i < 5; i++) {// 创建多个线程threads[i] = new Thread(){@Overridepublic void run() {// 获取当前线程的本地变量,然后累加Integer integer = integerThreadLocal.get();for (int i1 = 0; i1 < 5; i1++) {integer += 1;}// 重新设置累加后的本地变量integerThreadLocal.set(integer);// 重新获取值并打印出来Log.i("MainActivity", Thread.currentThread().getName() + " - integer value : " + integerThreadLocal.get());}};}// 开启线程for (int i = 0; i < threads.length; i++) {threads[i].start();}
运行结果如图:

我们可以看到5个线程最终打印的值都相同,也就说明了每一个线程第一次获取的值都是0,也就是定义的初始值,每一个线程拿到了初始值的副本,然后操作的也是各自拿到的副本,操作结果不会对初始值有影响。


ThreadLocal的几个方法说明:

// get()方法public T get() {        // 获取当前执行线程        Thread t = Thread.currentThread();        // 取得当前线程的ThreadLocalMap实例        ThreadLocalMap map = getMap(t);        // 如果map不为空,说明该线程已经有了一个ThreadLocalMap实例        if (map != null) {            // map中保存线程的所有的线程本地变量,我们要去查找当前线程本地变量            ThreadLocalMap.Entry e = map.getEntry(this);            // 如果当前线程本地变量存在这个map中,则返回其对应的值            if (e != null)                return (T)e.value;        }        // 如果map不存在或者map中不存在当前线程本地变量,调用setInitialValue()方法返回初始值        return setInitialValue();    }private T setInitialValue() {// 我们创建ThreadLocal时重写的方法,默认返回nullT value = initialValue();// 获取当前线程,然后取得当前线程的ThreadLocalMap实例Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);// 不为null,直接设置初始值if (map != null)map.set(this, value);// 为null,为当前线程创建一个ThreadLocalMap实例,并设置初始值elsecreateMap(t, value);return value;}// 创建一个ThreadLocalMap实例并赋值void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}// set()方法public void set(T value) {// 大部分和setInitialValue()方法一样,// 先获取ThreadLocalMap实例,获取到了设置值,没有获取到就创建一个并设置指定的值Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}// 获取当前线程的ThreadLocalMap实例ThreadLocalMap getMap(Thread t) {return t.threadLocals;}// 移除当前线程的ThreadLocalMap实例public void remove() {// 先获取到,不为null就移除ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);}

接下来我们就来看一下Looper类:

1.部分成员变量

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); // ThreadLocal对象private static Looper sMainLooper;  // 主线程的Looper对象final MessageQueue mQueue; // 与Looper绑定的MessageQueue对象final Thread mThread; // 当前Looper所在的线程


2.构造方法

// 构造方法用private修饰,表示不能其他类不能创建,需要一个boolean类型的参数private Looper(boolean quitAllowed) {// 在构造方法中与MessageQueue绑定// 将boolean类型的参数quitAllowed传给MessageQueue的构造// 我们在MessageQueue中说过这个参数的作用,true表示队列不能退出,false表示能退出mQueue = new MessageQueue(quitAllowed);// 初始化mThread变量为当前线程mThread = Thread.currentThread();}


3.初始化方法 prepare()

// 准备方法,调用带参数的prepare()方法public static void prepare() {// 参数为true,表示队列可以退出prepare(true);}// 带一个参数 quitAllowed 的prepare()方法private static void prepare(boolean quitAllowed) {// 先从sThreadLocal获取当前线程的Looper对象,如果获取到了,表示当前线程已经有一个Looper了// 抛出一个异常,表示在一个线程当中只能创建一个Looper对象if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}// 当前线程没有创建过Looper,那么就创建一个Looper并指定与Looper绑定的MessageQueue可以退出sThreadLocal.set(new Looper(quitAllowed));}// 这个方法是专门为主线程创建Looper的public static void prepareMainLooper() {// 同样调用带参数的prepare()方法创建Looper,但是参数为false,// 表示与Looper绑定的MessageQueue可以退出,也就是主线程的MessageQueue不能退出prepare(false);// 进入同步代码块synchronized (Looper.class) {// 判断成员变量 sMainLooper 是否为null,如果不为null,表示主线程已经创建过了,抛出异常if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}// sMainLooper 为null,调用myLooper()方法给 sMainLooper赋值sMainLooper = myLooper();}}


4.获取方法

// 从sThreadLocal中取出当前线程的Looper对象并返回public static @Nullable Looper myLooper() {return sThreadLocal.get();}// 获取与当前线程绑定的MessageQueue对象public static @NonNull MessageQueue myQueue() {return myLooper().mQueue;}// 获取当前Looper所在的线程public @NonNull Thread getThread() {return mThread;}


5.loop()方法,Looper类中最重要的一个方法

public static void loop() {// 获取与当前线程绑定的Looper对象final Looper me = myLooper();// 为null,表示当前线程没有Looper对象// 还不是Looper线程,抛出异常,没有调用Looper.prepare()方法if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}// 是Looper线程,初始化queue指向当前线程的MessageQueue对象final MessageQueue queue = me.mQueue;// 确保这个线程是运行在本地进程Binder.clearCallingIdentity();// 保存一个用于跟踪的身份令牌final long ident = Binder.clearCallingIdentity();// 进入无限循环for (;;) {// 调用MessageQueue的next()方法从消息队列中去消息// 有可能会阻塞,next()方法在上一篇博客中有详细说明Message msg = queue.next(); // might blockif (msg == null) {// 没有消息表示消息队列是退出状态,直接返回return;}// 如果调用了setMessageLogging(@Nullable Printer printer)方法// 那么就调用Printer接口中的方法打印日志信息final Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}final long traceTag = me.mTraceTag;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}try {// 调用Handler中的dispatchMessage(msg)方法,分发和处理消息// msg.target表示Message的目标Handler,前面的博客强调过这个变量msg.target.dispatchMessage(msg);} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}// 获取一个新的身份令牌,和原来的身份令牌进行比较final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) {// 如果两个身份令牌,打印一个错误级别很高的日志(What The Fuck)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.recycleUnchecked();}}
在《Android中的Handler机制分析(一) Handler和Message分析》这篇博客中,我们最后说到的了Handler中的dispatchMessage(msg)方法,当时只是说让大家记住,然后说了是在Looper中调用的,在这里就能看到了具体的调用位置及花时间。

6.quit()退出方法

// 调用MessageQueue中的方法,在上一篇博客中有说明public void quit() {// 参数为false,表示非安全退出mQueue.quit(false);}// 安全退出MessageQueue,参数为truepublic void quitSafely() {mQueue.quit(true);}

相关知识
1.主线程的Looper是在哪里初始化的

我们打开ActivityThread.java类,找到main()方法
public static void main(String[] args) {...// 调用Looper中的专门为主线程创建Looper对象的方法Looper.prepareMainLooper();...// 开始轮询,取消息Looper.loop();}


2.在子线程创建一个Handler并开启轮询

new Thread(new Runnable() {public void run() {Looper.prepare(); // 此处获取到当前线程的Looper,并且prepare()Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {Toast.makeText(getApplicationContext(), "handler msg", Toast.LENGTH_LONG).show();}};handler.sendEmptyMessage(1);Looper.loop(); // 开始轮询}}).start();new Thread(new Runnable() {public void run() {//Looper.prepare(); // 此处获取到当前线程的Looper,并且prepare()// 在子线程创建Handler,但是绑定到主线程的Looper中Handler handler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(Message msg) {Toast.makeText(getApplicationContext(), "handler msg", Toast.LENGTH_LONG).show();}};handler.sendEmptyMessage(1);//Looper.loop(); // 开始轮询}}).start();

Android Handler消息机制写到这里基本上写完了,最后在对Handler机制做一个简单的总结:

1.我们创建Handler对象并重写handlerMessage(Message msg)方法,然后在线程中使用Handler对象发送一个消息,在Handler类中实际上调用了MessageQueue中的方法将消息根据时间进行排序,然后唤醒Looper,让轮询器不断的获取消息队列中的消息(调用MessageQueue中的next()方法),取到消息之后就通过Message的目标Handler(也就是Message中的target变量)调用Handler的handlerMessage(Message msg)方法根据Message的what不同来分别处理消息;
2.如果在创建Handler时指定了一个callback,并且回调中重写的handlerMessage(Message msg)方法返回了true,那么,就不会再调用Handler中的handlerMessage(Message msg)方法了;
3.一个线程只能创建一个Looper对象,如果创建多个就会报错;
4.主线程的Looper是系统创建的,并且MessageQueue不能退出;
5.在子线程中我们需要使用Handler,那就要调用Looper.prepar()方法初始化Looper,要想执行Handler的handlerMessage(Message msg)方法,那还要调用Looper.loop()方法开启轮询,我们也可以在子线程中创建Handler并绑定到主线程的Looper上;
6.MessageQueue对象不能创建,它是在初始化Looper时就自动创建的,并和Looper绑定到一起。


0 0