Android面試題:bindService獲取代理是同步仍是異步?

Android中bindService是一個異步的過程,什麼意思呢?使用bindService無非是想得到一個Binder服務的Proxy,但這個代理獲取到的時機並不是由bindService發起端控制,而是由Service端來控制,也就是說bindService以後,APP端並不會馬上得到Proxy,而是要等待Service通知APP端,具體流程可簡化以下:java

  • APP端先經過bindService去AMS登記,說明本身須要綁定這樣一個服務,並留下派送地址
  • APP回來,繼續作其餘事情,能夠看作是非阻塞的
  • AMS通知Service端啓動這個服務
  • Service啓動,並通知AMS啓動完畢
  • AMS跟住以前APP端留下的地址通知APP端,並將Proxy代理傳遞給APP端

經過代碼來看更直接面試

void test(){
 bindService(intent, new ServiceConnection() {
 @Override
 public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
 iMyAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder);
 Log.v(TAG, "onServiceConnected..." );
 }
 @Override
 public void onServiceDisconnected(ComponentName componentName) {
 }
 }, Context.BIND_AUTO_CREATE);
 Log.v(TAG, "end..." );
 }

</>架構

bindService的過程當中,上面代碼的Log應該是怎麼樣的呢?若是bindService是一個同步過程,那麼Log應該以下:app

TAG onServiceConnected ...
TAG end ...

可是因爲是個異步過程,真實的Log以下異步

TAG end ... 
TAG onServiceConnected ...

也就是說bindService不會阻塞等待APP端獲取Proxy,而是直接返回,這些均可以從源碼得到支持,略過,直接去ActivityManagerNative去看ide

public int bindService(IApplicationThread caller, IBinder token,
 Intent service, String resolvedType, IServiceConnection connection,
 int flags, int userId) throws RemoteException {
 Parcel data = Parcel.obtain();
 Parcel reply = Parcel.obtain();
 data.writeInterfaceToken(IActivityManager.descriptor);
 data.writeStrongBinder(caller != null ? caller.asBinder() : null);
 data.writeStrongBinder(token);
 service.writeToParcel(data, 0);
 data.writeString(resolvedType);
 data.writeStrongBinder(connection.asBinder());
 data.writeInt(flags);
 data.writeInt(userId);

 mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
 reply.readException();
 int res = reply.readInt();
 data.recycle();
 reply.recycle();
 return res;
}

mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0)確實會讓APP端調用線程阻塞,等待AMS執行BIND_SERVICE_TRANSACTION請求,不過AMS在執行這個請求的時候並不是是喚醒Service才返回,它返回的時機更早,接着看ActivityManagerService,函數

public int bindService(IApplicationThread caller, IBinder token,
 Intent service, String resolvedType,
 IServiceConnection connection, int flags, int userId) {
 ...
 synchronized(this) {
 return mServices.bindServiceLocked(caller, token, service, resolvedType,
 connection, flags, userId);
 }
}

ActivityManagerService直接調用ActiveServices的函數bindServiceLocked,請求綁定Service,到這裏APP端線程依舊阻塞,等待AMS端返回,假定Service所處的進程已經啓動可是Service沒有啓動,這時ActiveServices會進一步調用bindServiceLocked->realStartServiceLocked來啓動Service,有趣的就在這裏:學習

private final void realStartServiceLocked(ServiceRecord r,
 ProcessRecord app) throws RemoteException {
 ...

 app.thread.scheduleCreateService(r, r.serviceInfo,
 mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));
 ...

 requestServiceBindingsLocked(r);

app.thread.scheduleCreateService也是一個Binder通訊過程,他實際上是AMS異步請求ActivityThread中的ApplicationThread服務,系統服務請求客戶端的本地服務通常都是異步的:this

// 插入消息,等待主線程執行
public final void scheduleCreateService(IBinder token,
 ServiceInfo info, CompatibilityInfo compatInfo) {
 CreateServiceData s = new CreateServiceData();
 s.token = token;
 s.info = info;
 s.compatInfo = compatInfo;

 queueOrSendMessage(H.CREATE_SERVICE, s);
}

不過,這個請求直接向Service端的ActivityThread線程中直接插入一個消息就返回了,而並未等到該請求執行,由於AMS使用的很是頻繁,不可能老等待客戶端完成一些任務,因此AMS端向客戶端發送完命令就直接返回,這個時候其實Service尚未被建立,也就是這個請求只是完成了一半,onServiceConnected也並不會執行,onServiceConnected何時執行呢?app.thread.scheduleCreateService向APP端插入第一條消息,是用來建立Service的, requestServiceBindingsLocked其實就是第二條消息,用來處理綁定的spa

private final boolean requestServiceBindingLocked(ServiceRecord r,
 IntentBindRecord i, boolean rebind) {
 ...

 r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);

第二條消息是處理一些綁定需求,Android的Hanlder消息處理機制保證了第二條消息必定是在第一條消息以後執行,

public final void scheduleBindService(IBinder token, Intent intent,
 boolean rebind) {
 BindServiceData s = new BindServiceData();
 s.token = token;
 s.intent = intent;
 s.rebind = rebind;
 queueOrSendMessage(H.BIND_SERVICE, s);
}

以上兩條消息插入後,AMS端被喚醒,進而從新喚醒以前阻塞的bindService端,而這個時候,Service並不必定被建立,因此說這是個未知的異步過程,Service端處理第一條消息的時會建立Service,

private void handleCreateService(CreateServiceData data) {
 ...
 LoadedApk packageInfo = getPackageInfoNoCheck(
 data.info.applicationInfo, data.compatInfo);
 Service service = null;
 try {
 java.lang.ClassLoader cl = packageInfo.getClassLoader();
 service = (Service) cl.loadClass(data.info.name).newInstance();
 ...

執行第二條消息的時候, 會向AMS請求publishService,其實就是告訴AMS,服務啓動完畢,能夠向以前請求APP端派發代理了。

private void handleBindService(BindServiceData data) {
 Service s = mServices.get(data.token);
 if (s != null) {
 try {
 data.intent.setExtrasClassLoader(s.getClassLoader());
 try {
 if (!data.rebind) {
 IBinder binder = s.onBind(data.intent);
 ActivityManagerNative.getDefault().publishService(
 data.token, data.intent, binder);
 ...

AMS端收到publishService消息以後,纔會向APP端發送通知,進而經過Binder回調APP端onServiceConnected函數,同時傳遞Proxy Binder服務代理

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
 ...
 try {

 c.conn.connected(r.name, service);
 } catch (Exception e) {}

到這裏,onServiceConnected纔會被回調,不過,至於Service端那兩條消息何時執行,誰也不能保證,也許由於特殊緣由,那兩條消息永遠不被執行,那onServiceConnected也就不會被回調,可是這不會影響AMS與APP端處理其餘問題,由於這些消息是否被執行已經不能阻塞他們兩個了,簡單流程以下:

bindService的異步流程

最後,其實startService也是異步。


感謝你們能耐着性子,看完我囉哩囉嗦的文章。

願與各位堅守在Android開發崗位的同胞們互相交流學習,共同進步!

在這裏我也分享一份本身收錄整理的Android學習PDF+架構視頻+面試文檔+源碼筆記,還有高級架構技術進階腦圖、Android開發面試專題資料,高級進階架構資料幫助你們學習提高進階,也節省你們在網上搜索資料的時間來學習,也能夠分享給身邊好友一塊兒學習

若是你有須要的話,能夠點贊關注我

相關文章
相關標籤/搜索