背景:
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去實現同步屏障。