每天记录学习的新知识 : Handler API和原理

参考地址

参考转载地址:图解Handler机制
参考地址:Android Handler 机制(二):Handler 机制深入探究问题梳理

Handler

理解

Handler是什么?

Handler 是一套消息处理机制,是Android SDK来处理异步消息的核心类

Handler有什么作用?

Handler 用于子线程与主线程通信。子线程可以通过Handler来通知主线程进行UI更新

Handler如何使用?

参考地址:Android之Handler用法总结

API

Android Developers 地址:http://www.android-doc.com/reference/android/os/Handler.html

构造方法

构造方法 用途
Handler() 使得该handler与当前线程的Loop相关联
Handler(Handler.Callback callback) 使得该handler与当前线程的Loop相关联,且在回调接口中可以处理message
Handler(Looper looper) 使得该handler与自定义的Loop相关联
Handler(Looper looper,Handler.Callback callback) 使得该handler与自定义的Loop相关联,且在回调接口中可以处理message

公共方法

返回值 公共方法 用途
void dispatchMessage(Message msg) 处理系统消息
final void dump(Printer pw, String prefix) 打印出Handler和Looper对象的信息,包含Looper中的Message个数等
String getMessageName(Message message) 返回表示指定消息名称的字符串
void handleMessage(Message msg) 发送消息和接收消息
final boolean hasMessages(int what, Object object) 检查消息队列中是否有包含what属性且Object为指定值的消息
final boolean hasMessages(int what) 检查消息队列中是否有包含what属性为指定值的消息
final Message obtainMessage(int what, int arg1, int arg2) 从Handler获取Message
final Message obtainMessage() 从Handler获取Message
final Message obtainMessage(int what, int arg1, int arg2, Object obj) 从Handler获取Message
final Message obtainMessage(int what) 从Handler获取Message
final Message obtainMessage(int what, Object obj) 从Handler获取Message
final boolean post(Runnable r) 在Handler所在线程,创建一个封装Runnable的Message对象
final boolean postAtFrontOfQueue(Runnable r) 立即发送封装Runnable的Message到队列,而且是放在队列的最前面
final boolean postAtTime(Runnable r, Object token, long uptimeMillis) 在某个时间点执行封装Runnable的Message
final boolean postAtTime(Runnable r, long uptimeMillis) 在某个时间点执行封装Runnable的Message
final boolean postDelayed(Runnable r, long delayMillis) 当前时间延迟delayMillis个毫秒后执行封装Runnable的Message
final void removeCallbacks (Runnable r) 移除MessageQueue中的所有封装Runnable的Message对象
final void removeCallbacks(Runnable r, Object token)
final void removeCallbacksAndMessages(Object token) 当传入的参数为null时,则移除所有的callbacks和messages
final void removeMessages(int what) MessageQueue将Message.what与函数传入的what相同的Message从队列中移除
final void removeMessages(int what, Object object) MessageQueue将Message.what和Message.obj与函数传入的相同的Message从队列中移除
final boolean sendEmptyMessage(int what) 发送what的Message
final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) 指定时间,发送what的Message
final boolean sendEmptyMessageDelayed(int what, long delayMillis) 延时发送what的Message
final boolean sendMessage(Message msg) 发送Message
final boolean sendMessageAtFrontOfQueue(Message msg) 立即发送Message到队列,而且是放在队列的最前面
boolean sendMessageAtTime(Message msg, long uptimeMillis) 指定时间,发送Message
final boolean sendMessageDelayed(Message msg, long delayMillis) 延时,发送Message
String toString()

dump(Printer pw, String prefix)

参考:Android中Handler和Looper的dump方法以及Printer接口的使用

handleMessage(Message msg)

首先,这方法是回调:

     Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.e(TAG, "handler msg.what:" + msg.what);
            super.handleMessage(msg);
        }
    };

用于接收消息。

其次,这个方法也可用于发送消息:

        Message message2 = Message.obtain();
        message2.what = 102;
        handler.handleMessage(message2);

需要注意的是:当我们构造Handler传参为CallBack,我们使用handler.handleMessage()发送消息的时候,handler是没有处理的。只能使用sendMessage();的方式来发送。

obtainMessage()

public final Message obtainMessage () 
public final Message obtainMessage (int what)
public final Message obtainMessage (int what, Object obj)
public final Message obtainMessage (int what, int arg1, int arg2)
public final Message obtainMessage (int what, int arg1, int arg2, Object obj)

从Handler中获取(一定条件的)Message,复用。因为不需要重新new所以会节省内存,提高效率。

例:

Message msg = handler.obtainMessage();  
msg.arg1 = i;  
msg.sendToTarget();   
    
Message msg=new Message();  
msg.arg1=i;  
handler.sendMessage(msg);  

原理

组成部分

Message,Message Queue,Looper和ThreadLocal Handler 实现功能的组成部分

他们分别代表:

  1. Message:消息
  2. Hanlder:消息的发起者
  3. Looper:消息的遍历者
  4. MessageQueue:消息队列
  5. ThreadLocal :线程本地变量

对应:一个Looper ,一个MessageQueue,多个Hanlder,多个Message

Message

数据模型,用于传递消息。

参数有:

  1. target:消息回调后的作用域类,通常是一个handler
  2. what:是一个区分不同消息的标识符
  3. obj:这是obj是一个对象类型,可以携带自定义的类
  4. arg:int类型,携带的参数

MessageQueue

数据结构:消息队列,线程唯一

参考:https://blog.csdn.net/nmyangmo/article/details/82260616

Looper循环队列的核心MessageQueue.next()
MessageQueue中无消息时,queue.next()会阻塞在nativePollOnce()nativePollOnce()被阻塞时,主线程会释放CPU资源,进入休眠状态. 直到下个消息到达或者有事务发生,会通过pipe管道写入数据来唤醒主线程工作,就可以继续工作了。

