一耕渴、handler的用法
1.1钧栖、用于線程切換
Handler handler = new Handler(Looper.getMainLooper());
new Thread(new Runnable() {
@Override
public void run() {
handler.post(() -> {
binding.tv.setText("hello world");
});
}
}).start();
1.2、線程間通信
為了防止handler導(dǎo)致內(nèi)存泄露副硅,handler對(duì)Activity采用弱引用,因弱引用不會(huì)影響Activity對(duì)象生命周期翅萤。
static class MyHandler extends Handler {
WeakReference<MainActivity> mActivityWeakReference;恐疲、
public MyHandler(MainActivity activity) {
mActivityWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
MainActivity activity = mActivityWeakReference.get();
if (activity != null && msg!=null && msg.obj!=null) {
//todo
}
}
}
MyHandler myHandler = new MyHandler(MainActivity.this);
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.obj = "aaa";
mMyHandler.sendMessage(message);
}
}).start();
1.3、任務(wù)延時(shí)執(zhí)行
static class MyRunnalbe implements Runnable {
@Override
public void run() {
Log.e("test", "test memory leak?");
}
}
//發(fā)送延時(shí)任務(wù)
Handler handler3 = new Handler(Looper.getMainLooper());
handler3.postDelayed(new MyRunnalbe(), 10000);
最后大家不要忘了套么,一定記得在Activity的onDestroy及時(shí)解除Message對(duì)Handler的強(qiáng)引用培己。
@Override
protected void onDestroy() {
super.onDestroy();
mMyHandler.removeCallbacksAndMessages(null);
}
二、Handler(Loope(MessageQueue))的創(chuàng)建
2.1胚泌、Handler創(chuàng)建
先看看handler的構(gòu)造函數(shù)省咨。構(gòu)造函數(shù)肯定是相互調(diào)用,只需要看最多的參數(shù)的那個(gè)
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) { xxxx }
從構(gòu)造可以看出玷室,Handler的創(chuàng)建需要Looper零蓉,沒(méi)有Looper玩不轉(zhuǎn)吶,我們之前寫handler用法的時(shí)候穷缤,都是在主線程創(chuàng)建Handler敌蜂,它構(gòu)造里面會(huì)調(diào)用Looper.myLoop()獲取Looper。
ThreadLocal可用于線程間的數(shù)據(jù)隔離津肛,這是解決多線程并發(fā)的一種手段 (不共享可變資源)紊册。將線程需要的Looper保存在ThreadLocal里面。
我們App的入口函數(shù)就是ActivityThread的main方法,一般在main方法里面就做兩件事快耿,第一件就是啟動(dòng)創(chuàng)建主線程的Looper囊陡,啟動(dòng)Looper輪詢;第二件事就是將ApplicationThread的binder對(duì)象注冊(cè)給AMS(AMS拿到它后就可以調(diào)用App的方法)掀亥。loop輪詢一旦跳出了死循環(huán)撞反,App直接就拋異常了。
如果最后一個(gè)參數(shù)async為true搪花,那么通過(guò)handler.sendMessage的message都會(huì)打上異步消息的標(biāo)簽遏片。
//from Handler
public Handler(@Nullable Callback callback, boolean async) {
.....
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
.......
}
//from Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//myLooper直接從Threadlocal中取
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
//創(chuàng)建主線程Looper嘹害,因?yàn)橹骶€程Looper不支持退出
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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));
}
//from ActivityThread
public static void main(String[] args) {
.......
//創(chuàng)建主線程Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
//向AMS注冊(cè),將app的binder對(duì)象給AMS
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
.....
//啟動(dòng)輪詢
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Q: 如果直接在子線程里面new Handler()創(chuàng)建吮便,從ThreadLocals里面肯定是取不到對(duì)應(yīng)的looper的笔呀,所以會(huì)拋異常。
需要顯式調(diào)用Looper.prepare()才行髓需。
子線程中彈吐司也要先通過(guò)Looper.prepare()創(chuàng)建Looper许师,不然也會(huì)奔潰。
原因同上??
2.1.1僚匆、ThreadLocal原理
都說(shuō)ThreadLocal可用于線程間數(shù)據(jù)隔離微渠,完美的解決并發(fā)問(wèn)題,那啥原理呢咧擂? 先看看用法逞盆。public void set(T value) {
Thread t = Thread.currentThread(); //獲取當(dāng)前線程
ThreadLocalMap map = t.threadLocals; //線程的t.threadLocals
if (map != null)
map.set(this, value); //直接添加
else
createMap(t, value); //那就給當(dāng)前線程創(chuàng)建這個(gè)ThreadLocalMap類型的t.threadLocals成員
}
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1); //斐波那契散列算法
for (Entry e = tab[i]; e != null;e = tab[i = nextIndex(i, len)]) { //開放尋址法解決沖突
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value); //key就是ThreadLocal
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash();
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
threadlocal.set(xxx)時(shí),會(huì)從當(dāng)前線程(主線程/子線程)取出它的ThreadLocalMap類型的成員threadLocals松申,不為null就直接添加了云芦,為空則先創(chuàng)建。ThreadLocalMap底層采用數(shù)組實(shí)現(xiàn)贸桶,不像HashMap采用數(shù)組+鏈表/紅黑樹實(shí)現(xiàn)舅逸。這里的元素的index采用斐波那契散列算法實(shí)現(xiàn),采用開放尋址法解決散列沖突刨啸,具體參考ThreadLocalMap的hash算法。數(shù)組元素Entry很有特點(diǎn)识脆。
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry是一個(gè)弱引用類设联,直接通過(guò)entry.get()可以獲取到Threadlocal引用,因其作為entry的key灼捂,只能對(duì)應(yīng)一個(gè)值value离例。同時(shí)也參與table數(shù)組index的hash算法(i = key.threadLocalHashCode & (len-1))。如通過(guò)entry.get()獲取threadLocal為null時(shí)悉稠,此時(shí)value不為null宫蛆,就有發(fā)生泄漏可能,一般建議就是用完及時(shí)進(jìn)行remove掉的猛。
2.2耀盗、Looper的創(chuàng)建
public static void prepare() {
prepare(true);
}
public static void prepare() {
prepare(true);
}
//quitAllowed 是否支持退出,默認(rèn)是支持退出的卦尊。但是主線程的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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
Handler創(chuàng)建需要Looper,Looper創(chuàng)建需要MessageQueue岂却。如果多次調(diào)用Loopr.prepare來(lái)創(chuàng)建Looper會(huì)報(bào)錯(cuò)忿薇,即在一個(gè)線程里面創(chuàng)建多個(gè)Looper會(huì)報(bào)錯(cuò)裙椭。
主線程的Looper當(dāng)然不支持退出,不然隨便調(diào)用looper.quit署浩,App就奔潰了揉燃。用戶子線程中通過(guò)Looper.prepare()創(chuàng)建Looper默認(rèn)是true,支持退出的筋栋。
2.3炊汤、MessageQueue的創(chuàng)建
private long mPtr; // used by native code
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
MessageQueue創(chuàng)建時(shí),創(chuàng)建了一個(gè)long類型的成員二汛,native可以通過(guò)這個(gè)long指針直接強(qiáng)轉(zhuǎn)成native對(duì)象用婿崭,這里是底層的NativeMessageQueue的指針。
從這里可以看出肴颊,一個(gè)線程可以創(chuàng)建無(wú)數(shù)個(gè)Handler氓栈,但只能有一個(gè)Looper,一個(gè)MessageQueue婿着。
三授瘦、Looper.loop() 與 handler.sendMessage(xx)
3.1、開啟輪詢消息Looper.loop()
入口函數(shù)ActivityThread.main()方法中竟宋,就開啟了Looper.loop()提完。
//from Looper
public static void loop() {
......
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // 可能阻塞
......
msg.target.dispatchMessage(msg);
......
msg.recycleUnchecked(); //消息進(jìn)入循環(huán)池
}
}
Q: 如果讓我們寫個(gè)Handler分發(fā)、處理Message丘侠,你會(huì)怎么寫徒欣?
因?yàn)镠andler的分發(fā)事件可能在其他線程,被稱為生產(chǎn)者蜗字,處理Message的Handler被稱為消費(fèi)者打肝,典型的生產(chǎn)消費(fèi)模型,肯定會(huì)考慮生產(chǎn)多了+消費(fèi)慢挪捕,生產(chǎn)少了+消費(fèi)快的場(chǎng)景粗梭,此時(shí)偽代碼如下:
public synchronized void loop() {
long now = System.currentTimeMillis();
int count; //mMessageQueue.top()就是獲得一個(gè)Message
while ((count = mMessageQueue.size()) == 0 || mMessageQueue.top().when > now) {
long diff = (count == 0) ? 0 : (now - mMessageQueue.top().when);
try {
this.wait(diff); //這里就類似Message msg = queue.next();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
......
this.notifyAll();
}
系統(tǒng)肯定不會(huì)用wait/notify+synchronized這種性能欠佳的組合實(shí)現(xiàn),對(duì)的级零,底層用的是Epoll機(jī)制實(shí)現(xiàn)
來(lái)看看MessageQueue的Next方法断医。
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands(); //被調(diào)用說(shuō)明后面的代碼可能會(huì)引起線程阻塞(此處不懂)
}
nativePollOnce(ptr, nextPollTimeoutMillis) //epoll_wait
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
/此處msg沒(méi)有target(handler),那就表示這個(gè)msg為同步屏障消息奏纪,配合異步消息使用的
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//獲取需要等待的時(shí)間
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//終于拿到一個(gè)可用的Message了鉴嗤,單鏈表取出Message的操作,模板代碼
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next; //所以mMessage就是下一個(gè)要處理的Message序调。記住了
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1; //-1傳給native的epoll_wait躬窜,表示永遠(yuǎn)不能喚醒,需要主動(dòng)被wake
}
//messageQueue為empty了 或者 mMessage是將來(lái)才處理的(when還沒(méi)到)
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
mBlocked = true; //那就繼續(xù)阻塞
continue;
}
//每次處理的handler最多只能是4個(gè)炕置,多了不處理荣挨?男韧??默垄?
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = idler.queueIdle(); //idleHandler的queueIdle返回值true/false處理不同
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0; //處理完idle消息后此虑,立馬就喚醒了,接下來(lái)再取新的口锭。
}
}
nextPollTimeoutMillis(阻塞的超時(shí)時(shí)間)有兩種情況
第一種:首次啟動(dòng)loop朦前,nextPollTimeoutMillis = 0,進(jìn)入到nativePollOnce(ptr, nextPollTimeoutMillis) 這里鹃操,其底層epoll_wait(epFd,events,MAX_EVENTS,timeout)阻塞韭寸,這個(gè)阻塞可因超時(shí)時(shí)間到了或者關(guān)注的事件可讀了被喚醒,這里timeout為0荆隘,所以很快會(huì)被喚醒恩伺。
第二種:非首次,nextPollTimeoutMillis就來(lái)自 (msg.when-now)椰拒,epoll_wait的超時(shí)時(shí)間就可能不是0了晶渠,時(shí)間未到,就阻塞著燃观,直到被喚醒褒脯。
3.1.1、nativePollOnce(ptr, nextPollTimeoutMillis)的底層調(diào)用
看源碼C++代碼可以使用官方網(wǎng)站:Android Code Search
private native void nativePollOnce(long ptr, int timeoutMillis);
之前說(shuō)java層創(chuàng)建MessageQueue的時(shí)候缆毁,native層返回一個(gè)NativeMessageQueue的指針番川,NativeMessageQueue的創(chuàng)建伴隨著native層的Looper的創(chuàng)建。
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); //源碼是CPP所以new的
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue); //long類型的指針
}
//NativeMessageQueue的構(gòu)造函數(shù):通過(guò)參數(shù)化表進(jìn)行構(gòu)造
NativeMessageQueue::NativeMessageQueue() :mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false); //創(chuàng)建了一個(gè)native層Looper脊框,給native線程綁定了一個(gè)Looper
Looper::setForThread(mLooper);
}
}
nativePollOnce的調(diào)用颁督,發(fā)現(xiàn)java層的long類型指針強(qiáng)轉(zhuǎn)成nativeMessageQueue,這是常規(guī)操作缚陷,java層傳遞過(guò)來(lái)的timeoutMillis一路攜帶适篙。
//nativePollOnce方法
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) { //java的long類型指針這里又強(qiáng)轉(zhuǎn)成nativeMessageQueue
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
....
mLooper->pollOnce(timeoutMillis);
......
}
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
......
//與handler功能關(guān)系不大的邏輯略去往核,這里是用底層Looper干其他的事情箫爷。
......
result = pollInner(timeoutMillis);
}
}
下面??的方法是重點(diǎn) epoll_wait出來(lái)了。
//timeoutMillis一路攜帶
int Looper::pollInner(int timeoutMillis) {
// Adjust the timeout based on when the next message is due.
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
timeoutMillis = messageTimeoutMillis;
}
}
.....
int result = POLL_WAKE;
mPolling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS]; //經(jīng)典的Epoll機(jī)制
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
mPolling = false;
.....
for (int i = 0; i < eventCount; i++) {
const SequenceNumber seq = eventItems[i].data.u64;
uint32_t epollEvents = eventItems[i].events;
if (seq == WAKE_EVENT_FD_SEQ) {
if (epollEvents & EPOLLIN) {
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
......//處理其他非handler的功能
}
}
return result;
}
void Looper::awoken() {
uint64_t counter;
//及時(shí)從管道m(xù)WakeEventFd.get()讀描述符中讀取數(shù)據(jù)
TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}
3.1.1.1聂儒、為了理解這里的調(diào)用可以看個(gè)管道配合Epoll的例子 具體來(lái)自這個(gè)博客
早期版本4.4及其以下版本虎锚,handler底層用的管道+Epoll實(shí)現(xiàn),現(xiàn)在采用eventfd()+Epoll機(jī)制實(shí)現(xiàn)衩婚。
..... //Mac上用CLion跑demo發(fā)現(xiàn)Mac找不到epoll的庫(kù)
int main(){
int ret,pid;
int pipe_fd[2];
if((ret=pipe(pipe_fd))<0) { //調(diào)用linux的pipe方法窜护,獲取讀寫描述符,讀0 寫1
return -1;
}
const int MAXEVENTS = 1024;
struct epoll_event events[MAXEVENTS]; //就緒列表
struct epoll_event ev; //epoll_event事件
ev.data.fd = pipe_fd[0]; //讀描述
ev.events = EPOLLIN|EPOLLET; //EPOLLIN事件觸發(fā)表示對(duì)應(yīng)的文件描述符上有可讀數(shù)據(jù)非春。EPOLLET邊緣觸發(fā)
int epfd=epoll_create(MAXEVENTS); //創(chuàng)建epoll的句柄柱徙,size(MAXEVENTS)用來(lái)告知內(nèi)核監(jiān)聽的數(shù)目
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,pipe_fd[0],&ev);//注冊(cè)要監(jiān)聽的事件缓屠,pipe_fd[0]再次傳入。
if (ret != 0){
close(pipe_fd[0]);
close(pipe_fd[1]);
close(epfd);
return -1;
}
//fork()有意思:會(huì)返回兩次分別在父子進(jìn)程返回护侮,pid>0表示的是父進(jìn)程敌完,返回的pid==0表示子進(jìn)程,
if((pid=fork())>0) {
int count=epoll_wait(epfd,events,MAXEVENTS,5000);
char r_buf[100];
for(int i=0;i<count;i++){
if((events[i].data.fd==pipe_fd[0])&&(events[0].events&EPOLLIN)){
int r_num=read(pipe_fd[0],r_buf,100); //及時(shí)從管道中讀
cout<<"read "<<r_num<<" bytes data from the pipe : "<<r_buf<<endl;
}
}
close(pipe_fd[1]);
close(pipe_fd[0]);
close(epfd);
}else if(pid==0){
close(pipe_fd[0]); //子進(jìn)程的資源繼承自父進(jìn)程
char w_buf[100];
strcpy(w_buf,"hello world");
if(write(pipe_fd[1],w_buf,12)!=-1) //往寫描述符中寫入
cout<<"data had written!\n";
close(pipe_fd[1]);//write
}
return 0;
}
epoll的三個(gè)關(guān)鍵函數(shù)epoll_create返回epollFd羊初,epoll_ctl注冊(cè)要監(jiān)聽的事件滨溉,epoll_wait阻塞等待關(guān)注的事件到來(lái)衡瓶。
epoll_wait函數(shù)說(shuō)明
- 返回值:成功時(shí)返回就緒列表中文件描述符的個(gè)數(shù)驾胆,超時(shí)返回0,異常返回-1端辱。
- timeout:指定epoll的超時(shí)時(shí)間得哆,單位是毫秒脯颜。當(dāng)timeout為-1是,epoll_wait調(diào)用將永遠(yuǎn)阻塞柳恐,直到某個(gè)事件發(fā)生伐脖。當(dāng)timeout為0時(shí),epoll_wait調(diào)用將立即返回乐设。
- maxevents:指定最多監(jiān)聽多少個(gè)事件讼庇,必須大于0。
- events:檢測(cè)到事件近尚,將所有就緒的事件從內(nèi)核事件表中復(fù)制到它的第二個(gè)參數(shù)events指向的數(shù)組中蠕啄。
3.1.1.2、mWakeEventFd
Looper創(chuàng)建時(shí)會(huì)創(chuàng)建Epoll實(shí)例戈锻,然后注冊(cè) mWakeEventFd歼跟。
//Looper創(chuàng)建
Looper::Looper(bool allowNonCallbacks):.....{
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
AutoMutex _l(mLock);
if (mEpollFd >= 0) {
mEpollFd.reset();
}
// Allocate the new epoll instance and register the WakeEventFd.
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
......
}
//捕捉一下WakeEventFd的操作
using unique_fd = unique_fd_impl<DefaultCloser>;
android::base::unique_fd mWakeEventFd;
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
void Looper::wake() {
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
......
}
void Looper::awoken() {
uint64_t counter;
TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}
之前例子采用管道讀寫,通過(guò)pipe方法實(shí)現(xiàn)格遭,輕易獲得讀哈街、寫描述符,讀寫描述符分開的拒迅,而這里讀寫都是mWakeEventFd.get()骚秦,通過(guò)eventfd函數(shù)創(chuàng)建fd。
什么是eventfd
eventfd是Linux 2.6提供的一種系統(tǒng)調(diào)用璧微,它可以用來(lái)實(shí)現(xiàn)事件通知作箍。eventfd包含一個(gè)由內(nèi)核維護(hù)的64位無(wú)符號(hào)整型計(jì)數(shù)器,創(chuàng)建eventfd時(shí)會(huì)返回一個(gè)文件描述符前硫,進(jìn)程可以通過(guò)對(duì)這個(gè)文件描述符進(jìn)行read/write來(lái)讀取/改變計(jì)數(shù)器的值胞得,從而實(shí)現(xiàn)進(jìn)程間通信。(mac完全用不了這個(gè)epoll和eventfd屹电,尷尬)
EPOLLIN的觸發(fā)條件是內(nèi)核接收到新發(fā)來(lái)的數(shù)據(jù)阶剑,從不可讀狀態(tài)變?yōu)榭勺x狀態(tài)跃巡。
一旦我們 nativePollOnce(ptr, nextPollTimeoutMillis)經(jīng)過(guò)底層走一圈,混到timeout時(shí)間到了牧愁,epoll_wait返回瓷炮,不阻塞了,就進(jìn)入java層做取消息的操作递宅。
因java取消息它是優(yōu)先判斷鏈表頭是不是同步屏障娘香,所以接下來(lái)先看同步屏障消息和異步消息??。
3.1.2办龄、同步屏障消息和異步消息
Message的三種類型 普通消息烘绽、同步屏障、異步消息俐填,區(qū)分它們很簡(jiǎn)單安接。
- 普通消息 我們一般發(fā)送的Message默認(rèn)就是,其target就是handler英融。
- 同步屏障 因沒(méi)有message.target盏檐,所以不需要分發(fā),(正常發(fā)送的消息必須指定target)驶悟,沒(méi)有handler的消息就可以認(rèn)定為同步屏障胡野。它也有時(shí)間戳,只會(huì)影響后面的消息痕鳍。消息隊(duì)列可以插入多個(gè)同步屏障硫豆,插入同步屏障沒(méi)有喚醒線程處理消息,普通消息插入會(huì)喚醒笼呆。插入同步屏障的會(huì)返回token(序列號(hào))熊响,作為remvoe同步屏障方法的參數(shù)用。
- 異步消息 設(shè)置過(guò)msg.setAsynchronous(true)的Message诗赌。
//MessageQueue的next方法部分邏輯 被喚醒后汗茄,優(yōu)先判斷是否同步屏障+異步消息組合
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
如果鏈表頭(msg != null && msg.target == null)是同步屏障消息,那就去找下一個(gè)異步消息铭若,二者狼狽為奸洪碳,阻擋正常普通消息的處理,此時(shí)普通消息就算達(dá)到when了奥喻,也只能乖乖等著偶宫。一般同步屏障+異步消息用于UI繪制這種需要立即響應(yīng)的場(chǎng)景非迹。
3.1.1.1环鲤、系統(tǒng)中使用同步屏障的場(chǎng)景
//當(dāng)我們更新Ui時(shí)喜歡調(diào)用:
binding.tv.requestLayout();
// ||||其調(diào)用路徑||||
view->requestLayout()
viewRootImpl->requestLayout()
viewRootImpl->scheduleTraversals()
-----------------------------------------------------------------------------
//from viewRootImpl
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true; //標(biāo)記位為true
//發(fā)送同步屏障消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//編舞者添加監(jiān)聽
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
......
}
}
//from MessageQueue
//插入同步屏障消息到鏈表,不走h(yuǎn)andler插入憎兽,直接在MessagQueue里面插入
private int postSyncBarrier() {
long when = SystemClock.uptimeMillis();
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain(); //沒(méi)有設(shè)置target操作
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages; //鏈表頭消息
if (when != 0) { //同步屏障也遵循按處理時(shí)間插入冷离,沒(méi)搞特殊化
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // 插入同步消息
msg.next = p;
prev.next = msg;
} else { //插入到鏈表頭
msg.next = p;
mMessages = msg;
}
return token; //同步屏障的token,用于取消用
}
}
當(dāng)我們調(diào)用View的requestLayout時(shí),沒(méi)有立馬繪制,而是由ViewRootImpl來(lái)協(xié)助走繪制流程吵冒,先發(fā)送同步屏障消息,注冊(cè)Choreographer監(jiān)聽(用戶添加監(jiān)聽還可用于屏幕幀率獲取西剥,固定脈沖頻率60Hz或者120Hz痹栖,它向SurfaceFlinger發(fā)起請(qǐng)求,獲取脈沖Vsync的到來(lái))瞭空,脈沖信號(hào)到來(lái)的回調(diào)-->FrameDisplayEventReceiver的onVsync()揪阿。
//from Choreographer
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
........
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this); //this就是??的run方法
msg.setAsynchronous(true); //發(fā)送異步消息
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime(); //當(dāng)前時(shí)間跟脈沖時(shí)間diff
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) { //這個(gè)日志太常見--丟幀提示
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
frameTimeNanos = startNanos - lastFrameOffset;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
//請(qǐng)求下一個(gè)垂直同步信號(hào)
scheduleVsyncLocked();
return;
}
......
mLastFrameTimeNanos = frameTimeNanos;
}
......
//Choreographer的一些回調(diào)
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
}
// frmo ViewRootImpl 前面mChoreographer.postCallbac設(shè)置的回調(diào)轉(zhuǎn)到mTraversalRunnable
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); //移除同步屏障消息
performTraversals(); //接下里就是測(cè)量、布局咆畏、繪制了
}
}
同步屏障插入了南捂,脈沖信號(hào)來(lái)臨時(shí),onVsync回調(diào)異步消息插入旧找,當(dāng)從消息隊(duì)列里面取出消息時(shí)溺健,如果同步屏障消息在鏈表頭,那么插入的異步消息會(huì)很快得到處理钮蛛,在真正開始執(zhí)行繪制前鞭缭,移除同步屏障。
如果消息隊(duì)列為空 or 也沒(méi)有同步消息 or 待處理的普通消息還沒(méi)有到時(shí)間魏颓,那就該處理IdleHandler了
??岭辣。
3.1.3、IdleHandler的處理
- 處理時(shí)機(jī) 當(dāng)MessageQueue為空或者接下來(lái)的Message的when還沒(méi)有到的時(shí)候甸饱。
- 使用場(chǎng)景 可用于啟動(dòng)優(yōu)化易结,針對(duì)一些不是啟動(dòng)必須的操作,可以放到首頁(yè)idleHandler中做延遲處理柜候。
- idler.queueIdle()的返回值 默認(rèn)是false搞动,就只處理一次后就移除了,如果返回true渣刷,可能會(huì)處理多次鹦肿。
看來(lái)確實(shí)只有在目前沒(méi)有可處理的Message了才會(huì)考慮處理idleHandler。
3.2辅柴、發(fā)送消息之handler.sendMessage(xx)
//from Handler
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postDelayed(Runnable r, long delayMillis){
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) delayMillis = 0;
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //這個(gè)this就是Handler
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
用戶使用的各種post/postDalay/sendMessage最終都是會(huì)走到enqueueMessage方法箩溃,在這里給Message設(shè)置target,target就是Handler碌嘀。如果handler構(gòu)造參數(shù)asynchronous傳入true,就在這里給Message添加異步標(biāo)志涣旨。
boolean enqueueMessage(Message msg, long when) {
.....
//考慮多線程,這里用了 synchronized
synchronized (this) {
......
msg.markInUse();
msg.when = when;
Message p = mMessages; //mMessages表示接下來(lái)要處理的Message股冗,可能是null
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; //前面處在nativePollOnce的epoll_wait那就阻塞了為true
} else {
//要插入的消息比接下來(lái)要處理的消息(mMessages)的when還晚
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
在這里傳入的msg不可能是同步屏障消息霹陡,因同步屏障消息直接通過(guò)postSyncBarrier插到隊(duì)列里面。(注意postSyncBarrier是私有api,我們用不了烹棉,除了反射)
- if (p == null || when == 0 || when < p.when) 表示鏈表頭為null或要處理的比鏈表頭mMessages還要早攒霹,那肯定要將msg插入到鏈表頭了。一般mBlocked為true浆洗,然后就喚醒了催束。
-
needWake = mBlocked && p.target == null && msg.isAsynchronous() 能走到這里就說(shuō)明插入的消息處理時(shí)間比隊(duì)列頭的msg晚,一般是不喚醒的伏社,但是有特殊情況(鏈表頭為同步屏障+首個(gè)異步消息插入)抠刺。
假設(shè)當(dāng)前mBlocked是true,隊(duì)頭的mMessages是同步屏障摘昌,要插入的消息為異步消息矫付,neekWake此時(shí)為true。 - 1第焰、進(jìn)入循環(huán)买优,同步屏障消息在鏈表頭且隊(duì)列中無(wú)異步消息,如果要插入的消息是異步消息挺举,就會(huì)被喚醒杀赢。
- 2、進(jìn)入循環(huán)湘纵,同步屏障消息在鏈表頭且隊(duì)列中有異步消息脂崔,即使再插入消息,也不會(huì)喚醒梧喷。
總結(jié)一下插入消息能被喚醒的場(chǎng)景:
1砌左、鏈表頭為null 或者 插入的普通消息/異步消息比鏈表頭消息的處理時(shí)間早。
2铺敌、鏈表頭為同步屏障+首個(gè)異步消息插入汇歹。
如當(dāng)前鏈表頭是同步屏障消息,插入的異步消息按時(shí)間處理順序即使插入到隊(duì)列中很靠后的位置偿凭,也不影響異步消息的處理時(shí)效产弹,仍是優(yōu)先處理的。
//loop取消息時(shí)弯囊,如果鏈表頭是同步屏障消息痰哨,優(yōu)先從隊(duì)列里面找異步消息處理。
......
nativePollOnce(ptr, nextPollTimeoutMillis)
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//看到msg沒(méi)有target(handler)匾嘱,那就表示這個(gè)Message為同步屏障消息斤斧,配合異步消息使用
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
......
//處理普通消息
......
3.2.1、nativeWake(mPtr)
//from java MessageQueue
private native static void nativeWake(long ptr);
//from NativeMessageQueue
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
// from native的Looper
void Looper::wake() {
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
.....
}
nativeWake是在mWakeEventFd.get()里面寫入了unit64_t的1霎烙。mWakeEventFd.get()的eventfd包含一個(gè)由內(nèi)核維護(hù)的64位無(wú)符號(hào)整型計(jì)數(shù)器撬讽,進(jìn)程可以通過(guò)對(duì)這個(gè)文件描述符進(jìn)行read/write來(lái)讀取/改變計(jì)數(shù)器的值蕊连。當(dāng)這里寫入數(shù)值時(shí),那epoll_wait就能返回了锐秦,這里的inc改成其他值應(yīng)該也可以,因?yàn)槲覀冎魂P(guān)心epoll_wait是否返回不再阻塞盗忱。
總結(jié)一下:epoll_wait在timeout超時(shí)時(shí)間到來(lái)時(shí)或mWakeEventFd.get()有數(shù)據(jù)可讀時(shí)會(huì)返回酱床,不再阻塞。
3.3趟佃、處理消息之handleMessage
用戶可能用3種方式去處理消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) { //1
handleCallback(msg);
} else {
if (mCallback != null) { //2
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); //3
}
}
Message在獲取時(shí)扇谣,有傳入callback就走1,handler構(gòu)造函數(shù)傳入了Callback闲昭,就走2罐寨,默認(rèn)(mCallback.handleMessage(msg))的值為false,false表示我們還可以繼續(xù)處理3序矩,如果是返回值為true鸯绿,一旦被處理過(guò)了,那我們重寫的handleMessage方法還是沒(méi)法調(diào)用簸淀。
四瓶蝴、關(guān)于Handler的其他問(wèn)題
4.1、一般我們固定頻率的任務(wù)(定時(shí)器)怎么實(shí)現(xiàn)比較好呢租幕?
- 方式一舷手、handler.postDelayed,但是精度么呵呵呵劲绪,handler發(fā)送普通的延時(shí)消息男窟,通過(guò)epoll_wait的超時(shí)機(jī)制實(shí)現(xiàn),而且前面有Message如果處理不及時(shí)會(huì)導(dǎo)致精度更差了贾富。
- 方式二歉眷、Timer:底層通過(guò)synchronize+wait(diff)實(shí)現(xiàn)。
- 方式三颤枪、AlarmManager姥芥,用的少。
- 方式四:固定頻率的線程池執(zhí)行任務(wù)汇鞭。
- 方式五:Choreographer添加監(jiān)聽實(shí)現(xiàn)凉唐,頻繁獲取幀率可用它,相當(dāng)于16ms的定時(shí)器霍骄。不知道有沒(méi)得兄弟在實(shí)際場(chǎng)景中用過(guò)台囱。
4.2、主線程Looper為啥沒(méi)有ANR?
入口函數(shù)ActivityThread的main中開啟了Loop.loop()读整,在queue.next()獲取下一個(gè)Message時(shí)會(huì)走底層的epoll機(jī)制簿训,進(jìn)行阻塞等到超時(shí)時(shí)間到來(lái)時(shí)喚醒,就跟我們用synchronize+wait(diff)類似,wait是線程阻塞强品。
ANR機(jī)制 ANR大家都知道是應(yīng)用程序無(wú)響應(yīng)膘侮,而且這個(gè)應(yīng)用程序無(wú)響應(yīng)的彈窗不是在我們app進(jìn)程,而是在AMS所在的進(jìn)程(SystemServer進(jìn)程)的榛。
Service TimeOut: 前臺(tái)20s琼了,后臺(tái)服務(wù)200s。
廣播 Timeout: 前臺(tái)廣播10s夫晌,后臺(tái)廣播60s
contentProvider Timeout: 10s
InputDispatching Timeout: 5s
這里的超時(shí)雕薪,前面3大組件都是啟動(dòng)創(chuàng)建超時(shí)導(dǎo)致ANR,最后一個(gè)是用戶操作導(dǎo)致ANR晓淀。
先就從最簡(jiǎn)單的Service啟動(dòng)看起吧所袁。
//通過(guò)startService啟動(dòng)service
Intent intent = new Intent(MainActivity.this, MyService.class);
startService(intent);
//from ContextImpl 很關(guān)鍵的一個(gè)類,四大組件中只要attach上下文就需要它
@Override
public ComponentName startService(Intent service) {
.......
return startServiceCommon(service, false, mUser);
}
private ComponentName startServiceCommon(Intent service, boolean requireForeground,UserHandle user) {
...... //binder調(diào)用來(lái)了
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), requireForeground,
getOpPackageName(), user.getIdentifier());
........
return cn;
}
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
我們的app能夠調(diào)用AMS的方法凶掰,肯定要先拿到AMS的binder代理對(duì)象燥爷,這里通過(guò)ActivityManager.getService()就可以搞到,發(fā)起binder調(diào)用AMS的startService懦窘。(AMS是在SystemServer進(jìn)程局劲,跟我們app不在同一個(gè)進(jìn)程,看到源碼里面以I開頭的類奶赠,基本就當(dāng)做binder對(duì)象類處理)鱼填,同樣AMS想要調(diào)用我們app的方法,也要通過(guò)binder調(diào)用毅戈,也要搞到我們app的binder代理對(duì)象(IApplicationThread)才行苹丸。
AMS有啥用,四大組件的創(chuàng)建啟動(dòng)都?xì)w他管苇经,我們可以通過(guò)Instrumentation去捕捉四大組件的生命周期方法赘理。
//from AMS
AMS-->startService(XXX)
AMS--->startServiceLocked(XXX)
AMS--->startServiceInnerLocked(XXX)
AMS--->bringUpServiceLocked(XXX)
AMS--->realStartServiceLocked(XXX)
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
//創(chuàng)建前埋入炸彈
bumpServiceExecutingLocked(r, execInFg, "create");
boolean created = false;
try {
....... //創(chuàng)建Service
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
} catch (DeadObjectException e) {
.......
} finally {
if (!created) {
//服務(wù)創(chuàng)建done 拆除炸彈
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
}
}
.......
}
Service創(chuàng)建前,肯定有很多準(zhǔn)備工作扇单,先判斷要啟動(dòng)的service的進(jìn)程是否存在商模,不存在還得走Zygote創(chuàng)建進(jìn)程,然后進(jìn)程創(chuàng)建后還得向AMS注冊(cè)蜘澜,如果進(jìn)程存在施流,且ServiceRecord沒(méi)有值,就表示之前沒(méi)有創(chuàng)建過(guò)service鄙信,就走首次創(chuàng)建Service邏輯瞪醋,Service創(chuàng)建前,就埋入bomb装诡,Service創(chuàng)建完成银受,拆除bomb践盼。
//from AMS
AMS----bumpServiceExecutingLocked(XXX)
AMS----scheduleServiceTimeoutLocked(XXX)
static final int SERVICE_TIMEOUT = 20*1000;
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
.......
Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageDelayed(msg,
proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
所謂的埋入炸彈就是通過(guò)handler發(fā)了一個(gè)延時(shí)消息消息,延時(shí)的值前臺(tái)服務(wù)20s宾巍、后臺(tái)服務(wù)200s咕幻,這個(gè)Handler不是我們app的mH,是AMS進(jìn)程中的Handler顶霞。
再看看真正創(chuàng)建Service的操作
//from ApplicationThread
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
sendMessage(H.CREATE_SERVICE, s);
}
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
.....
case CREATE_SERVICE:
handleCreateService((CreateServiceData)msg.obj);
break;
......
//真正創(chuàng)建Service的邏輯
private void handleCreateService(CreateServiceData data) {
LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);
Service service = null;
try {//先創(chuàng)建上下文ContextImpl
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
//獲取所在的進(jìn)程
Application app = packageInfo.makeApplication(false, mInstrumentation);
java.lang.ClassLoader cl = packageInfo.getClassLoader();
//直接反射創(chuàng)建Service實(shí)例
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
//加載資源
context.getResources().addLoaders(
app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
.....
//添加上下文
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
mServices.put(data.token, service);
try { //binder調(diào)用AMS的serviceDoneExecuting
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
......
}
}
Service創(chuàng)建是在我們自己的app進(jìn)程肄程,自己通過(guò)反射構(gòu)造的Service實(shí)例,調(diào)用service的onCreate的后确丢,及時(shí)binder調(diào)用AMS的方法serviceDoneExecuting拆除??(remove掉那個(gè)延時(shí)Message)绷耍。
//from AMS
AMS---serviceDoneExecuting(XXX)
AMS---serviceDoneExecutingLocked(XXX)
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing) {
if (r.executeNesting <= 0) {
if (r.app != null) {
.......
r.app.execServicesFg = false;
r.app.executingServices.remove(r);
if (r.app.executingServices.size() == 0) {
//要啟動(dòng)的Service搞完了就移除消息
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
} else if (r.executeFg) {
......
}
......
}
.....
}
}
如果說(shuō)Service創(chuàng)建時(shí)間很長(zhǎng)吐限,那就處理ActivityManagerService.SERVICE_TIMEOUT_MSG的超時(shí)Message鲜侥。
//from AMS
final class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
.....
case SERVICE_TIMEOUT_MSG: {
mServices.serviceTimeout((ProcessRecord)msg.obj);
........
void serviceTimeout(ProcessRecord proc) {
......
if (anrMessage != null) {
mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
}
}
//from AppErrors
final void appNotResponding(ProcessRecord app, ActivityRecord activity,
ActivityRecord parent, boolean aboveSystem, final String annotation) {
......
// Bring up the infamous App Not Responding dialog (infamous--臭名昭著的)
Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem);
mService.mUiHandler.sendMessage(msg);
}
}
ANR的彈窗就可以顯示出來(lái)了。(infamous--臭名昭著的)诸典。
所以ANR跟Loopr.loop()完全是兩個(gè)不同的功能描函,ANR需要Looper的支持,沒(méi)得Looper狐粱,ANR不知道何時(shí)彈窗舀寓。
4.3、BlockCanary如何檢測(cè)UI卡頓肌蜻。
先看看Loopr.loop()互墓,仔細(xì)看看在message處理前后有啥。
//from Looper
public static void loop() {
......
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // 可能阻塞
.....
//logger-- 分發(fā)message
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
......
msg.target.dispatchMessage(msg);
......
//logger-- message處理完成
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
msg.recycleUnchecked(); //消息進(jìn)入循環(huán)池
}
}
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
從上面看logging.println在msg處理前后都打印日志了蒋搜,如果能夠捕捉到日志打印的時(shí)機(jī)篡撵,基本是就可以知道handleMessage耗時(shí)多少了,拿到耗時(shí)就可以判斷出是否卡頓了豆挽,而且貼心的是這個(gè)Printer是Looper的成員變量育谬,可以讓用戶設(shè)置自己的Printer。
我們看看blockCanary的自定義Printer以及設(shè)置時(shí)機(jī)
class LooperMonitor implements Printer {
private long mStartTimestamp = 0;
private long mStartThreadTimestamp = 0;
......
@Override
public void println(String x) {
if (mStopWhenDebugging && Debug.isDebuggerConnected()) {
return;
}
if (!mPrintingStarted) {
mStartTimestamp = System.currentTimeMillis();
......
} else {
final long endTime = System.currentTimeMillis();
if (isBlock(endTime)) {
notifyBlockEvent(endTime);
}
......
}
}
private boolean isBlock(long endTime) {
return endTime - mStartTimestamp > 3000;
}
//設(shè)置Looper的Printer
//BlockCanary
public void start() {
if (!mMonitorStarted) {
mMonitorStarted = true;
Looper.getMainLooper().setMessageLogging(new LooperMonitor(xxxx));
}
}
BlockCanary自定義了Printer帮哈,然后捕捉兩次時(shí)間diff膛檀,diff大于3s就認(rèn)定有卡頓發(fā)生,可以dump出堆棧顯示娘侍。設(shè)置Printer的時(shí)機(jī)是在入口類BlockCanary的start方法里面咖刃。
4.4、池化技術(shù)
池化技術(shù)采用享元設(shè)計(jì)模式實(shí)現(xiàn)憾筏,Message的池化僵缺、Glide中的BitmapPool、EventBus中的PendingPost等等踩叭。
我們關(guān)心的是池的設(shè)計(jì)磕潮,采用啥容器存儲(chǔ)翠胰、size選取、以及recycle自脯、obtain方法的處理之景。
Message的池化:存儲(chǔ)采用單鏈表實(shí)現(xiàn),鏈表長(zhǎng)度是50膏潮,主要看obtain和recycle方法锻狗。
//Handler中Message的池化操作
public final class Message implements Parcelable {
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50; //size最大50
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0;
sPoolSize--;
return m;
}
}
return new Message();
}
public void recycle() {
if (isInUse()) {
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
sPool、sPoolSync都是靜態(tài)的焕参,每個(gè)Message對(duì)象共享轻纪,obtain就是從單鏈表中取出一個(gè)Message,單鏈表沒(méi)有就new 新的Message叠纷,recycle是先重置Message刻帚,然后看message池中size是否大于MAX_POOL_SIZE,超了就不加入涩嚣,單鏈表的常規(guī)操作崇众,簡(jiǎn)直不要太簡(jiǎn)單。
Glide中的BitmapPool
//LruBitmapPool默認(rèn): 只有1 or 4張的屏幕大小的Bitmap可以復(fù)用航厚;
GroupedLinkedMap<Key, Bitmap> groupedMap = new GroupedLinkedMap<>();
存put 取get 基于有限的size來(lái)看是否需要trim顷歌。
EventBus采用ArrayList來(lái)存儲(chǔ),超了就不添加
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) {
PendingPost pendingPost = pendingPostPool.remove(size - 1);
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
return pendingPost;
}
}
return new PendingPost(event, subscription);
}
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
五幔睬、補(bǔ)充
MessageQueue 中還有一些native方法未涉及到眯漩,就是基于NativeMessagQueue做些其他的功能實(shí)現(xiàn)。是public的Api麻顶,目前暫未使用過(guò)赦抖。如有童鞋知道,可以評(píng)論告知澈蚌。
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
@OnFileDescriptorEventListener.Events int events,
@NonNull OnFileDescriptorEventListener listener) {
.....
synchronized (this) {
updateOnFileDescriptorEventListenerLocked(fd, events, listener);
}
}
六摹芙、相關(guān)推薦
本文轉(zhuǎn)自 https://juejin.cn/post/7016291737039536142,如有侵權(quán)宛瞄,請(qǐng)聯(lián)系刪除浮禾。