追蹤 getSystemService

寫過 Android 的同學(xué)都應(yīng)該調(diào)用過這個函數(shù) getSystemService(...)好乐,比如:

// 獲取電話服務(wù)
TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
// 獲取網(wǎng)絡(luò)連接的服務(wù)
ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
// 獲取窗口服務(wù)
WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);

getSystemService(serviceName) 用于獲取系統(tǒng)服務(wù)瘾蛋,今天我們就從源碼的角度來分析一下 getSystemService 背后的邏輯是怎樣的搁拙。
按住 Ctrl + 鼠標左鍵掺炭,立即就能定位到 getSystemService 來自 ContextWrapper

// android.content.ContextWrapper
@Override
public Object getSystemService(String name) {
    return mBase.getSystemService(name);
}

那么 mBase 是什么辫诅?它在哪里初始化的呢?

// android.content.ContextWrapper
Context mBase;
...
public ContextWrapper(Context base) {
    mBase = base;
}
...
protected void attachBaseContext(Context base) {
   if (mBase != null) {
        throw new IllegalStateException("Base context already set");
    }
    mBase = base;
}

構(gòu)造函數(shù)attachBaseContext(...) 可以初始化 mBase涧狮。
但無論是 ContextWrapper 的子類 Service炕矮,還是 ContextThemeWrapper (Activity 的父類),它們的構(gòu)造函數(shù)都沒有給 mBase 賦值:

// android.view.ContextThemeWrapper
public ContextThemeWrapper() {
    super(null);
}
// android.app.Service
public Service() {
    super(null);
}

看來一定是從 attachBaseContext(...) 來初始化 mBase 的了勋篓。
在 Service.java 和 Activity.java 中吧享,我們分別找到了 ?attachBaseContext(...) 的調(diào)用處:

// android.app.Activity
final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
        attachBaseContext(context);
        ...
}

// android.app.Service
public final void attach(Context context, ActivityThread thread, String className, IBinder token, Application application, Object activityManager) {
    attachBaseContext(context);
    ...
}

原來 attachBaseContext(...) 的參數(shù) context 來自 attach() 函數(shù)。
要了解 attach()函數(shù)是何時被調(diào)用的譬嚣,就必須了解 Framework 初始化 Service 和 Activity 的流程钢颂,通過查看 Android FrameWork 源碼,我們定位到這里:

// android.app.ActivitThread
// --- activity ---
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    Application app = r.packageInfo.makeApplication(false, mInstrumentation);
    if (activity != null) {
        // 創(chuàng)建 context
        Context appContext = createBaseContextForActivity(r, activity);
        CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
        Configuration config = new Configuration(mCompatConfiguration);
        // attach(...)
        activity.attach(appContext, this, getInstrumentation(), r.token,
                r.ident, app, r.intent, r.activityInfo, title, r.parent,
                r.embeddedID, r.lastNonConfigurationInstances, config,
                r.referrer, r.voiceInteractor);
        ...
    }
    ...
}
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
    int displayId = Display.DEFAULT_DISPLAY;
    ...
    // 創(chuàng)建 context
    ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, displayId, r.overrideConfig);
    appContext.setOuterContext(activity);
    Context baseContext = appContext;
    ...
}

// --- service ----
private void handleCreateService(CreateServiceData data) {
    LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = (Service) cl.loadClass(data.info.name).newInstance();
    } catch (Exception e) {
        if (!mInstrumentation.onException(service, e)) {
            throw ...
        }
    }

    // 創(chuàng)建 context
    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
    context.setOuterContext(service);

    Application app = packageInfo.makeApplication(false, mInstrumentation);
    // attach(...)
    service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault());
    service.onCreate();
    ...
}

到此拜银,mBase 找到了殊鞭,原來是 ContextImpl。
接下來就應(yīng)該到 ContextImpl 中查看 getSystemService(...) 的實現(xiàn)了:

// android.app.ContextImpl
@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}

繼續(xù)查看 SystemServiceRegistry

// android.app.SystemServiceRegistry
public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS = new HashMap<String, ServiceFetcher<?>>();

可以看到尼桶,getSystemServer(...) 最終會到 SYSTEM_SERVICE_FETCHERS 這個 HashMap 中去獲取系統(tǒng)服務(wù)操灿,我們只要找到這個 HashMap 的數(shù)據(jù)來源即可:

private static <T> void registerService(String serviceName, Class<T> serviceClass,
            ServiceFetcher<T> serviceFetcher) {
    ...
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}

原來就是 registerService(...) 是用來初始化 HashMap 的函數(shù),很快我們就能在文件中定位到調(diào)用 registerService() 函數(shù)的靜態(tài)方法塊:

static{
    ...
    registerService(Context.WINDOW_SERVICE, WindowManager.class,
        new CachedServiceFetcher<WindowManager>() {
        @Override
        public WindowManager createService(ContextImpl ctx) {
            return new WindowManagerImpl(ctx.getDisplay());
        }});
    ...
    registerService(Context.BLUETOOTH_SERVICE, BluetoothManager.class,
        new CachedServiceFetcher<BluetoothManager>() {
        @Override
        public BluetoothManager createService(ContextImpl ctx) {
            return new BluetoothManager(ctx);
    }});
}

到此泵督,getSystemService(...) 分析結(jié)束趾盐。

我們來總結(jié)一下:
1、Context 的實現(xiàn)類為 android.app.ContextImpl小腊;
2救鲤、系統(tǒng)服務(wù)緩存在一個 android.app.SystemServiceRegistry 的一個 HashMap 中;

全文完秩冈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末本缠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子入问,更是在濱河造成了極大的恐慌丹锹,老刑警劉巖稀颁,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異楣黍,居然都是意外死亡匾灶,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門锡凝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粘昨,“玉大人,你說我怎么就攤上這事窜锯≌派觯” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵锚扎,是天一觀的道長吞瞪。 經(jīng)常有香客問我,道長驾孔,這世上最難降的妖魔是什么芍秆? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮翠勉,結(jié)果婚禮上妖啥,老公的妹妹穿的比我還像新娘。我一直安慰自己对碌,他們只是感情好荆虱,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著朽们,像睡著了一般怀读。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上骑脱,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天菜枷,我揣著相機與錄音,去河邊找鬼叁丧。 笑死啤誊,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的拥娄。 我是一名探鬼主播坷衍,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼条舔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起乏矾,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤孟抗,失蹤者是張志新(化名)和其女友劉穎迁杨,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凄硼,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡铅协,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了摊沉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狐史。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖说墨,靈堂內(nèi)的尸體忽然破棺而出骏全,到底是詐尸還是另有隱情,我是刑警寧澤尼斧,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布姜贡,位于F島的核電站,受9級特大地震影響棺棵,放射性物質(zhì)發(fā)生泄漏楼咳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一烛恤、第九天 我趴在偏房一處隱蔽的房頂上張望母怜。 院中可真熱鬧,春花似錦缚柏、人聲如沸苹熏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柜裸。三九已至,卻和暖如春粱锐,著一層夾襖步出監(jiān)牢的瞬間疙挺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工怜浅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留铐然,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓恶座,卻偏偏與公主長得像搀暑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子跨琳,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容