Looper

以下三个方法的分析转载自:图解Handler机制

Looper.prepare()
  1. 规定了一个线程只有一个Looper,也就是一个线程只能调用一次Looper.prepare()
  2. 如果当前线程没有Looper,那么就创建一个,存到sThreadLocal
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        // 规定了一个线程只有一个Looper,也就是一个线程只能调用一次Looper.prepare()
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 如果当前线程没有Looper,那么就创建一个,存到sThreadLocal中
        sThreadLocal.set(new Looper(quitAllowed));
    }
Looper的初始化
  1. 创建了MessageQueue,并供Looper持有
  2. Looper持有当前线程对象
    private Looper(boolean quitAllowed) {
        // 创建了MessageQueue,并供Looper持有
        mQueue = new MessageQueue(quitAllowed);
        // 让Looper持有当前线程对象
        mThread = Thread.currentThread();
    }
Looper.loop()
  1. 获取当前Looper,通过final Looper me = myLooper();
  2. 获得消息队列,通过 final MessageQueue queue = me.mQueue;
  3. 创建死循环
  4. 在循环内获取下一个消息,Message msg = queue.next();
  5. 如果存在消息,就分发,msg.target.dispatchMessage(msg);
  6. 执行完毕,复用消息, msg.recycleUnchecked();
   public static void loop() {
        // 得到当前线程的Looper对象
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        // 得到当前线程的MessageQueue对象
        final MessageQueue queue = me.mQueue;
        
        // 无关代码
        ......
        
        // 死循环
        for (;;) {
            // 不断从当前线程的MessageQueue中取出Message,当MessageQueue没有元素时,方法阻塞
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            // Message.target是Handler,其实就是发送消息的Handler,这里就是调用它的dispatchMessage方法
            msg.target.dispatchMessage(msg);
            // 回收Message
            msg.recycleUnchecked();
        }
    }

ThreadLocal

Handler 使用原理

主线程使用Handler原理

启动App时,App的入口,是ActivityThread.main方法:

   public static void main(String[] args) {
       // 不相干代码
       ......
       // 1.调用Looper.prepareMainLooper,其实也就是调用的Looper.prepare,初始化Looper、MessageQueue等
       Looper.prepareMainLooper();
       // 2.创建ActivityThread的同时,初始化了成员变量Handler mH
       ActivityThread thread = new ActivityThread();
       thread.attach(false);
       // 
       if (sMainThreadHandler == null) {
           // 把创建的Handler mH赋值给sMainThreadHandler
           sMainThreadHandler = thread.getHandler();
       }

       if (false) {
           Looper.myLooper().setMessageLogging(new
                   LogPrinter(Log.DEBUG, "ActivityThread"));
       }
       // 3.调用Looper.loop()方法,开启死循环,从MessageQueue中不断取出Message来处理
       Looper.loop();

       throw new RuntimeException("Main thread loop unexpectedly exited");
   }

可以看到,main方法做了三步:

  1. 调用Looper.prepareMainLooper,其实也就是调用的Looper.prepare,初始化LooperMessageQueue
  2. 创建ActivityThread的同时,初始化了成员变量Handler mH
  3. 调用Looper.loop()方法,开启死循环,从MessageQueue中不断取出Message来处理

第二步,在初始化Handler 过程中:
Handler 对象会得到当前线程的Looper对象,并判断否为空,让创建的Handler 对象持有LooperMessageQueueCallback的引用。

线程使用Handler原理

在线程中使用Handler ,需要自己创建Looper,并调用Looper.prepareLooper.loop()两个方法哦,这就是区别。

更多的原理性问题

1.Looper 死循环为什么不会导致应用卡死,会消耗大量资源吗?

参考:Handler后传篇一: 为什么Looper中的Loop()方法不能导致主线程卡死?

首先ANR致使应用卡死的原因是:

  1. 输入超时:5秒(最常见)
  2. 服务超时:前台服务20s、后台服务200s。
  3. 广播队列超时:前台广播10秒、后台广播60秒。
  4. ContentProvider超时:10秒

所以,looper死循环如果导致崩溃,那么将是堵塞主线程5S导致。

那么Looper循环内的消息堵塞,会堵塞主线程吗?

不会。
虽然因为Looper循环内采用了epoll机制,做到了消息队列的阻塞和唤醒,CPU在消息阻塞下会进入休眠状态,但是休眠和死循环完全不是一个东西,CPU的休眠不会阻碍分发。

循环本身会导致ANR吗?

不会。
只有消息数量达到无限循环的量级,消息才不会被分发完,据我们所知消息数量不会达到无限的量级。

循环的分发事件内可以处理耗时吗?

我们知道分发完的事件会在主线程处理哦,耗时5S处理不完,会导致ANR

会消耗大量资源吗?

不会的,消息阻塞期间,CPU会进入休眠

2.引用 Handler课后题

包含如下问题:

  1. Android中,有哪些是基于Handler来实现通信的?
  2. 处理Handler消息,是在哪个线程?一定是创建Handler的线程么?
  3. 消息是如何插入到MessageQueue中的?
  4. 当MessageQueue没有消息时,它的next方法是阻塞的,会导致App ANR么?
  5. 子线程中可以使用Toast么?
  6. Looper.loop()是死循环,可以停止么?
  7. Handler内存泄露怎么解决?

3.内存泄露

内存泄露的原因:线程占用mH,mH占用Activity,致使Activity退出内存泄露

危害:OOM;多次OOM后程序占用内存超过系统限制,FC

解决办法:
1. Handler修改成静态内部类
2. 逻辑保护

你可能感兴趣的:(#,Android,基础知识,通讯,handler学习)