经过以前对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
。