问题阐述:
由某个系统service创建出来的对象,并用的这个对象通过getSystemService()函数去获取对应Service,发现总是为null。
解决办法
去SystemServer里面review你的逻辑,看看是否有对应service未起来的时候就通过getSystemService去获取了,这样会导致当前context的cache里面记录STATE_NOT_FOUND的flag,那么自此之后即使后来service起来了此Context也不会拿到这个service。
探索:
一般上述问题的出现是在对应service没起来的时候就去获取了,当然得不到,但是研究表明,此时的service已经起来,并不是这个的问题。
我们一般获取service都是通过context.getSystemService的,那我们看一下调用逻辑:
getSystemService()
-->
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
-->
public final T getService(ContextImpl ctx) {
final Object[] cache = ctx.mServiceCache;
final int[] gates = ctx.mServiceInitializationStateArray;
for (;;) {
boolean doInitialize = false;
synchronized (cache) {
// Return it if we already have a cached instance.
T service = (T) cache[mCacheIndex];
//从服务缓存中获取,如果有则返回,如果状态为STATE_NOT_FOUND则返回null
if (service != null || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) {
return service;
}
// If we get here, there's no cached instance.
// Grr... if gate is STATE_READY, then this means we initialized the service
// once but someone cleared it.
// We start over from STATE_UNINITIALIZED.
// 缓存中没有找到服务实例,但是其状态是 STATE_READY,说明有可能是缓存中
// 的服务实例被清除了,只是状态没有更新而已,此时需要更新状态并重新创建。
if (gates[mCacheIndex] == ContextImpl.STATE_READY) {
gates[mCacheIndex] = ContextImpl.STATE_UNINITIALIZED;
}
// It's possible for multiple threads to get here at the same time, so
// use the "gate" to make sure only the first thread will call createService().
// At this point, the gate must be either UNINITIALIZED or INITIALIZING.
/ 更新服务状态为 STATE_INITIALIZING,表示接下来开始创建该系统服务。
if (gates[mCacheIndex] == ContextImpl.STATE_UNINITIALIZED) {
doInitialize = true;
gates[mCacheIndex] = ContextImpl.STATE_INITIALIZING;
}
}
if (doInitialize) {
// Only the first thread gets here.
T service = null;
@ServiceInitializationState int newState = ContextImpl.STATE_NOT_FOUND;
try {
// This thread is the first one to get here. Instantiate the service
// *without* the cache lock held.
//创建service
service = createService(ctx);
newState = ContextImpl.STATE_READY;
} catch (ServiceNotFoundException e) {
onServiceNotFound(e);
} finally {
// 把服务实例和服务状态保存到缓存中去,有两种可能:
// 1. 服务创建成功时,把创建的服务实例和状态 STATE_READY 分别缓存起来;
// 2. 服务创建失败时,把 null 和状态 STATE_NOT_FOUND 分别缓存起来。
// 在后续查询缓存的过程中,只要是这两种情况就直接返回不再进行创建。
synchronized (cache) {
cache[mCacheIndex] = service;
gates[mCacheIndex] = newState;
cache.notifyAll();
}
}
return service;
}
// The other threads will wait for the first thread to call notifyAll(),
// and go back to the top and retry.
synchronized (cache) {
while (gates[mCacheIndex] < ContextImpl.STATE_READY) {
try {
cache.wait();
} catch (InterruptedException e) {
Log.w(TAG, "getService() interrupted");
Thread.currentThread().interrupt();
return null;
}
}
}
}
}
以上就为Android P的获取逻辑,关键在于getService里面mCacheIndex的作用,及每一个Context里面都会把system里面所有的service的状态存在mCacheIndex的数组里面,值如下:
static final int STATE_UNINITIALIZED = 0;
static final int STATE_INITIALIZING = 1;
static final int STATE_READY = 2;
static final int STATE_NOT_FOUND = 3;
按照P上的逻辑,未create的状态为STATE_UNINITIALIZED,create过称中service没起来(其实也可以看作是没有这个service)为STATE_NOT_FOUND。那么在这个Context里面当一个service的flag被标志成STATE_NOT_FOUND,那么之后无论这个service起来与否,通过getSystemService()是得不到这个service的。
O上的就有些许的差别,代码如下:
public final T getService(ContextImpl ctx) {
final Object[] cache = ctx.mServiceCache;
synchronized (cache) {
// Fetch or create the service.
Object service = cache[mCacheIndex];
if (service == null) {
try {
service = createService(ctx);
cache[mCacheIndex] = service;
} catch (ServiceNotFoundException e) {
onServiceNotFound(e);
}
}
return (T)service;
}
}
如上,它只会抛一个异常,每次调用如果chche里面没有,都会去create service。所以,在O上即使getSystemService的时候对应service没起来,也可以等后期起来的时候重新获得。