Android 面试(五):探索 Android 的 Handler(1)

  • MessageQueue
    MessageQueue 如其名,消息队列。它按时序将消息插入队列,最小的时间戳将被优先处理。

  • Looper
    Looper 负责从消息队列读取消息,然后分发给对应的 Handler 进行处理。它是一个死循环,不断地调用 MessageQueue.next() 去读取消息,在没有消息分发的时候会变成阻塞状态,在有消息可用时继续轮询。

在 Android 开发中使用 Handler 有什么需要注意的

首先自然是在工作线程中创建自己的消息队列必须要调用 Looper.prepare(),并且在一个线程中只能调用一次。当然,仅仅创建了 Looper 还不行,还必须使用 Looper.loop() 开启消息循环,要不然要 Looper 也没用。

我们平时在开发中不用调用是因为默认会调用主线程的 Looper。
此外,一个线程中只能有一个 Looper 对象和一个 MessageQueue 对象。

大概的标准写法是这样。

Looper.prepare();
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i(TAG, “在子线程中定义Handler,并接收到消息。。。”);
}
};
Looper.loop();

另外一个比较常考察的就是 Handler 可能引起的内存泄漏了。

Handler 可能引起的内存泄漏

我们经常会写这样的代码。

private final Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};

当你这样写的时候,你一定会收到编译器的黄色警告。

In Android, Handler classes should be static or leaks might occur, Messages enqueued on the application thread’s MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well. To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class

在 Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,而静态的内部类不会持有外部类的引用。

要解决这样的问题,我们在继承 Handler 的时候,要么是放在单独的类文件中,要么直接使用静态内部类。当需要在静态内部类中调用外部的 Activity 的时候,我们可以直接采用弱引用进行处理,所以我们大概修改后的代码如下。

private static final class MyHandler extends Handler{
private final WeakReference mWeakReference;

public MyHandler(MainActivity activity){
mWeakReference = new WeakReference<>(activity);
}

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity activity = mWeakReference.get();
if (activity != null){
// 开始写业务代码
}
}
}

private MyHandler mMyHandler = new MyHandler(this);

其实在我们实际开发中,不止一个地方可能会用到内部类,我们都需要在这样的情况下尽量使用静态内部类加弱引用的方式解决我们可能出现的内存泄漏问题。

用过 HandlerThread 吗?它是干嘛的?

HandlerThread 是 Android API 提供的一个便捷的类,使用它可以让我们快速地创建一个带有 Looper 的线程,有了 Looper 这个线程,我们又可以生成 Handler,本质上也就是一个建立了内部 Looper 的普通 Thread

我们在上面提到的子线程建立 Handler 大概代码是这样。

new Thread(new Runnable() {
@Override public void run() {
// 准备一个 Looper,Looper 创建时对应的 MessageQueue 也会被创建
Looper.prepare();
// 创建 Handler 并 post 一个 Message 到 MessageQueue
new Handler().post(new Runnable() {
@Override
public void run() {
MLog.i("Handler in " + Thread.currentThread().getName());
}
});
// Looper 开始不断的从 MessageQueue 取出消息并再次交给 Handler 执行
// 此时 Lopper 进入到一个无限循环中,后面的代码都不会被执行
Looper.loop();
}
}).start();

而采用 HandlerThread 可以直接把步骤简化为这样:

// 1. 创建 HandlerThread 并准备 Looper
handlerThread = new HandlerThread(“myHandlerThread”);
handlerThread.start();

// 2. 创建 Handler 并绑定 handlerThread 的 Looper
new Handler(handlerThread.getLooper()).post(new Runnable() {
@Override
public void run() {
// 注意:Handler 绑定了子线程的 Looper,这个方法也会运行在子线程,不可以更新 UI
MLog.i("Handler in " + Thread.currentThread().getName());
}
});

// 3. 退出
@Override public void onDestroy() {
super.onDestroy();
handlerThread.quit();

尾声

你不踏出去一步,永远不知道自己潜力有多大,千万别被这个社会套在我们身上的枷锁给捆住了,30岁我不怕,35岁我一样不怕,去做自己想做的事,为自己拼一把吧!不试试怎么知道你不行呢?

改变人生,没有什么捷径可言,这条路需要自己亲自去走一走,只有深入思考,不断反思总结,保持学习的热情,一步一步构建自己完整的知识体系,才是最终的制胜之道,也是程序员应该承担的使命。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
存中…(img-DBVbT5tq-1714456913696)]

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 27
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值