理解 Context.getSystemService 原理

我愛宋慧喬發表於2019-04-03

本人只是Android小菜一個,寫技術文章只是為了總結自己最近學習到的知識,從來不敢為人師,如果裡面有不正確的地方請大家盡情指出,謝謝!

本文基於原生 Android 9.0 原始碼來解析 Context.getSystemService 原理:

frameworks/base/core/java/android/app/Activity.java
frameworks/base/core/java/android/view/ContextThemeWrapper.java
frameworks/base/core/java/android/view/LayoutInflater.java
frameworks/base/core/java/android/content/Context.java
frameworks/base/core/java/android/app/ContextImpl.java
frameworks/base/core/java/android/app/SystemServiceRegistry.java
複製程式碼

1. 概述

Android系統為程式開發者提供了各種各樣的系統服務,以滿足在不同場景下的需求,例如:

  • LayoutInflater:把layout佈局檔案渲染成view控制元件物件;
  • DownloadManager:發起下載任務,從特定檔案來源中下載檔案;
  • CameraManager:呼叫系統攝像元件進行拍照錄影等功能。

這裡只列舉了幾個常用的系統服務,實際上現在Android框架中存在的系統服務要遠遠多於這些,基本涵蓋了所有的系統服務型別,同時Android框架為了幫助程式開發者更便捷地獲取和使用這些服務,提供了一個統一的介面Context.getSystemService(),通過這個介面,開發者可以很方便快捷地獲取想要的系統服務。

通過獲取LayoutInflater系統服務例項的過程來簡單瞭解下getSystemService()的基本使用方法,示例程式碼如下:

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 1. 通過 getSystemService 系統介面獲取 LayoutInflater 服務例項
        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        // 2. 使用獲取到的 LayoutInflater 服務來把佈局檔案渲染成控制元件物件
        View view = inflater.inflate(R.layout.view_merge, null);
    }
}
複製程式碼

簡直太簡單了! 只通過一行程式碼就獲取到了LayoutInflater例項,然後就可以“肆意”使用這個服務來完成自己想要的功能了。

知道這些就夠了嗎?當然不是,作為一個“有理想有抱負”的程式設計師,在知道一項技術的基本用法之後,接下來要做的就是了解其背後的原理和實現方式,爭取做到“知其然更知其所以然”。

本文將通過分析系統服務類LayoutInflater例項的具體獲取過程來講解Context.getSystemService()的實現原理,雖然其本身的邏輯並不複雜,仍希望能對感興趣但沒有時間檢視原始碼的同學有所幫助。

2. 理解 Context.getSystemService 原理

Android系統框架中提供的系統服務多種多樣,要學習和理解其實現原理,必須要以一個具體的系統服務為切入點,在這裡選擇LayoutInflater服務為入口,通過對其例項獲取過程的逐步分析來揭開Context.getSystemService()的神祕面紗。

2.1 LayoutInflater 獲取過程

前面已經給出獲取LayoutInflater服務例項的示例程式碼,如下:

// 在 Activity 中呼叫 getSystemService 獲取 LayoutInflater 服務物件。
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
複製程式碼

這裡通過Activity.getSystemService()獲取LayoutInflater服務例項,直接看這塊程式碼:

public Object getSystemService(@ServiceName @NonNull String name) {
    // 檢查 context 是否存在,只有在 Activity 呼叫了 onCreate 才存在。
    if (getBaseContext() == null) {
        throw new IllegalStateException(
                "System services not available to Activities before onCreate()");
    }

    // 針對 WINDOW_SERVICE 和 SEARCH_SERVICE 可用直接返回相關服務例項。
    if (WINDOW_SERVICE.equals(name)) {
        return mWindowManager;
    } else if (SEARCH_SERVICE.equals(name)) {
        ensureSearchManager();
        return mSearchManager;
    }
    // 返回其他型別的系統服務,LayoutInflater 會走到這裡。
    return super.getSystemService(name);
}
複製程式碼

Activity中返回了WINDOW_SERVICESEARCH_SERVICE兩種特殊型別的系統服務,其他型別的服務則繼續呼叫super.getSystemService()獲取,這裡的super指的是ContextThemeWrapper,轉到這裡繼續分析:

@Override
public Object getSystemService(String name) {
    // 在獲取 LayoutInflater 例項時傳入的名字就是 LAYOUT_INFLATER_SERVICE,此時會走到這裡。
    if (LAYOUT_INFLATER_SERVICE.equals(name)) {
        if (mInflater == null) {
            mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
        }
        return mInflater;
    }
    // 其他型別的系統服務繼續呼叫 getSystemService 來獲取服務例項。
    return getBaseContext().getSystemService(name);
}
複製程式碼

