經過以前對Service銷燬流程的分析,stopService
和unbindService
最終都會進入到ActiveServices.bringDownServiceIfNeededLocked
方法中,該方法會判斷當前的Service
是否知足銷燬條件,其中的核心方法即是isServiceNeeded
。java
private final boolean isServiceNeeded(ServiceRecord r, boolean knowConn, boolean hasConn) {
// Are we still explicitly being asked to run?
if (r.startRequested) {
return true;
}
// Is someone still bound to us keepign us running?
if (!knowConn) {
hasConn = r.hasAutoCreateConnections();
}
if (hasConn) {
return true;
}
return false;
}
複製代碼
有兩個很是關鍵的變量:ServiceRecord.startRequested
和hasConn
,前者與start
有關,後者與bind
有關,只有二者都爲false
才能銷燬一個Service
。 咱們先來看看startRequested
app
經過全局搜索發現,該字段只有在ActiveServices.startServiceLocked
方法中,也便是start
流程中會被置爲true
。 在ActiveServices.stopServiceLocked
、ActiveServices.stopServiceTokenLocked
、ActiveServices.killServicesLocked
這三個方法中會被置爲false,ActiveServices.stopServiceTokenLocked
是在Service
調用stopSelf
時會觸發的,而ActiveServices.killServicesLocked
則是在清理應用(內存不足等場景)的時候觸發。函數
簡單來講ServiceRecord.startRequested
會在start
流程中被置爲true
,在stop
流程中置爲false
。所以,不管你以前調用過多少次startService
,只要你調了一次stopService
(以後沒有再調用startService
),那麼startRequested
就被置爲了false
。**startRequested
的值取決於最後一次調用的是startService
仍是stopService
。post
該字段的值跟ServiceRecord.hasAutoCreateConnection
方法的返回值有關this
public boolean hasAutoCreateConnections() {
// XXX should probably keep a count of the number of auto-create
// connections directly in the service.
for (int conni=connections.size()-1; conni>=0; conni--) {
ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
for (int i=0; i<cr.size(); i++) {
//這個flags就是調用bindService時使用的flags
if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
return true;
}
}
}
return false;
}
複製代碼
該方法內部會遍歷全部bind
至當前服務的鏈接,若是還存在任一鏈接,其調用bindService
時使用的flags
包含BIND_AUTO_CREATE
標誌,則返回true
,不然返回false
。spa
咱們以具體場景來分析怎樣才能銷燬一個服務:code
startService
來啓動服務。 這種場景下,只須要調用stopService
就能夠正常銷燬服務bindService
啓動服務 這種場景下,只須要調用對應的unbindService
便可、startService
和bindService
這種場景想要關閉服務的話,首先要調用stopService
,其次還須要確保以前使用BIND_AUTO_CREATE
進行綁定的客戶端解綁(unbindService
)便可。在Service啓動流程中有一個realStartServiceLocked
方法,在服務進程啓動完畢以後,會調用該方法繼續服務啓動的流程。realStartServiceLocked
內部調用了一個名爲requestServiceBindingsLocked
的方法處理bind
請求。從新貼一下該方法代碼:對象
private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg) throws TransactionTooLargeException {
for (int i=r.bindings.size()-1; i>=0; i--) {
IntentBindRecord ibr = r.bindings.valueAt(i);
//該方法內部會經過跨進程調用ApplicationThread.scheduleBindService
//來回調Service.onBind方法
if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
break;
}
}
}
複製代碼
能夠看到這裏有一個for
循環,這說明了Service.onBind
被屢次回調是可能的。那麼問題就變成了ServiceRecord.bindings
何時會保存多個值呢? 對bindings
字段的put
操做只發生在retrieveAppBindingLocked
方法中,該方法是在bind
流程中的ActiveServices.bindServiceLocked
方法中被調用的。 貼下代碼進程
public AppBindRecord retrieveAppBindingLocked(Intent intent,//客戶端發起bind請求所使用的Intent ProcessRecord app) {//客戶端進程記錄
Intent.FilterComparison filter = new Intent.FilterComparison(intent);
IntentBindRecord i = bindings.get(filter);
if (i == null) {
i = new IntentBindRecord(this, filter);
bindings.put(filter, i);
}
AppBindRecord a = i.apps.get(app);
if (a != null) {
return a;
}
a = new AppBindRecord(this, i, app);
i.apps.put(app, a);
return a;
}
複製代碼
能夠看到該方法首先將intent
封裝成了一個FilterComparison
對象做爲key
,而後去bindings
中檢索,若是沒有對應的值就會建立一個值。 再來看看FilterComparison.equals
方法,由於只有建立出不一樣的FilterComparison
實例,bindings
中才會保存多個值。內存
//Intent$FilterComparison.java
public boolean equals(Object obj) {
if (obj instanceof FilterComparison) {
Intent other = ((FilterComparison) obj).mIntent;
return mIntent.filterEquals(other);
}
return false;
}
//Intent.java
public boolean filterEquals(Intent other) {
if (other == null) {
return false;
}
if (!Objects.equals(this.mAction, other.mAction)) return false;
if (!Objects.equals(this.mData, other.mData)) return false;
if (!Objects.equals(this.mType, other.mType)) return false;
if (!Objects.equals(this.mPackage, other.mPackage)) return false;
if (!Objects.equals(this.mComponent, other.mComponent)) return false;
if (!Objects.equals(this.mCategories, other.mCategories)) return false;
return true;
}
複製代碼
能夠看到,FilterComparison
的比較實際上是跟Intent
密切相關的。Intent
內部mAction
、mData
、mType
、mPackage
、mComponent
、mCategories
中的任意字段發生變化,就會產生兩個不一樣的FilterComparison
實例。
在調用bindService
時,改變一下Intent
內部的一些值,就能夠觸發屢次Service.onBind
。
知道告終論,咱們來複盤一下,屢次使用同一個Intent
來bindService
的問題 一般咱們是如下面這種方式來構造Intent
的
Intent intent = new Intent(activity, DemoService.class);
//Intent.java
public Intent(Context packageContext, Class<?> cls) {
mComponent = new ComponentName(packageContext, cls);
}
複製代碼
這種方式初始化Intent
,最終會將構造函數的入參保存成mComponent
。
第一次進入bind
流程以後,調用retrieveAppBindingLocked
確定會爲bindings
生成一條新的IntentBindRecord
記錄。 這時候若是服務已經啓動,就會立刻進入requestServiceBindingLocked
方法
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i, boolean execInFg, boolean rebind) throws TransactionTooLargeException {
//...
//requested此時爲false
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
//...
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
if (!rebind) {
//觸發onBind以後requested被置爲了true
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
} catch (TransactionTooLargeException e) {
//...
} catch (RemoteException e) {
//...
}
}
return true;
}
複製代碼
因而可知,若是使用相同的Intent
請求bind
,那麼第二次進來requested
已是true
了,便不會觸發Service.onBind
。