文章目录

        • 1. 前因
        • 2. 工作原理
        • 3. ThreadLocal
        • 3. 消息队列MessageQueue
        • 4. Looper
        • 5. Handler
        • 6. 主线程的消息循环
        • 7. 使用示例
        • 8. 其他

1. 前因

Handler的主要作用是讲一个任务切换到某个指定的线程中工作,主要用于更新UI,因为Android规定访问UI只能在主线程中进行(Android的UI控件不是线程安全的,上锁机制会让UI的访问逻辑复杂,且锁机制会降低UI访问效率,所以最简单高效的方法就是用单线程模型来处理UI操作)。

2. 工作原理

Handler创建时会采用当前线程的Looper来构建内部的消息循环系统。

通过Handler的post方法将一个Runnable或通过send方法发一个消息到Looper中,post方法最终也是调到send,send方法调用MessageQueue的enqueueMessage方法将消息放入消息队列,然后Looper发现会进行处理,最终消息中的Runnable或者Handler的handleMessage会被调用,由于Looper是运行在创建Handler所在的线程中的,这样Handler中的业务逻辑就被切换到创建Handler的线程中去执行了。

3. ThreadLocal

ThreadLocal 是一个线程内部的数据存储类,通过它可以再指定的线程中存储数据,Looper、ActivityThread、AMS中都有用到。有 set 和 get 方法

使用场景分析:

1)对于Handler来说,它需要获取当前线程的looper,显然Looper的作用域就是线程并且不同的线程有不同的Looper,这时就可以使用ThreadLocal实现Looper在线程中的存取。

2)复杂逻辑下的对象传递,比如监听器的传递,采用ThreadLocal可以让监听器作为线程内的全局对象而存在。

3. 消息队列MessageQueue

是一个单链表

1)插入 enqueueMessage,单链表的插入

2)读取并删除 next,是一个无限循环的方法,如果消息队列中没有消息,next方法会一直阻塞在这里,当新消息到来时,next方法会返回这条消息并将其从单链表中移除

3)这里创建Message对象的时候,有三种方式:

Message msg1 = new Message();// 就是直接初始化一个Message对象Message msg2 = Message.obtain();Message msg3 = handler.obtainMessage();

2、3种方式一样,从整个Messge池中返回一个新的Message实例,Messge池为空才创建新的对象,能避免重复Message创建对象,源码:

/**     * Return a new Message instance from the global pool. Allows us to     * avoid allocating new objects in many cases.     */    public static Message obtain() {        synchronized (sPoolSync) {            if (sPool != null) {                Message m = sPool;                sPool = m.next;                m.next = null;                m.flags = 0; // clear in-use flag                sPoolSize--;                return m;            }        }        return new Message();    }

4. Looper

不停的从MessageQueue中查看是否有新消息,如果有立刻处理,否则阻塞在那里。

1)构造方法:创建一个MessageQueue;将当前线程的对象保存起来

private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }

2)创建Looper:Looper.prepare()为当前线程创建一个Looper,Looper.loop() 开启消息循环

new Thread("123") {        @Override        public void run() {            super.run();            Looper.prepare();            Handler handler = new Handler();            Looper.loop();        };    }.start();

3)Looper.getMainLooper() 获取主线程Looper

4)退出:quit 和 quitSafely

quit直接退出,quitSafely设定一个退出标记,等消息队列中已有的消息处理完毕后退出。

Looper退出后,Handler发送消息会失败,send返回false。

子线程如果手动创建Looper在所有事情完成后记得用quit终止循环,退出Looper后,这个线程也立即终止。

5)loop方法:死循环,唯一跳出循环的方式是MessageQueue的next方法返回null

Looper的quit方法调用,会调用MessageQueue的quit或quitSafely方法通知消息队列退出,此时next返回null

6)loop 方法调用MessageQueue的next方法获取新消息,而next是一个阻塞操作,当没有消息时,next会一直阻塞在那里,导致loop也一直阻塞在那里;如果MessageQueue的next返回了新消息,Looper就会处理这条消息:

msg.target.dispatchMessage(msg),这里的msg.target 是发送这条消息的Handler对象,这样Handler发送的消息最终又交给它的dispatchMessage来处理了,注意,Handler的dispatchMessage方法是在创建 Handler时所使用的Looper中执行的,这样就成功将代码逻辑切换到指定的线程中去执行了。

5. Handler

发送消息和接收

1)发送通过post或者send,post最终也是通过send,发送消息的过程就是向消息队列中插入一条消息,MessageQueue的next方法会将这条消息给Looper

2)Looper收到消息后开始处理,最终交由Handler

public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

首先检查 Message的callback是否为null,不为bukk就通过handleCallback 来处理消息,Message的callback 是一个 Runnable 对象,实际上就是Handler 的 post方法所传递的Runnable参数

private static void handleCallback(Message message) {        message.callback.run();    }

其次检查mCallback 是否为null,不为null就调用 mCallback 的 handleMessage 方法。

Callback 是一个接口,可以用来创建一个Handler的实例但不需要派生Handler的子类

Handler handler = new Handler(callback);

最后调用Handler 的 handleMessage 方法来处理消息。

3)Handler 还可以通过一个Looper来构造

public Handler(Looper looper) {        this(looper, null, false);}

6. 主线程的消息循环

