1. 一个线程可以创建几个Handler?创建Looper的两种方式?
一个线程可以创建多个Handler。
一般是在主线程中实现一个Handler,然后在子线程中使用它。
class HandlerActivity: AppCompatActivity() {
private val mHandler = MyHandler()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 在子线程中通过自定义的 Handler 发消息
thread {
mHandler.sendEmptyMessageDelayed(1, 1000)
}
}
// 自定义一个 Handler
class MyHandler: Handler() {
override fun handleMessage(msg: Message) {
Log.i("HandlerActivity", "主线程:handleMessage: ${msg.what}")
}
}
}
或者有时候需要在子线程中创建运行在主线程中的Handler
class HandlerActivity: AppCompatActivity() {
private var mHandler: Handler? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
thread {
//获得main looper 运行在主线程
mHandler = MyHandler(Looper.getMainLooper())
mHandler!!.sendEmptyMessageDelayed(1, 1000)
}
}
// 自定义一个 Handler
class MyHandler(): Handler() {
override fun handleMessage(msg: Message) {
Log.i("HandlerActivity", "子线程:handleMessage: ${msg.what}")
}
}
}
2. 一个线程有几个looper,几个message,几个messageQueue怎么保证?Looper的工作流程。
一个线程只有一个looper,一个message。
looper在Handler初始化的时候获取looper:mLooper = Looper.myLooper();在这一句中Handler通过Looper.myLooper方法获取到了Looper对象,那我们看看这个Looper.myLooper()方法做了什么事情呢。它是如何返回一个Looper对象的呢?
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
ThreadLocal提供了线程的局部变量,每个线程可以通过set()和get()方法来对这个局部变量进行操作,但是不会和其他线程的局部变量产生冲突,实现了线程的数据隔离,ThreadLocal中填充的变量是属于当前线程的,该变量对于其他线程而言是隔离的。
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));
}
可以看得出,最后调用了是prepare(boolean quitAllowed)方法,而这个方法首先判断,如果sThreadLocal有值,就抛异常,没有值才会塞进去一个值。其实很好理解,就是说prepare方法必须调用但也只能调用一次,不调用没有值,抛异常,调用多次也还抛异常
接下来再看看这行sThreadLocal.set(new Looper(quitAllowed));做了什么吧,它是如何塞进去的呢?
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
set方法首先获取到了当前的线程,然后获取一个map。这个map是以键值对形式存储内容的。如果获取的map为空,就创建一个map。如果不为空就塞进去值。要注意的是,这里面的key是当前的线程,这里面的value就是Looper。也就是说,线程和Looper是一一对应的。也就是很多人说的Looper和线程绑定了,其实就是以键值对形式存进了一个map中。
3. handler如何延迟发送消息?
4. Looper.loop()方法在主线程死循环,为啥不会造成ANR?
具体的流程是:
1、mainThread中ActivityThread首先创建了一个运行在主线程的Looper,并且把它和主线程进行了绑定。
2、Looper又创建了一个MessageQueue,然后调用Looper.loop方法不断地在主线程中尝试取出Message
3、Looper如果取到了Message,那么就在主线程中调用发送这个Message的Handler的handleMessage方法。
4、我们在主线程或者子线程中通过Looper.getMainLooper为参数创建了一个Handler。
5、在子线程中发送了Message,主线程中的Looper不断循环,终于收到了Message,在主线程中调用了这个Handler的handleMessage方法。
Handler是构成整个Android系统的基础,正是Looper的死循环才让Android程序能够不退出。所有的类似于屏幕刷新,UI互动都是一种事件,通过Handler发送给了Looper来进行分发。整个Android程序可以说就是运行在这个死循环中。
5. Handler为什么会造成内存泄漏?如何避免造成内存泄漏?
内部类持有外部类的对象,handler持有activity的对象,当页面activity关闭时,handler还在发送消息,handler持有activity的对象,导致handler不能及时被回收,所以造成内存泄漏。
为啥其他内部类不会呢?
因为当handler发送消息时,会有耗时操作,并且会利用线程中的looper和messageQueue进行消息发送,looper和messageQueue的生命周期是很长的,和application一样,所以handler不容易被销毁,所以造成内存泄漏。
如何解决?
1.把handler生命成静态内部类,静态内部类不会持有activity,所以不会造成内存泄漏,
2.弱引用(WeakReference):把使用handle的activity设置成弱引用,
protected MyHandler handler = new MyHandler(this);
public abstract void handlerMessage1(Message msg);
public static class MyHandler extends Handler {
private WeakReference<BaseActivity> weakReference;
public MyHandler(BaseActivity activity) {
this.weakReference = new WeakReference<BaseActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
weakReference.get().handlerMessage1(msg);
}
}
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
3.页面销毁时,清空发送的消息:
handler.removeCallbacksAndMessages()
6. 子线程如何创建hander?主线程给子线程的Handler发送消息怎么写?
子线程如果要创建Handler,必须通过Looper.prepare()方法创建Looper,在主线程中ActivityThread已经帮我们创建好了,我们不需要自己去创建,但如果在子线程中创建Handler,要么使用Looper的mainLooper,要么自己调用Looper.prepare()方法创建属于这个线程的looper对象。如下是创建了一个子线程的Looper对象:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
//TODO 子线程处理消息
}
};
Looper.loop();
}
}
mHandler.sendMessage()//主线程发送消息
7. 为什么建议使用Message.obtain()来创建Message实例?
提高消息的复用
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();
}
可以看到,obtain方法是将一个Message对象的所有数据清空,然后添加到链表头中。sPool就是个消息池,默认的缓存是50个。
Looper在分发结束以后,会将用完的消息回收掉,并添加到回收池里。
8. 为什么子线程中不可以直接new Handler()而主线程中可以?
如果要创建Handler,必须通过Looper.prepare()方法创建Looper,在主线程中ActivityThread已经帮我们创建好了,我们不需要自己去创建,但如果在子线程中创建Handler,要么使用Looper的mainLooper,要么自己调用Looper.prepare()方法创建属于这个线程的looper对象。
9. 请描述MessageQueue的数据结构和工作流程。
按时间先后顺序排列的单链表,在Handler的构造方法中MessageQueue被赋值。最后发送消息都调用的是MessageQueue的queue.enqueueMessage(msg, uptimeMillis)方法。现在我们已经拿到了queue,然后在这个单链表中进行发消息。
// MessageQueue.java
//省略部分代码
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
//【1】拿到队列头部
Message p = mMessages;
boolean needWake;
//【2】如果消息不需要延时,或者消息的执行时间比头部消息早,插到队列头部
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//【3】消息插到队列中间
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
可以看到,消息队列是一个根据消息【执行时间先后】连接起来的单向链表。想要获取可执行的消息,只需要遍历这个列表,对比当前时间与消息的执行时间,就知道消息是否需要执行了。好了
网友评论