一个线程有几个Handler?多个
一个线程有几个Looper?只有一个
如何保证只有一个?
在Looper使用前会调用Looper.prepare(),该函数源码中调用sThreadLocal.set(new Looper()),sThreadLocal为ThreadLocal<Looper>实例,每个Thread中都有一个ThreadLocalMap类型的threadLocals成员变量来保存数据,通过ThreadLocal类来进行维护。set时先获取当前线程,然后获取当前线程的threadLocals,最后把ThreadLocal<Looper>作为key,Looper作为value保存起来,保证一个线程只能创建一个Looper。
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));
}
//ThreadLocal
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
//获取ThreadLoaclMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
Handler内存泄漏的原因是什么?为什么其他的内部类没有说过这个问题?
内部类持有了外部类的引用:Activity销毁时,java虚拟机认为其被Handler持有,因此不会释放。
Handler插入消息时,可插入延时消息,并且Handler对象会被Message持有,Message放入消息队列MessageQueue后,会等待到执行时间执行完毕才会销毁。
Message持有Handler,Handler持有Activity。
//Handler类
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;//持有了Handler
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
主线程中解决方法:static+弱引用,代码来源:
public class MainActivity extends AppCompatActivity {
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new MyHandler(this);
start();
}
private void start() {
Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);
}
private static class MyHandler extends Handler {
private WeakReference<MainActivity> activityWeakReference;
public MyHandler(MainActivity activity) {
activityWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = activityWeakReference.get();
if (activity != null) {
if (msg.what == 1) {
// 处理
}
}
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);//移除mHandler的回调和发送的消息
}
子线程中解决:当线程处理完任务后不主动调用Looper.quit()或者Looper.quitSafely()释放Looper,该线程就不会结束,会造成内存泄漏。Looper.quit()与Looper.quitSafely()调用了消息队列MessageQueue.quit()函数,销毁所有队列中的Message,MessageQueue.next返回null,Looper结束,线程结束释放内存。主线程不允许调用这个两个方法,不许销毁,调了会报异常。
为什么其他的内部类没有说过这个问题:没有耗时操作,没有其余类持有内部类。
主线程为什么可以直接创建使用Handler?子线程需要做什么准备?
Handler想要运行必须有Looper在运行。在ActivityThread的main方法里面已经进行了Looper的初始化操作。所以子线程使用Handler,也需要初始并运行Looper。
public static void main(String[] args) {
...
//Looper准备
Looper.prepareMainLooper();
...
//looper运行loop
Looper.loop();
...
}
多个子线程用Handler向主线程发送消息,或者同一个线程多个Handler,发送处理消息,将消息插入到同一个MessageQueue中,如何保证内部线程安全?
MessageQueue的enqueueMessage()添加消息与next()取消息通过synchronized锁保证队列不混乱,保证安全,但是会造成Handler的延迟消息时间不完全准确。
我们使用Message时如何创建它?
new Message,直接创建一个Message对象,不推荐使用,频繁创建销毁造成内存抖动。
Message.obtain
public static Message obtain() {
// 使用同步保证线程安全,因为此方法可以在任意线程调用。
synchronized (sPoolSync) {
if (sPool != null) {
// 消息池头不为空,说明队列有内容,从中获取一条消息并返回。缓存的
Message m = sPool; // 消息池的Head元素
sPool = m.next; // 缓存池Head为下个元素(移除m)
m.next = null; // 断开链接
m.flags = 0; // 清除在用标记
sPoolSize--; // 池数量减1
return m; // 返回此消息
}
}
// 消息池头为空,说明队列没有内容,直接创建一条消息并返回。
return new Message();
}
void recycleUnchecked() {
// 将此消息标记为在用,并判断是否添加到消息池中。
// 清除所有属性
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
// 同步,保证线程安全。
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
// 小于最大值,则进行添加,把此Message添加到链表的头部。
next = sPool; // 此Message的next指向链表的Head
sPool = this; // 链表的Head为此Message
sPoolSize++; // 消息池数量加1
}
}
}
主线程中Looper死循环为什么不会导致应用卡死?
当用户启动应用时,Launcher--->application--->zygote--->虚拟器--->ActivityThread--->main函数--->初始化并运行Looper.loop,主线程相关的东西,如Activity,Service都跑在Loop中间,所有的生命周期都是以消息的方式传递存在。
loop()方法中for(;;)是一个无限循环,不停地轮询消息队列并取出消息,然后将消息分发出去。Android应用程序就是通过这个方法来达到及时响应用户操作。这个过程并不会导致ANR,ANR指应用程序在一定时间内没有得到响应或者响应时间太长。在主线程的MessageQueue没有消息时,便阻塞在MessageQueue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态。因为没有消息,即不需要响应程序,便不会出现程序无响应(ANR)现象。
总结:loop无限循环用于取出消息并将消息分发出去,没有消息时会阻塞在queue.next()里的nativePollOnce()方法里,并释放CPU资源进入休眠。Android的绝大部分操作都是通过Handler机制来完成的,如果没有消息,则不需要程序去响应,就不会有ANR。ANR一般是消息的处理过程中耗时太长导致没有及时响应用户操作。