前面兩篇文章我們分別總結了Android四種Service的基本使用以及源碼層面總結一下startService的執行過程,本篇繼續從源碼層面總結bindService的執行過程。
本文依然按著是什么?有什么?怎么用?啥原理?的步驟來分析。
bindService使用方法和調用流程都與startService時有很多相似之處,方便的話請先閱讀上一篇《展開說說:Android服務之startService解析》。
- 是什么
調用bindService()來創建,調用方可以通過一個IBinder接口和service進行通信,需要通過ServiceConnection建立連接。多用于有交互的場景。
只能調用方通過unbindService()方法來斷開連接。調用方可以和Service通訊,并且一個service可以同時和多個調用方存在綁定關系,解除綁定也需要所有調用全部解除綁定之后系統才會銷毀service。
2、有什么
Service和Activity一樣也有自己的生命周期,也需要在AndroidManifest.xml中注冊。另外bindService的使用比startService要復雜一些:第一需要中創建一個Binder子類并定義方法來給使用者調用在onBind方法中返回它的實例;第二使用者需要創建ServiceConnection對象,并在onServiceConnected回調方法調用Binder子類中定義方法。
2.1 在AndroidManifest.xml中注冊
和startService注冊流程一樣:
?<service android:name="com.example.testdemo.service.ServiceJia" />
2.2 bindService時Service的生命周期
與startService時執行的生命周期有些不同。
onCreate???
它只在Service剛被創建的時刻被調用,Service在運行中,這個方法將不會被調用。也就是只有經歷過onDestroy生命周期以后再次。
onBind
當另一個組件調用?bindService()想要與Service綁定(例如執行 RPC)時執行,在此方法的實現中,必須通過返回?IBinder?提供一個接口,供客戶端用來與服務通信。您必須始終實現此方法;但是,如果您不想允許綁定,則應返回 null。這個方法默認時返回null。
onUnbind
調用方調用?unbindService()?來解除Service綁定時執行。
onDestroy
所有綁定到Service的調用方都解綁以后,則系統會銷毀該服務。
onRebind
當Service中的onUnbind方法返回true,并且Service調用unbindService之后并沒有銷毀,此時重新綁定時將會觸發onRebind。Service執行過onBind又onUnbind返回true并且還沒執行onDestroy,等再次bindService就會執行它。
日志打印
bindService
2024-12-01 11:19:14.434 30802-30802/com.example.testdemo3?E/com.example.testdemo3.service.ServiceJia: onCreate:
2023-12-01 11:19:14.436 30802-30802/com.example.testdemo3 E/com.example.testdemo3.activity.ServiceActivity: onServiceConnected:
2023-12-01 11:19:14.436 30802-30802/com.example.testdemo3 E/com.example.testdemo3.service.ServiceJia: JiaBinder ?--doSomething: start conncetion ???//這里不是生命周期,是binder對象調用binder內方法的打印,證明完成交互
unbindService
2023-12-01 11:21:10.705 12765-12765/com.example.testdemo3 E/com.example.testdemo3.service.ServiceJia: onUnbind:
2023-12-01 11:21:10.705 12765-12765/com.example.testdemo3 E/com.example.testdemo3.service.ServiceJia: onDestroy:
- 怎么用
因為是有交互的嘛,因此肯定比那些啟動以后就成了甩手掌柜的startService使用稍微負責一些,第一需要中創建一個Binder子類并定義方法來給使用者調用在onBind方法中返回它的實例;第二使用者需要創建ServiceConnection對象,并在onServiceConnected回調方法調用Binder子類中定義方法。
具體可以參考前面的《展開說說:Android四大組件之Service使用》已經總結了使用方法,這里不在贅述。
- 啥原理,SDK版本API 30
bindService調用流程都與startService時有很多相似之處,方便的話請先閱讀上一篇《展開說說:Android服務之startService解析》。
bindService的啟動方法是調用
Intent serviceIntent = new Intent(ServiceActivity.this, ServiceJia.class);
bindService(serviceIntent,serviceConnection, Context.BIND_AUTO_CREATE)
然后我們順著bindService方法開始解析源碼,Go :
4.1 從ContexWrapper的bindService開始,同startService:
@Override
public boolean bindService(Intent service, ServiceConnection conn,int flags) {return mBase.bindService(service, conn, flags);
}
4.2 ContextImpl類bindService
mBase的類型是Context,但實際代碼邏輯是在它的實現類ContextImpl類。
@Overridepublic boolean bindService(Intent service, ServiceConnection conn, int flags) {warnIfCallingFromSystemProcess();return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null,getUser());
}private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,String instanceName, Handler handler, Executor executor, UserHandle user) {// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.IServiceConnection sd;if (conn == null) {throw new IllegalArgumentException("connection is null");}if (handler != null && executor != null) {throw new IllegalArgumentException("Handler and Executor both supplied");}if (mPackageInfo != null) {if (executor != null) {sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);} else {sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);}} else {throw new RuntimeException("Not supported in system context");}validateServiceIntent(service);try {IBinder token = getActivityToken();if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null&& mPackageInfo.getApplicationInfo().targetSdkVersion< android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {flags |= BIND_WAIVE_PRIORITY;}service.prepareToLeaveProcess(this);int res = ActivityManager.getService().bindIsolatedService(mMainThread.getApplicationThread(), getActivityToken(), service,service.resolveTypeIfNeeded(getContentResolver()),sd, flags, instanceName, getOpPackageName(), user.getIdentifier());if (res < 0) {throw new SecurityException("Not allowed to bind to service " + service);}return res != 0;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}
bindService調用bindServiceCommon方法。將ServiceConnection 轉為Binder的實現類IServiceConnection方便跨進程的遠程服務的回調自己定義的方法。
4.3 來到LoadedApk
?final @NonNull LoadedApk mPackageInfo;
因此來到LoadedApk 查看getServiceDispatcher方法:
@UnsupportedAppUsage
public final IServiceConnection getServiceDispatcher(ServiceConnection c,Context context, Handler handler, int flags) {return getServiceDispatcherCommon(c, context, handler, null, flags);
}private IServiceConnection getServiceDispatcherCommon(ServiceConnection c,Context context, Handler handler, Executor executor, int flags) {synchronized (mServices) {LoadedApk.ServiceDispatcher sd = null;ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);if (map != null) {if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c);sd = map.get(c);}if (sd == null) {if (executor != null) {sd = new ServiceDispatcher(c, context, executor, flags);} else {sd = new ServiceDispatcher(c, context, handler, flags);}if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);if (map == null) {map = new ArrayMap<>();mServices.put(context, map);}map.put(c, sd);} else {sd.validate(context, handler, executor);}return sd.getIServiceConnection();}
}
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
????= new ArrayMap<>();
mServices記錄了應用當前活動的ServiceConnection和ServiceDispatcher的映射關系,不知是否記得ActivityThread中也有一個final ArrayMap<IBinder, Service> mServices = new ArrayMap<>(); 記錄了IBinder,和Service的映射關系。
繼續說LoadedApk中的service哈,上面代碼會判斷是否存在相同的ServiceConnection,如果不存在就創建新ServiceDispatcher實例并將其存儲在mService中,key時ServiceConnection,value為ServiceDispatcher,ServiceDispatcher內部存儲了ServiceConnection和InnerConnection對象。在調用bindService以后Service和調用方成功建立連接時系統會通過InnerConnection調用ServiceConnection中的onServiceConnected方法,此時我們就可以利用傳過來的IBinder調用Service中的方法完成交互了。這個過程支持跨進程IPC通信,比如兩個進程使用AIDL通信。
4.4 ContextImpl類bindService的bindIsolatedService
返回頭看上面4.2中的bindService方法,繼續向下看會調用ActivityManagerService的bindIsolatedService方法:
synchronized(this) {return mServices.bindServiceLocked(caller, token, service,resolvedType, connection, flags, instanceName, callingPackage, userId);}
4.5 來到ActiveService類的bindServiceLocked
繼續調用本類的bringUpServiceLocked:
bringUpServiceLocked(serviceRecord,
????????serviceIntent.getFlags(),
????????callerFg, false, false);
在調用本類realStartServiceLocked:
realStartServiceLocked(r, app, execInFg);
一般來說源碼中當一個方法多次穿梭調用之后突然帶上了real,那一定是離真相不遠了。
app.thread.scheduleCreateService(r, r.serviceInfo,
????????mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
????????app.getReportedProcState());
這一行就很熟悉了,和上一篇startService一樣,調用的ApplicationThread來創建Service實例并調用它的onCreate生命周期。
上一篇分析這個方法之下是調用onStartCommand生命周期
,沒錯這里也不例外下面requestServiceBindingsLocked(r, execInFg)也會去調用ApplicationThread的scheduleBindService:
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
????????r.app.getReportedProcState());
4.6來到ApplicationThread
利用它封裝的handler發送BIND_SERVICE消息:
public final void scheduleBindService(IBinder token, Intent intent,boolean rebind, int processState) {updateProcessState(processState, false);BindServiceData s = new BindServiceData();s.token = token;s.intent = intent;s.rebind = rebind;if (DEBUG_SERVICE)Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="+ Binder.getCallingUid() + " pid=" + Binder.getCallingPid());sendMessage(H.BIND_SERVICE, s);}
接收消息:
case BIND_SERVICE:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");handleBindService((BindServiceData)msg.obj);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;
關鍵來咯:
private void handleBindService(BindServiceData data) {Service s = mServices.get(data.token);if (DEBUG_SERVICE)Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);if (s != null) {try {data.intent.setExtrasClassLoader(s.getClassLoader());data.intent.prepareToEnterProcess();try {if (!data.rebind) {IBinder binder = s.onBind(data.intent);ActivityManager.getService().publishService(data.token, data.intent, binder);} else {s.onRebind(data.intent);ActivityManager.getService().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);}} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}} catch (Exception e) {if (!mInstrumentation.onException(s, e)) {throw new RuntimeException("Unable to bind to service " + s+ " with " + data.intent + ": " + e.toString(), e);}}}
}
上面代碼顯示根據token獲取Service對象,然后判斷首次綁定就調用onBind生命周期,已經綁定過就調用onReBind生命周期,返回的IBinder對象就可以用來調用Service中的方法了。但是為了讓調用方拿到這個IBinder就同過onServiceConnected方法回調回去,這個工作就有ActivityManagerService的publishService方法來完成。
4.6 來到ActivityManagerService
public void publishService(IBinder token, Intent intent, IBinder service) {// Refuse possible leaked file descriptorsif (intent != null && intent.hasFileDescriptors() == true) {throw new IllegalArgumentException("File descriptors passed in Intent");}synchronized(this) {if (!(token instanceof ServiceRecord)) {throw new IllegalArgumentException("Invalid service token");}mServices.publishServiceLocked((ServiceRecord)token, intent, service);}}
然后它有調用了ActiveService的publishServiceLocked方法來處理:
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {final long origId = Binder.clearCallingIdentity();try {if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r+ " " + intent + ": " + service);if (r != null) {Intent.FilterComparison filter= new Intent.FilterComparison(intent);IntentBindRecord b = r.bindings.get(filter);if (b != null && !b.received) {b.binder = service;b.requested = true;b.received = true;ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();for (int conni = connections.size() - 1; conni >= 0; conni--) {ArrayList<ConnectionRecord> clist = connections.valueAt(conni);for (int i=0; i<clist.size(); i++) {ConnectionRecord c = clist.get(i);if (!filter.equals(c.binding.intent.intent)) {if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Not publishing to: " + c);if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bound intent: " + c.binding.intent.intent);if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Published intent: " + intent);continue;}if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);try {c.conn.connected(r.name, service, false);} catch (Exception e) {Slog.w(TAG, "Failure sending service " + r.shortInstanceName+ " to connection " + c.conn.asBinder()+ " (in " + c.binding.client.processName + ")", e);}}}}serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);}} finally {Binder.restoreCallingIdentity(origId);}
}
它在for循環里調用一行代碼:
c.conn.connected(r.name, service, false);
順著代碼看:
Conn的類型是是在ConnectionRecord類定義的IServiceConnection:
final IServiceConnection conn; ?// The client connection.
Service就是建立連接的Ibinder實例。
4.7再次來到LoadedApk類
看一下IServiceConnection類connected方法:
private static class InnerConnection extends IServiceConnection.Stub {@UnsupportedAppUsagefinal WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;InnerConnection(LoadedApk.ServiceDispatcher sd) {mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);}public void connected(ComponentName name, IBinder service, boolean dead)throws RemoteException {LoadedApk.ServiceDispatcher sd = mDispatcher.get();if (sd != null) {sd.connected(name, service, dead);}}
}
而它又調用了ActivityThread類,mActivityThread就是其中Handler子類 H ,這一步就是為了利用Handler在主線程回調給調用方的onServiceConnected:
public void connected(ComponentName name, IBinder service, boolean dead) {if (mActivityExecutor != null) {mActivityExecutor.execute(new RunConnection(name, service, 0, dead));} else if (mActivityThread != null) {mActivityThread.post(new RunConnection(name, service, 0, dead));} else {doConnected(name, service, dead);}
}
RunConnection 的實現:
private final class RunConnection implements Runnable {RunConnection(ComponentName name, IBinder service, int command, boolean dead) {mName = name;mService = service;mCommand = command;mDead = dead;}public void run() {if (mCommand == 0) {doConnected(mName, mService, mDead);} else if (mCommand == 1) {doDeath(mName, mService);}}final ComponentName mName;final IBinder mService;final int mCommand;final boolean mDead;
}
回調onServiceConnected,徹底呼應上了:
public void doConnected(ComponentName name, IBinder service, boolean dead) {ServiceDispatcher.ConnectionInfo old;ServiceDispatcher.ConnectionInfo info;synchronized (this) {if (mForgotten) {// We unbound before receiving the connection; ignore// any connection received.return;}old = mActiveConnections.get(name);if (old != null && old.binder == service) {// Huh, already have this one. Oh well!return;}if (service != null) {// A new service is being connected... set it all up.info = new ConnectionInfo();info.binder = service;info.deathMonitor = new DeathMonitor(name, service);try {service.linkToDeath(info.deathMonitor, 0);mActiveConnections.put(name, info);} catch (RemoteException e) {// This service was dead before we got it... just// don't do anything with it.mActiveConnections.remove(name);return;}} else {// The named service is being disconnected... clean up.mActiveConnections.remove(name);}if (old != null) {old.binder.unlinkToDeath(old.deathMonitor, 0);}}// If there was an old service, it is now disconnected.if (old != null) {mConnection.onServiceDisconnected(name);}if (dead) {mConnection.onBindingDied(name);}// If there is a new viable service, it is now connected.if (service != null) {mConnection.onServiceConnected(name, service);} else {// The binding machinery worked, but the remote returned null from onBind().mConnection.onNullBinding(name);}
}
至此bindService的綁定流程分析完畢!
才疏學淺,如有錯誤,歡迎指正,多謝。