Handler 都没搞懂,拿什么去跳槽啊,Android开发社招面试总结

    // 不断从 MessageQueue 获取 消息
    Message msg = queue.next(); // might block
    //退出 Looper 
    if (msg == null) {
        // No message indicates that the message queue is quitting.
        return;
    }
    //...
    try {
        msg.target.dispatchMessage(msg);
        end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
    } finally {
        //...
    }
    //...
			//回收 message, 见【3.5】
    msg.recycleUnchecked();
}

}


`loop()` 里调用了 `MessageQueue.next()` :

//MessageQueue
Message next() {
//…
for (;😉 {
//…
nativePollOnce(ptr, nextPollTimeoutMillis);

    synchronized (this) {
        // Try to retrieve the next message.  Return if found.
        final long now = SystemClock.uptimeMillis();
        Message prevMsg = null;
        Message msg = mMessages;
        //...
        if (msg != null) {
            if (now < msg.when) {
                // Next message is not ready.  Set a timeout to wake up when it is ready.
                nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
            } else {
                // Got a message.
                mBlocked = false;
                if (prevMsg != null) {
                    prevMsg.next = msg.next;
                } else {
                    mMessages = msg.next;
                }
                msg.next = null;
                return msg;
            }
        } else {
            // No more messages.
            nextPollTimeoutMillis = -1;
        }

        // Process the quit message now that all pending messages have been handled.
        if (mQuitting) {
            dispose();
            return null;
        }
    }

    // Run the idle handlers. 关于 IdleHandler 自行了解
    //...
}

}


还调用了 `msg.target.dispatchMessage(msg)` ,msg.target 就是发送该消息的 Handler,这样就回调到了 Handler 那边去了:

//Handler
public void dispatchMessage(Message msg) {
//msg.callback 是 Runnable ,如果是 post方法则会走这个 if
if (msg.callback != null) {
handleCallback(msg);
} else {
//callback 见【3.4】
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//回调到 Handler 的 handleMessage 方法
handleMessage(msg);
}
}


**注意:dispatchMessage() 方法针对 Runnable 的方法做了特殊处理,如果是 ,则会直接执行 `Runnable.run()` 。**

**分析:**Looper.loop() 是个死循环,会**不断调用 MessageQueue.next() 获取 Message ,并调用 `msg.target.dispatchMessage(msg)` 回到了 Handler 来分发消息,以此来完成消息的回调**。

**注意:loop()方法并不会卡死主线程,见【6】。**

那么**线程的切换又是怎么回事**呢?  
很多人搞不懂这个原理,但是其实非常简单,我们将所涉及的方法调用栈画出来,如下:

Thread.foo(){
Looper.loop()
-> MessageQueue.next()
-> Message.target.dispatchMessage()
-> Handler.handleMessage()
}


**显而易见,Handler.handleMessage() 所在的线程最终由调用 Looper.loop() 的线程所决定。**

平时我们用的时候从异步线程发送消息到 Handler,这个 Handler 的 `handleMessage()` 方法是在主线程调用的,所以消息就从异步线程切换到了主线程。

[]( )

### 2.3 图解原理

文字版的原理解析到这里就结束了,如果你看到这里还是没有懂,没关系,我特意给你们准备了些图,配合着前面几个章节,再多看几遍,一定可以吃透。

![handler-looper-mq.jpg](https://user-gold-cdn.xitu.io/2019/2/26/16927e6098cf257b?imageView2/0/w/1280/h/960/ignore-error/1)

![handler_java.jpg](https://user-gold-cdn.xitu.io/2019/2/26/16927e6099e1d48c?imageView2/0/w/1280/h/960/ignore-error/1)

  
图片来源见【6】

[]( )

### 2.4 小结

Handler 的背后有着 Looper 以及 MessageQueue 的协助,三者通力合作,分工明确。

尝试小结一下它们的职责,如下:

*   Looper :**负责关联线程以及消息的分发**在该线程下\*\*从 MessageQueue 获取 Message,分发给 Handler ;
*   MessageQueue :**是个队列,负责消息的存储与管理**,负责管理由 Handler 发送过来的 Message ;
*   Handler : **负责发送并处理消息**,面向开发者,提供 API,并隐藏背后实现的细节。

对【2】章节提出的问题用一句话总结:

**Handler 发送的消息由 MessageQueue 存储管理,并由 Loopler 负责回调消息到 handleMessage()。**

**线程的转换由 Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决定。**

[]( )

3\. Handler 的延伸
---------------

Handler 虽然简单易用,但是要用好它还是需要注意一点,另外 Handler相关 还有些鲜为人知的知识技巧,比如 IdleHandler。

由于 Handler 的特性,它在 Android 里的应用非常广泛,比如: AsyncTask、HandlerThread、Messenger、IdleHandler 和 IntentService 等等。

这些我会讲解一些,我没讲到的可以自行搜索相关内容进行了解。

[]( )

### 3.1 Handler 引起的内存泄露原因以及最佳解决方案

Handler 允许我们发送**延时消息**,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露。

这个泄露是因为 Message 会持有 Handler,而又因为 **Java 的特性,内部类会持有外部类**,使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄露。

解决该问题的最有效的方法是:**将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除所有消息**。

示例代码如下:

private static class SafeHandler extends Handler {

private WeakReference<HandlerActivity> ref;

public SafeHandler(HandlerActivity activity) {
    this.ref = new WeakReference(activity);
}

@Override
public void handleMessage(final Message msg) {
    HandlerActivity activity = ref.get();
    if (activity != null) {
        activity.handleMessage(msg);
    }
}

}


并且再在 `Activity.onDestroy()` 前移除消息,加一层保障:

@Override
protected void onDestroy() {
safeHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}


这样双重保障,就能完全避免内存泄露了。

**注意:单纯的在 `onDestroy` 移除消息并不保险,因为 `onDestroy` 并不一定执行。**

[]( )

### 3.2 为什么我们能在主线程直接使用 Handler,而不需要创建 Looper ?

前面我们提到了每个Handler 的线程都有一个 Looper ,主线程当然也不例外,但是我们不曾准备过主线程的 Looper 而可以直接使用,这是为何?

**注意:通常我们认为 ActivityThread 就是主线程。事实上它并不是一个线程,而是主线程操作的管理者,所以吧,我觉得把 ActivityThread 认为就是主线程无可厚非,另外主线程也可以说成 UI 线程。**

在 ActivityThread.main() 方法中有如下代码:

//android.app.ActivityThread
public static void main(String[] args) {
//…
Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//…
Looper.loop();

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


Looper.prepareMainLooper(); 代码如下:

/**

  • Initialize the current thread as a looper, marking it as an
  • application’s main looper. The main looper for your application
  • is created by the Android environment, so you should never need
  • to call this function yourself. See also: {@link #prepare()}
    */
    public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
    if (sMainLooper != null) {
    throw new IllegalStateException(“The main Looper has already been prepared.”);
    }
    sMainLooper = myLooper();
    }
    }

可以看到**在 ActivityThread 里 调用了 Looper.prepareMainLooper() 方法创建了 主线程的 Looper ,并且调用了 loop() 方法**,所以我们就可以直接使用 Handler 了。

**注意:`Looper.loop()` 是个死循环,后面的代码正常情况不会执行。**

[]( )

### 3.3 主线程的 Looper 不允许退出

如果你尝试退出 Looper ,你会得到以下错误信息:

Caused by: java.lang.IllegalStateException: Main thread not allowed to quit.
at android.os.MessageQueue.quit(MessageQueue.java:415)
at android.os.Looper.quit(Looper.java:240)


why? 其实原因很简单,**主线程不允许退出**,退出就意味 APP 要挂。

[]( )

### 3.4 Handler 里藏着的 Callback 能干什么?

在 Handler 的构造方法中有几个 要求传入 Callback ,那它是什么,又能做什么呢?

来看看 `Handler.dispatchMessage(msg)`  方法:

public void dispatchMessage(Message msg) {
//这里的 callback 是 Runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果 callback 处理了该 msg 并且返回 true, 就不会再回调 handleMessage
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}


可以看到 Handler.Callback 有**优先处理消息的权利** ,当一条消息被 Callback 处理**并拦截(返回 true)**,那么 Handler 的 `handleMessage(msg)` 方法就不会被调用了;如果 Callback 处理了消息,但是并没有拦截,那么就意味着**一个消息可以同时被 Callback 以及 Handler 处理**。

这个就很有意思了,这有什么作用呢?

**我们可以利用 Callback 这个拦截机制来拦截 Handler 的消息!**

场景:Hook [ActivityThread.mH]( ) , 在 ActivityThread 中有个成员变量 `mH` ,它是个 Handler,又是个极其重要的类,几乎所有的插件化框架都使用了这个方法。

[]( )

### 3.5 创建 Message 实例的最佳方式

由于 Handler 极为常用,所以为了节省开销,Android 给 Message 设计了回收机制,所以我们在使用的时候尽量复用 Message ,减少内存消耗。

方法有二:

1.  通过 Message 的静态方法 `Message.obtain();`   获取;
2.  通过 Handler 的公有方法 `handler.obtainMessage();` 。

[]( )

### 3.6 子线程里弹 Toast 的正确姿势

当我们尝试在子线程里直接去弹 Toast 的时候,会 crash :

java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()


**本质上是因为 Toast 的实现依赖于 Handler**,按子线程使用 Handler 的要求修改即可(见【2.1】),同理的还有 Dialog。

正确示例代码如下:

new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(HandlerActivity.this, “不会崩溃啦!”, Toast.LENGTH_SHORT).show();
Looper.loop();
}
}).start();


[]( )

### 3.7 妙用 Looper 机制

### 最后

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

![](https://img-blog.csdnimg.cn/img_convert/70ff660e4365f6f30ceaf5e2e4a0aadc.png)

> 最后针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

##### **[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**

改进的,请给我留言。一定会认真查询,修正不足。谢谢。

[外链图片转存中...(img-paGJvvDf-1630930409680)]

> 最后针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

##### **[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值