天天看點

關于bindService的那些事

寫在前面的話: 知識是永遠學不完的,就算是世界上的天才,他也隻能掌握茫茫知識海洋的一小部分,是以不管你在什麼領域,什麼位置,永遠保持一顆學習的态度,這樣我們才會明白自己的無知,才會激勵自己不斷成長、強大。

一、明确文章目标,通過該篇文章我們需要掌握什麼?

  • bindService的使用場景
  • bindService的實作原理

    關于Service的使用方法官方文檔、其他blog到處都有我就不在此贅述了

二、bindService的使用場景:

bindService接口定義在Context中,是以有Context的地方我們都可以取bindService,通常是在Activity的生命周期方法中(onCreate、onStart)去bindService,另外bindService通常用在需要跨程序的場景,如果是同程序建議使用startService即可,這裡提下startService和bindService的特性,startService後需要stopService來結束Service的生命周期,而bindService對應的是unbindservice來接收Service的生命周期,當所有bindService的用戶端都unbindService時,Service就會被系統kill掉(沒有結合startService啟動的話),bind遠端Service時,如果遠端Service挂掉,client端也能通過onServiceDisconnected感覺到(通過Binder的linkToDeath實作)

三、bindService的實作原理:

關于bindService的那些事

以上時序圖基本囊括bindService的主幹流程,具體每一步我不多說了,結合源碼去看沒什麼難度,其中onServiceConnected接口的回調是AMS通過scheduleBindService建構Server端Binder,再通過publishService調用IServiceConnection connected來完成的,另外需要注意的是這張圖中涉及到3個程序的互動,發起調用的Client程序,提供Service服務的Server端程序,AMS所在的SystemServer程序。同時bindService時Service所在程序有3種狀态,1.Service所在程序不在運作狀态,2.Service所在程序為運作狀态,但Service不在運作狀态,3.Service為運作狀态。上面的時序圖是針對相對複雜的第一種情況畫的。

兩個問題:

a.為什麼說bindService是一個異步調用?

b.可以在onServiceConnected中直接使用傳回的IBinder嗎?為什麼?

先分析第一個問題,為什麼說bindService是一個異步調用?也就是說調用bindservice後會了解傳回,而我們知道一般情況下,通過Binder跨程序調用時會将目前程序挂起,等待Server端Binder完成函數操作後再将目前發起函數調用的線程恢複并傳回結果,而bindService這個地方有什麼特殊的玄機嗎?先公布實作的方式就是Binder的oneway方式,也就是在AIDL接口中若聲明oneway關鍵字,則在發起IPC調用時不會阻塞在client端跟驅動的互動中,我們知道bindService接口本身的聲明為同步的,注意如下最後一個參數為0,IBinder.FLAG_ONEWAY為1。

mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0)  
           

這說明在其他地方發起了異步調用,經過分析,從IServiceConnection.aidl中找到了答案:

/** @hide */
    oneway interface IServiceConnection {
        void connected(in ComponentName name, IBinder service);
    }
           

該interface的實作類為:ServiceDispatcher.InnerConnection

當要bind的Service已經是運作狀态時且有其他Client端bind過,bindService時AMS中會通過IServiceConnection通過Binder方式發起IPC回調,然後bindService方法直接傳回,connected最終會回調Client端的onServiceConnted方法。當要bind的Service為非運作狀态或之前未bind過,AMS會通過Binder方式想Server端程序發起scheduleBindService請求,該請求也為異步方式,scheduleBindService主要通過調用Server的onBind函數擷取Binder對象,然後通過AMS将其publish,publish時也會回調Client端的onServiceConnected接口。

這裡可能大家會有疑惑,善于專研的同學可能會發現無論你再哪個線程去發起bindService操作,onServiceConnected都會在主線程完成回調,原因直接上代碼:

public void connected(ComponentName name, IBinder service) {
        if (mActivityThread != null) {
            mActivityThread.post(new RunConnection(name, service, 0));
        } else {
            doConnected(name, service);
        }
    }  
           

mActivityThread實際為ActivityThread中的mH對象,也就是處理UI線程相關操作的Handler,到此我們就明白不管你再Activity中的哪個生命周期方法發起bindService操作,也不管需要bind的Service是否已經運作或被被bind過(Service的onBinnd已經回調,Binder已經建構完成),onServiceConneced總是無法在你bind後的再下一行代碼就能擷取到。

到此第一個問題基本分析結束,其實第二個問題第一個已經解釋一部分,就是onServiceConned在主線程完成回調,如果通過IBinder發起的調用耗時的話就會嚴重影響使用者體驗,是以,如果Binder Server端提供的接口是耗時操作,在對應的Client端請使用線程池方式發起調用,同時如果定義的某個aidl接口存在多線程操作的話,需要做好Binder Server端接口的同步操作!

說明:這是本站的第一篇部落格,是以肯定存在纰漏之處,後續針對每篇部落格存在問題的 地方我也會不斷更新,力求完美一點,大家如果有什麼問題可以在下面留言(不是關于這篇部落格的也可以),我看到後會盡快給大家回複。

閱讀原文:http://beawaters.com/2016/11/20/its-about-bindService/,歡迎轉發,轉發請注明作者:bewater, 以及原部落格原文位址:http://beawaters.com/2016/11/20/its-about-bindService/

繼續閱讀