ContextThemeWrapper.getSystemService也沒有真正返回服務例項,而是繼續呼叫LayoutInflater.from():

/**
 * Obtains the LayoutInflater from the given context.
 */
public static LayoutInflater from(Context context) {
    // 呼叫 Context.getSystemService 獲取 LayoutInflater 例項。
    LayoutInflater LayoutInflater =
            (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    // 獲取失敗則丟擲異常。
    if (LayoutInflater == null) {
        throw new AssertionError("LayoutInflater not found.");
    }
    return LayoutInflater;
}
複製程式碼

轉了一圈,LayoutInflater的例項物件原來是通過其內部靜態方法from()呼叫Context.getSystemService()獲取到的,這個方法才是需要分析的核心。

2.2 解析 Context.getSystemService 原理

前面已經分析到LayoutInflater例項是通過Context.getSystemService獲取的,馬上來看看這個獲取過程:

 public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
複製程式碼

Context並沒有提供具體的實現,而是僅僅提供了一個抽象的getSystemService()介面,其具體實現應該在Context的實現類中。我們知道在Android系統中Context的實現類是ContextImpl,繼續來看ContextImpl.getSystemService():

/**
 * Common implementation of Context API, which provides the base
 * context object for Activity and other application components.
 */
class ContextImpl extends Context {
    // 省略無關程式碼
    
    // The system service cache for the system services that are cached per-ContextImpl.
    // 有些系統服務是需要進行快取的,這樣針對同一個 ContextImpl 例項就只需要建立服務一次。
    // 在建立之後把服務例項快取起來,以後同一個 ContextImpl 例項獲取服務時只需要從快取查詢即可。
    final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();

    // 服務狀態 - 還沒有初始化
    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;

    /**
     * Initialization state for each service. Any of {@link #STATE_UNINITIALIZED},
     * {@link #STATE_INITIALIZING} or {@link #STATE_READY},
     */
    @ServiceInitializationState
    // 儲存每個服務的初始化狀態
    final int[] mServiceInitializationStateArray = new int[mServiceCache.length];
    
    // 省略無關程式碼
    
    @Override
    public Object getSystemService(String name) {
        // 呼叫 SystemServieRegistry 獲取系統服務
        return SystemServiceRegistry.getSystemService(this, name);
    }
    // 省略無關程式碼
複製程式碼

分析到這裡,終於看到了和系統服務直接相關的變數和方法了,最終呼叫SystemServiceRegistry來建立系統服務例項,那這個SystemServiceRegistry又是如何完成系統服務的建立並返回的呢?

/**
 * Manages all of the system services that can be returned by {@link Context#getSystemService}.
 * Used by {@link ContextImpl}.
 */
final class SystemServiceRegistry {
    private static final String TAG = "SystemServiceRegistry";

    // Service registry information.
    // This information is never changed once static initialization has completed.
    // 儲存系統“服務實現類”和“服務描述名”的鍵值對,即 key=“服務實現類”,value=“服務描述名”。
    private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =
            new HashMap<Class<?>, String>();
    
    // 儲存系統服務的“描述名”和“獲取類”的鍵值對,即 key=“服務描述名”,value=“服務獲取類”。
    private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
            new HashMap<String, ServiceFetcher<?>>();
            
    // 快取類服務數量,用以提供服務在快取中的索引。
    private static int sServiceCacheSize;

    // Not instantiable.
    private SystemServiceRegistry() { }
    
    /**
     * Gets a system service from a given context.
     */
    public static Object getSystemService(ContextImpl ctx, String name) {
        // 根據“服務描述名”查詢“服務獲取類”
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        // 通過“服務獲取類”返回具體系統服務,在必要時負責建立服務物件。
        return fetcher != null ? fetcher.getService(ctx) : null;
    }
複製程式碼

SystemServiceRegistry.getSystemService()內部要先通過“服務描述名”查詢到“服務獲取類”,這裡之所以把 ServiceFetcher稱為“獲取類”,是因為它不僅負責返回對應的系統服務還要在必要的時候建立服務物件,可見ServiceFetcher類在系統服務機制中的重要作用:

/**
 * Base interface for classes that fetch services.
 * These objects must only be created during static initialization.
 */
static abstract interface ServiceFetcher<T> {
    // ServiceFetcher 介面中只提供了一個返回服務的介面。
    T getService(ContextImpl ctx);
}
複製程式碼

ServiceFetcher是一個介面類,實際在Android系統中有三個不同的實現類,用於建立不同型別的系統服務。

  • 快取類系統服務: 在建立的時候需要ContextImpl引數,並且會把建立的系統服務快取起來,這樣以後同一個ContextImpl再次獲取該類系統服務時不再建立直接返回快取中的服務物件即可,LayoutInflater屬於這類服務。
/**
 * Override this class when the system service constructor needs a
 * ContextImpl and should be cached and retained by that context.
 */
static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
    // 服務物件在快取中的索引
    private final int mCacheIndex;

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

    @Override
    @SuppressWarnings("unchecked")
    public final T getService(ContextImpl ctx) {
        // SystemServiceRegistry 裡的服務快取
        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];
                // 查詢服務快取時有兩種情況可以直接返回:
                // 1. service != null 表示該類服務已經被建立並儲存在服務快取裡,直接返回服務例項即可;
                // 2. service == null && gets[mCacheIndex] == ContextImpl.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.
                
                // 快取中沒有找到服務,開始建立服務。
                if (gates[mCacheIndex] == ContextImpl.STATE_READY) {
                    // 快取中沒有找到服務例項,但是其狀態是 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;
                    // 更新服務狀態為 STATE_INITIALIZING,表示接下來開始建立該系統服務。
                    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.
                    // 嘗試建立服務,並在建立成功後更新服務狀態為 STATE_READY.
                    service = createService(ctx);
                    newState = ContextImpl.STATE_READY;

                } catch (ServiceNotFoundException e) {
                    // 如果沒有找到服務,服務狀態仍然為 STATE_NOT_FOUND 並且服務例項是 null.
                    onServiceNotFound(e);
                } finally {
                    synchronized (cache) {
                        // 把服務例項和服務狀態儲存到快取中去,有兩種可能:
                        // 1. 服務建立成功時,把建立的服務例項和狀態 STATE_READY 分別快取起來;
                        // 2. 服務建立失敗時,把 null 和狀態 STATE_NOT_FOUND 分別快取起來。
                        // 在後續查詢快取的過程中,只要是這兩種情況就直接返回不再進行建立。
                        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;
                    }
                }
            }
        }
    }
    // 建立服務,需要具體的子類去實現,在建立過程中需要傳遞 ContextImpl 引數。
    public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
}
複製程式碼
  • 靜態類系統服務:在建立的時候不需要ContextImpl引數,是一個單例物件,可以跨程式使用,InputManager就屬於這一型別的系統服務。
