背景:
FD泄露通常会造成OOM、ANR甚至Crash。
Android作为Linux系统的一部份,在Linux中“一切都是文件”,从小到一块儿内存区域大到进程、Socket、线程、磁盘文件等都有自身的文件描述符。我们在保证Socket、文件句柄、IO操作不存在泄露的情况下,是否还有其他方面可以优化?
答案是肯定的,不然也不会写这么一篇文章了,那我们今天的主角表面上HandlerThread优化,实际上是通过共享HandlerThread减少FD的创建。
在Android系统中,创建一个HandlerThread通常会创建2个FD,一个是线程的FD,一个是MessageQueue的FD,前者是系统机制,后者是为了epoll队列监控使用。这些 eventfd 由 HandlerThread 创建,每个 HandlerThread 会创建 eventfd 和 epollfd 两个 fd,在 Android 5上甚至会创建三个FD,pipe_in、pipe_out 和 epollfd。
原理:
换种思路,让所有的Handler共享同一个HandlerThread,所有的消息都交给同一个Looper去监控,等到执行时间到的时候再转发到其他线程
public class LightHandler extends Handler implements Runnable {
static final HandlerThread sHandlerThread = new HandlerThread("LightHandlerThread");
static {
sHandlerThread.start();
}
private AbstractQueue<Message> queue = null;
private final Thread thread;
private volatile boolean isQuited = false;
public LightHandler(String threadName, Callback callback) {
super(sHandlerThread.getLooper(), callback);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
this.queue = new LinkedTransferQueue<>();
} else {
this.queue = new LinkedBlockingQueue<>();
}
this.thread = new Thread(Thread.currentThread().getThreadGroup(), this, threadName, 512);
this.thread.start();
}
public LightHandler(String threadName) {
this(threadName, null);
}
@Override
public void dispatchMessage(Message msg) {
Message message = Message.obtain(this);
message.copyFrom(msg);
queue.offer(message);
}
public void doHandleMessage(Message msg) {
super.dispatchMessage(msg);
}
@Override
public void run() {
while (!isQuited) {
Message msg = queue.poll();
if (msg == null) {
continue;
}
doHandleMessage(msg);
msg.recycle();
}
}
public boolean isQuited() {
return isQuited;
}
public void quit(){
isQuited = true;
}
}
评价
优点:
避免了创建更多FD
缺点:
这种依然是有缺点的,主要是同步屏障降对所有Handler开启,当然使用同步屏障的情况实际并不多,理论上也不会有人使用共享ThreadHandler去实现同步屏障。