Android getSystemService principle

Preface

  • GetSystemService will be referred to as GSS below.

Why do I need to understand the principle of getSystemService?
When analyzing the source code, I mistakenly thought that GSS was implemented based on binder, but I found that the printed log content was different from what I imagined. Logically speaking, audioService should return the obtained pid of audioManager when calling Binder.getCallingPid(), because It is the audioService called from audioManager through binder. But the actual printing was indeed applied by the upper layer, and then I saw that the constructor of AudioManager had a Context parameter, and later I realized that it was meaningless. GSS returned an instance class object, not a binder of an independent process.

Let me give you a conclusion first. For system services of the cache type (in most cases), the instance object will be returned according to the service name through map. The object is actually created when it is obtained for the first time, and the cache will be returned for subsequent acquisitions. What really does this work is SystemServiceRegistry.java, where the process of handling multi-threaded concurrent acquisition of service instances is very subtle.

        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;
                        }
                    }
                }
            }
        }

The call chain is:
first, mainActivity calls the GSS of the inherited parent class Activity.

  1. Activity.getSystemService(), this step filters the service name WINDOW_SERVICE or SEARCH_SERVICE . These two special services can be returned directly.
if (WINDOW_SERVICE.equals(name)) {
    
    
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
    
    
            ensureSearchManager();
            return mSearchManager;
        }
  1. ContextThemeWrapper.getSystemService(), this step filters out the service LAYOUT_INFLATER_SERVICE . Its processing is actually similar to the next step, but the returned service will call cloneInContext(this) again. I don’t understand why this service needs special processing.
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);

The next step will be to call the saved mBase. This parameter is passed when the ContextThemeWrapper is constructed. It is estimated that it is created when the activity is created, but it is not clear how to pass the context, where and when it was created. Context is an abstract class, and its implementation class is contextImpl.

  1. contextImpl.getSystemService(). This step is just transferred to SystemServiceRegistry for processing
return SystemServiceRegistry.getSystemService(this, name);```
  1. SystemServiceRegistry.getSystemService(). Here, something called ServiceFetcher will be retrieved from hashMap SYSTEM_SERVICE_FETCHERS based on the service name , and then the fetcher will be responsible for obtaining the service instance.

ServiceFetcher is an interface. To implement this interface, you need to implement the getService function.

static abstract interface ServiceFetcher<T> {
    
    
        T getService(ContextImpl ctx);
    }

Regarding this hashmap, SYSTEM_SERVICE_FETCHERS is initialized when SystemServiceRegistry is instantiated, that is, in the static statement block of this class. This step registers many services.

static {
    
    
	......
	registerService(Context.AUDIO_SERVICE, AudioManager.class,
                new CachedServiceFetcher<AudioManager>() {
    
    
            @Override
            public AudioManager createService(ContextImpl ctx) {
    
    
                return new AudioManager(ctx);
            }});
    ......
}

registerSerivce is just a put element encapsulated in a hashmap, throwing in the service name and a CachedServiceFetcher class object. You can guess that CachedServiceFetcher implements 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++;
        }

This is the constructor of this class and needs to be explained, so the cached service instance object is stored in the cached array of context. The other int type array gates represents the current status of each cached service instance object, so mCacheIndex is The stored index of the service instance object pointed to by this fetcher in the cached array. The comments explain why there is no need to lock sServiceCacheSize.

As can be seen from the initial instance process, each service object is lazy initialized, so it can speed up the startup of the activity (my understanding), and avoid instantiating some service objects that are not needed, only when they are really needed. will be instantiated.
In addition, the instantiation process also involves the handling of multi-threaded race conditions, which is solved very cleverly (for a novice like me).

Guess you like

Origin blog.csdn.net/weixin_45719581/article/details/132106715