/**
 * Override this class when the system service does not need a ContextImpl
 * and should be cached and retained process-wide.
 */
static abstract class StaticServiceFetcher<T> implements ServiceFetcher<T> {
    // 服務的單例物件
    private T mCachedInstance;

    @Override
    public final T getService(ContextImpl ctx) {
        // 使用單例模式建立服務物件,保證只存在一個服務物件。
        synchronized (StaticServiceFetcher.this) {
            if (mCachedInstance == null) {
                try {
                    mCachedInstance = createService();
                } catch (ServiceNotFoundException e) {
                    onServiceNotFound(e);
                }
            }
            return mCachedInstance;
        }
    }
    // 建立服務,需要具體的子類去實現,在建立過程中不需要 ContextImpl 引數。
    public abstract T createService() throws ServiceNotFoundException;
}
複製程式碼
  • 靜態應用類系統服務:在建立的時候使用Application Context,每個程式只有一個服務例項,目前在整個Android系統中只有ConnectivityManager屬於這類服務。
/**
 * Like StaticServiceFetcher, creates only one instance of the service per application, but when
 * creating the service for the first time, passes it the application context of the creating
 * application.
 *
 * TODO: Delete this once its only user (ConnectivityManager) is known to work well in the
 * case where multiple application components each have their own ConnectivityManager object.
 */
static abstract class StaticApplicationContextServiceFetcher<T> implements ServiceFetcher<T> {
    // 單例服務物件
    private T mCachedInstance;

    @Override
    public final T getService(ContextImpl ctx) {
        synchronized (StaticApplicationContextServiceFetcher.this) {
            if (mCachedInstance == null) {
                // 獲取對應程式的 context 資訊
                Context appContext = ctx.getApplicationContext();
                // 建立單例服務物件並返回
                try {
                    mCachedInstance = createService(appContext != null ? appContext : ctx);
                } catch (ServiceNotFoundException e) {
                    onServiceNotFound(e);
                }
            }
            return mCachedInstance;
        }
    }
    // 建立服務,需要具體的子類去實現,需要傳遞 Application Context 引數。
    public abstract T createService(Context applicationContext) throws ServiceNotFoundException;
}
複製程式碼

