一、介绍
Handler,作为一个在主线程存活的消息分发工具,在App开发过程使用频率很高,也是面试问的比较多的。
面试常见的比如:子线程如何创建?Handler的机制是什么?内存抖动等,接下来我们会针对Handler的使用进行总结与指导
二、使用
1.在主线程的使用
在主线程的使用,是Handler比较常见的一种写法,如下:
var handler=Handler()这样就创建完了
但是,有人会说我们在使用过程中与弱引用搭配起来。
public abstract class WeakHanlder<T> extends Handler {
private WeakReference<T> weakReference;
public WeakHanlder(T activity) {
weakReference = new WeakReference<T>(activity);
}
@Override
public final void handleMessage(Message msg) {
if (weakReference.get() == null) {
handleMessageWhenNotServive(msg);
} else {
if (weakReference.get() instanceof Fragment) {
Fragment fragment = (Fragment) weakReference.get();
if (fragment.getActivity() == null) {
handleMessageWhenNotServive(msg);
} else {
handleMessageWhenServive(msg, weakReference.get());
}
} else {
handleMessageWhenServive(msg, weakReference.get());
}
}
}
//当引用对象存在(未被GC回收)时,调用此方法
public abstract void handleMessageWhenServive(Message msg, T host);
//当引用对象不存在(已被GC回收)时,调用此方法,非必须重写
public void handleMessageWhenNotServive(Message msg) {
}
public WeakReference<T> getWeakReference() {
return weakReference;
}
}
若引用,可以很好的规避当前target的对象被回收,handler的消息还没有被消费完毕,会引起OOM。
1.2、内存抖动
内存抖动产生的直接原因对象不行的创建与消费,这种现象可以通过AndroidStudio的profiler的工具可以直接看到
如果不停的释放和创建,波普图想我们见到的心电图一样,上下来回波浪线,形成的是抖动状态。
这样是什么原因导致的?
Handler在发送消息的时候,我们不应一直通过new Message来创建,
应该通过handler.obtain(),这样是复用内存中的,在Message中,有一个对象是mPool,这就是当前Message线程池,用到了就取一个,不用了就释放了。
var handler=Handler()
var msg= handler.obtainMessage()
msg.what=0;
handler.sendMessage(msg)
如果我们只发送一个通知,可以直接通过发送一个空消息
handler.sendEmptyMessage(1)
1.3、OOM
handler被问频率比较高的莫过于oom,oom原因大家应该都很清楚。这个和GC回收有关,GC回收分为两种情况,一种是GC还有就是GCRoot。GCRoot,就是我们常定义的static变量。
如果我们不手动回收static对象,GCRoot是不会释放,所以还有一种就是定义成静态变量使用
companion object{ var handler=Handler() }
2、子线程创建Handler
有些小伙伴听到子线程创建Handler也许是第一次,因为Handler是主线程,常用的就是处理子线程的UI更新操作,子线程创建还是头一次挺多。这个会涉及到多线程的问题
子线程创建Handler:
子线程创建
public class TestThread extends Thread implements Runnable {
private Looper looper;
@Override
public void run() {
Looper.prepare();
looper = Looper.myLooper();
Looper.loop();
}
public Looper getLooper() {
return looper;
}
}
调用:
var thread=TestThread()
thread.start()
var handler = Handler(thread.looper,object :Handler.Callback{
override fun handleMessage(p0: Message): Boolean {
showToast("TestThread")
return false
}
})
handler.sendEmptyMessage(1)
这时,已完成子线程的创建,但是这里有个问题。当我们调用子线程start()后,线程开始执行run()函数,同时,我们也调用了getLooper()来获取,当cpu时间没分配到这个线程时,我们获取的looper是空。这里,明显有一个雷区。
这里涉及到多线程的问题:
处理可以通过wait和notify来完成,有人会问,为什么不sleep?sleep会导致线程阻塞,wait是将cpu的时间转让出去。针对这个我们可以自己设计一个等待和通知的多线程。
有人会提到用锁来完成,大家可以试一下,这边也可以提示一下公平锁,ReentrantLock。
这个问题在Android体系中已给出了解决线程HandlerThread
借助HandlerThread:
在run()执行时,通过synchronized对当前对象加了锁
这样完成一个多线程的机制。当轮询到looper为空,进入等待状态,当接收到notify(),释放。
同时,也支持退出消息队列
这样我们已完成了子线程创建Handler。
附上HandlerThread源码地址:HandlerThread.java - OpenGrok cross reference for /frameworks/base/core/java/android/os/HandlerThread.java