序文
- 以下、GetSystemService を GSS と呼びます。
getSystemService の原理を理解する必要があるのはなぜですか?
ソースコードを解析した際、GSS がバインダーベースで実装されていると勘違いしていたのですが、出力されるログの内容が想像と異なっていたことが分かりました 論理的には、audioService は Binder.getCallingPid() を呼び出したときに取得した audioManager の pid を返すはずですこれは、バインダーを通じて audioManager から呼び出される audioService であるためです。しかし、実際の印刷は確かに上位層によって適用されており、その後、AudioManager のコンストラクターに Context パラメーターがあることに気づき、その後、それが無意味であることに気づきました。GSS は、独立したプロセスのバインダーではなく、インスタンス クラスのオブジェクトを返しました。
結論を先に言っておきます。キャッシュ タイプのシステム サービスの場合 (ほとんどの場合)、マップを介してサービス名に従ってインスタンス オブジェクトが返されます。オブジェクトは初めて取得されるときに実際に作成され、その後の取得ではキャッシュが返されます。 。実際にこの機能を実行するのは SystemServiceRegistry.java ですが、サービス インスタンスのマルチスレッド同時取得を処理するプロセスは非常に複雑です。
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];
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.
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.
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 = createService(ctx);
newState = ContextImpl.STATE_READY;
} catch (ServiceNotFoundException e) {
onServiceNotFound(e);
} finally {
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;
}
}
}
}
}
呼び出しチェーンは次のとおりです。
まず、mainActivity が継承された親クラス Activity の GSS を呼び出します。
- Activity.getSystemService()、このステップではサービス名WINDOW_SERVICEまたはSEARCH_SERVICEがフィルタリングされます。これら 2 つの特別なサービスは直接返すことができます。
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
- ContextThemeWrapper.getSystemService(), このステップはサービスLAYOUT_INFLATER_SERVICEをフィルタリングします。その処理は実際には次のステップと似ていますが、返されたサービスは cloneInContext(this) を再度呼び出します。なぜこのサービスに特別な処理が必要なのかわかりません。
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
次のステップは、保存された mBase を呼び出すことです。このパラメータは、ContextThemeWrapper が構築されるときに渡されます。アクティビティの作成時に作成されると推定されていますが、コンテキストを渡す方法、いつどこで渡されたかは明確ではありません。作成した。Context は抽象クラスであり、その実装クラスは contextImpl です。
- contextImpl.getSystemService()。このステップは処理のために SystemServiceRegistry に転送されるだけです
return SystemServiceRegistry.getSystemService(this, name);```
- SystemServiceRegistry.getSystemService()。ここでは、 ServiceFetcherと呼ばれるものが、サービス名に基づいて hashMap SYSTEM_SERVICE_FETCHERSから取得され、フェッチャーがサービス インスタンスを取得する役割を果たします。
ServiceFetcher はインターフェースであり、このインターフェースを実装するには getService 関数を実装する必要があります。
static abstract interface ServiceFetcher<T> {
T getService(ContextImpl ctx);
}
このハッシュマップに関して、SYSTEM_SERVICE_FETCHERS は、SystemServiceRegistry がインスタンス化されるとき、つまりこのクラスの静的ステートメント ブロック内で初期化されます。このステップでは、多くのサービスを登録します。
static {
......
registerService(Context.AUDIO_SERVICE, AudioManager.class,
new CachedServiceFetcher<AudioManager>() {
@Override
public AudioManager createService(ContextImpl ctx) {
return new AudioManager(ctx);
}});
......
}
registerSerivce は、ハッシュマップにカプセル化された put 要素であり、サービス名と CachedServiceFetcher クラス オブジェクトをスローします。CachedServiceFetcher が ServiceFetcher を実装していると推測できます。
CachedServiceFetcher() {
// Note this class must be instantiated only by the static initializer of the
// outer class (SystemServiceRegistry), which already does the synchronization,
// so bare access to sServiceCacheSize is okay here.
mCacheIndex = sServiceCacheSize++;
}
これは、このクラスのコンストラクターであり、説明する必要があるため、キャッシュされたサービス インスタンス オブジェクトは、コンテキストのキャッシュされた配列に格納されます。もう 1 つの int 型の配列ゲートは、各キャッシュされたサービス インスタンス オブジェクトの現在のステータスを表すため、mCacheIndex は、格納されたキャッシュされた配列内でこのフェッチャーが指すサービス インスタンス オブジェクトのインデックス。コメントは、sServiceCacheSize をロックする必要がない理由を説明しています。
初期インスタンスのプロセスからわかるように、各サービス オブジェクトは遅延初期化されるため、(私の理解では) アクティビティの起動が高速化され、本当に必要な場合にのみ、不要なサービス オブジェクトのインスタンス化を回避できます。インスタンス化されます。
さらに、インスタンス化プロセスにはマルチスレッドの競合状態の処理も含まれており、これは (私のような初心者にとっては) 非常に巧妙に解決されます。