在瞭解Android框架中對系統服務的不同分類後,再回到之前的問題:SystemServiceRegistry.getSystemService()是如何返回具體服務例項的呢?

/**
 * Gets a system service from a given context.
 */
public static Object getSystemService(ContextImpl ctx, String name) {
    // 根據“服務描述名”查詢“服務獲取類”
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    // 通過“服務獲取類”返回具體系統服務,在必要時負責建立服務物件。
    return fetcher != null ? fetcher.getService(ctx) : null;
}
複製程式碼

可以看到在獲取服務例項過程中是根據傳入的name資訊去找到對應的ServiceFetcher物件,再由ServiceFetcher中的getService返回具體服務,getService針對不同型別的服務有不同的返回方式,這點在前面已經講到。

現在剩下的疑問就是SYSTEM_SERVICE_FETCHERS裡面的ServiceFetcher是什麼時候被新增進去的呢?如果你曾經看過SystemServiceRegistry的程式碼就會很容易發現這點,在其內部有一大段靜態程式碼塊,所有的ServiceFetcher都是在這裡註冊的:

static {
    // 省略其他程式碼
    
    // 註冊 ActivityManager 服務 - 快取類服務
    registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
            new CachedServiceFetcher<ActivityManager>() {
        @Override
        // 具體的建立方式
        public ActivityManager createService(ContextImpl ctx) {
            return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
        }});

    // 註冊 ConnectivityManager 服務 - 靜態應用類服務
    registerService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
            new StaticApplicationContextServiceFetcher<ConnectivityManager>() {
        @Override
        // 具體建立方式
        public ConnectivityManager createService(Context context) throws ServiceNotFoundException {
            IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
            IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
            return new ConnectivityManager(context, service);
        }});

    // 註冊 InputManager 服務 - 靜態類服務
    registerService(Context.INPUT_SERVICE, InputManager.class,
            new StaticServiceFetcher<InputManager>() {
        @Override
        // 具體建立方式
        public InputManager createService() {
            return InputManager.getInstance();
        }});


    // 註冊 LayoutInflater 服務
    registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
            new CachedServiceFetcher<LayoutInflater>() {
        @Override
        // 具體建立方式,返回的是 PhoneLayoutInflater 例項,它是 LayoutInflater 的實現類。
        public LayoutInflater createService(ContextImpl ctx) {
            return new PhoneLayoutInflater(ctx.getOuterContext());
        }});
}

/**
 * Statically registers a system service with the context.
 * This method must be called during static initialization only.
 */
private static <T> void registerService(String serviceName, Class<T> serviceClass,
        ServiceFetcher<T> serviceFetcher) {
    // 把“服務實現類”和“服務描述名”鍵值對存入 SYSTEM_SERVICE_NAMES 表。
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    // 把“服務描述名”和“服務獲取類”鍵值對存入 SYSTEM_SERVICE_FETCHER 表,
    // 後續就可以直接通過“服務描述名”來找到“服務獲取類”了,進而獲取具體的服務例項,getSystemService 就是如此。
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
複製程式碼

SystemServiceRegistry類被載入的時候,其靜態程式碼塊就會通過registerService把所有的系統服務都進行註冊,並把“服務實現類”-“服務描述名”鍵值對和“服務描述名”-“服務獲取類”鍵值對分別儲存,這樣後續通過getSystemService(name)查詢具體服務例項時,就可以直接通過“服務描述名”找到“服務獲取類”,進而獲取到服務例項。

實際上這段靜態程式碼非常長,裡面註冊了所有的系統服務,這裡只針對每一種服務型別顯示了一個具體服務的註冊過程,同時也顯示了一直關心的LayoutInflater例項的註冊方式,其返回的是PhoneLayoutInflater實現類。

自此,終於看到Context.getSystemService(name)根據服務描述名獲取系統服務例項的全過程,也對其中的實現方式和原理有了較深入的理解。

3. 總結

本文以LayoutInflater服務為切入點,逐步分析其例項的獲取過程,講解Context.getSystemService的整體流程和實現原理,並對系統服務的分類和具體建立方法進行了簡要說明,希望能對大家在系統服務方面有一定的幫忙。