Activity 的 主线程就是 ActivityThread,入口为main,main方法中系统通过 Looper.prepareMainLooper() 来创建主线程的 Looper 以及 MessageQueue,并通过 Looper.loop() 来开启主线程的消息循环

ActivityThread.H 是其Handler,定义了一组消息类型,包含了四大组件的启动和停止等过程

ActivityThread 通过 ApplicationThread 和 AMS 进行进程间通信,AMS 以进程间通信的方式完成 ActivityThread 的请求后会回调 ApplicationThread 中的Binder方法,然后 ApplicationThread 会向 H发送消息,H收到消息后会将 ApplicationThread 中的逻辑切换到 ActivityThread中去执行,即切换到主线程中去执行,这个过程就是主线程的消息循环模型

7. 使用示例

1)在主线程创建

    // 方法1 使用内部类方式创建Handler    class mHandler extends Handler {        // 复写handlerMessage()         @Override        public void handleMessage(Message msg) {         ...// 需执行的UI操作                    }    }           // 方法2 使用匿名内部类创建Handler    Handler handler = new Handler(){        // 复写handlerMessage()         @Override        public void handleMessage(Message msg) {            //msg就是子线程发送过来的消息。        }    };    //开启一个子线程 发消息方式1    new Thread(new Runnable() {            @Override            public void run() {                //在子线程发送一个消息。                Message msg = new Message();                handler.sendMessage(msg);            }        }).start();      //开启一个子线程 发消息方式2    new Thread(new Runnable() {            @Override            public void run() {                //在子线程post一个Runnable对象                handler.post(new Runnable() {                    @Override                    public void run() {                        //这里是消息处理的方法                        //这里运行在主线程。                    }                });            }        }).start();

注意:Handler的内存泄漏

as不建议我们用上述方式生成Handler,这是应为非静态内部类会持有外部内的引用。那么Handler将会持有Activity的引用,我们知道handler是会被msg.target持有的,而msg又在MessageQueue队列中,那么当消息队列中拥有未消费的Message时,会导致Activity即使finish了也无法被GC回收,最终导致内存泄漏。为了避免这个问题我们可以将Handler写成外部类或者静态的内部类,并且传递的Activity引用可以用WeakReference弱引用来持有,同时可以在Activity的onDestory中使用Handler.removeCallbacksAndMessages(null);来清空消息队列

2)在子线程创建方式1 使用子线程的Looper 消息处理在子线程

    // 声明Handler    Handler handler;    new Thread(new Runnable() {        @Override        public void run() {        // 创建当前线程的Looper            Looper.prepare();            // 在子线程创建handler对象            handler = new Handler() {                @Override                public void handleMessage(Message msg) {                // 这里是消息处理,它是运行在子线程的                }           };           // 开启Looper的消息轮询           Looper.loop();       }   }).start();   mBanner.setOnClickListener(new View.OnClickListener() {       @Override       public void onClick(View v) {        // 在主线程发送一个消息到子线程           Message msg = new Message();           handler.sendMessage(msg);       }   });

3)在子线程创建方式2 使用主线程的Looper 消息处理在主线程

    new Thread(new Runnable() {        @Override        public void run() {            // 获取主线程的Looper            Looper looper = Looper.getMainLooper();            // 用主线程的Looper创建Handler            handler = new Handler(looper) {                @Override                public void handleMessage(Message msg) {                // 这里是运行在主线程的                }            };        }    }).start();

4)使用 Callback接口

public class MyClass implements Handler.Callback {...private Handler handler;// 主线程的Looperhandler = new Handler(Looper.getMainLooper(), this);// 子线程的LooperHandlerThread handlerThread = new HandlerThread("subthread");    handlerThread.start();    handler = new Handler(handlerThread.getLooper(), this);    @Override    public boolean handleMessage(Message msg) {        ...    }}

8. 其他

**1)Android已经提供了很多实现了Handler的类和方法,方便我们使用。**如Activity类的runOnUiThread()方法,View的post()方法,HandlerThread类等,关于这些知识,有待补充

  • runOnUiThread
public final void runOnUiThread(Runnable action) {    if (Thread.currentThread() != mUiThread) {        mHandler.post(action);    } else {        action.run();    }}
  • postInvalidate底层是使用了Handler,向主线程发消息更新UI

  • HandlerThread 是Android API提供的一个方便、便捷的类,使用它我们可以快速的创建一个带有Looper的线程。Looper可以用来创建Handler实例

2)Android中为什么主线程不会因为Looper.loop()里的死循环卡死

3)一个线程只能有一个Looper

public static void prepare() {        prepare(true);    }    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));    }

本文参考:

《Android开发艺术探索》第十章

https://juejin.im/post/5910533dac502e006cfe01cd

更多相关文章

  1. Android多线程下载远程图片
  2. Android应用程序消息处理机制(Looper、Handler)分析(4)
  3. Android开发实践:自定义带消息循环(Looper)的工作线程
  4. Android中线程形态AsyncTask、HandlerThread 和 IntentService简
  5. Android多线程:理解和简单使用总结

随机推荐

  1. Android NFC架构分析
  2. android EditText设置文字选中
  3. Android 获取状态栏的高度
  4. Android(安卓)Animation总结
  5. linux基础教程--安装Android SDK
  6. android之View属性
  7. cocos2d-x3.0beta版+NDK-r9b在android上
  8. android点击邮箱链接跳转发送
  9. Android 深入解析selector
  10. Android ScrollView 判断到顶到底,和设置