從App啟動理解ContentProvider的創(chuàng)建

ActivityThread.main

我們知道app的啟動是從ActivityThread.main方法開始的,所以我們先從main看起

    public static void main(String[] args) {
     ...
    //創(chuàng)建Looper
    Looper.prepareMainLooper();
    //創(chuàng)建ActivityThread
    ActivityThread thread = new ActivityThread();
    thread.attach(false);

   if (sMainThreadHandler == null) {
          sMainThreadHandler = thread.getHandler();
     }
    ...
    //開啟Looper循環(huán)
    Looper.loop();
    ...
    }

main方法里主要做了三件事

  • 創(chuàng)建主線程Looper,并開啟循環(huán)
  • 創(chuàng)建ActivityThread
  • 調(diào)用attach方法

ActivityThread.attach

接下來我們看ActivityThread.attach方法

private void attach(boolean system) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if (!system) {
        ...
        final IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            ...
        }
        ...
    }
    ...
}

attach方法里主要做里就是調(diào)用ActivityManager.getService() 方法返回IActivityManager 類型的 Binder 對象, 具體實現(xiàn)是在AMS里實現(xiàn)attachApplication

ActivityManagerService.attachApplication

public final void attachApplication(IApplicationThread thread) {
    ...
    attachApplicationLocked(thread, callingPid);
    ...
}

private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid) {
    ....
    thread.bindApplication(processName, appInfo, providers,
            app.instr.mClass,
            profilerInfo, app.instr.mArguments,
            app.instr.mWatcher,
            app.instr.mUiAutomationConnection, testMode,
            mBinderTransactionTrackingEnabled, enableTrackAllocation,
            isRestrictedBackupMode || !normalMode, app.persistent,
            new Configuration(getGlobalConfiguration()), app.compat,
            getCommonServicesLocked(app.isolated),
            mCoreSettingsObserver.getCoreSettingsLocked(),
            buildSerial);
    ...
    if (normalMode) {
        try {
            if (mStackSupervisor.attachApplicationLocked(app)) {
                didSomething = true;
            }
        } catch (Exception e) {
            ...
        }
    }
    ...
}

可以發(fā)現(xiàn)attachApplication實際調(diào)用了attachApplicationLocked,而在attachApplicationLocked里又通過IApplicationThread (其類型是ApplicationThreadActivityThread的內(nèi)部類梦重,繼承自IApplicationThread.Stub) 的bindApplication方法

ActivityThread.bindApplication

public final void bindApplication(String processName, ApplicationInfo appInfo,
        List<ProviderInfo> providers, ComponentName instrumentationName,
        ProfilerInfo profilerInfo, Bundle instrumentationArgs,
        IInstrumentationWatcher instrumentationWatcher,
        IUiAutomationConnection instrumentationUiConnection, int debugMode,
        boolean enableBinderTracking, boolean trackAllocation,
        boolean isRestrictedBackupMode, boolean persistent, Configuration config,
        CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
        String buildSerial) {
    ...
    sendMessage(H.BIND_APPLICATION, data);
}
private void sendMessage(int what, Object obj) {
    sendMessage(what, obj, 0, 0, false);
}
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    if (DEBUG_MESSAGES) Slog.v(
        TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
        + ": " + arg1 + " / " + obj);
    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);
}

bindApplication最終調(diào)用了mH.sendMessage方法仑乌,因為AMS通過ApplicationThread回調(diào)到我們的進程百拓,這也是一次跨進程過程,而ApplicationThread就是一個binder晰甚,回調(diào)邏輯是在binder線程池中完成的衙传,所以需要通過Handler 將其切換到ui線程.所以這里調(diào)用mh.sendMessage(mhActivityThread 的內(nèi)部類 H 的一個實例)來通知處理BIND_APPLICATION事件

H.handleMessage

public void handleMessage(Message msg) {
    ...
    switch (msg.what) {
        ...
        case BIND_APPLICATION:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
            AppBindData data = (AppBindData)msg.obj;
            handleBindApplication(data);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;
        ...
    }
}

可以看到Handler.H收到BIND_APPLICATION事件后實際調(diào)用來handleBindApplication方法來完成Application的創(chuàng)建以及ContentProvider的創(chuàng)建

ActivityThread.handleBindApplication

 private void handleBindApplication(AppBindData data) {
     ....
     final InstrumentationInfo ii;
     ....
     
    if (ii != null) {
       //1.創(chuàng)建ContentImpl
       final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);

            try {
                final ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            } catch (Exception e) {
                throw new RuntimeException(
                    "Unable to instantiate instrumentation "
                    + data.instrumentationName + ": " + e.toString(), e);
            }

        //2.創(chuàng)建Instrumentation
      final ComponentName component = new ComponentName(ii.packageName, ii.name);
            mInstrumentation.init(this, instrContext, appContext, component,
                    data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
        ....
    
        //3.創(chuàng)建Application對象
         Application app;
         app = data.info.makeApplication(data.restrictedBackupMode, null);

         // Propagate autofill compat state
            app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);

            mInitialApplication = app;

        ...
        //4.啟動當前進程中的ContentProvider和調(diào)用其onCreate方法

        if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }

        //5.調(diào)用Application的onCreate方法
        try {
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                      "Unable to create application " + app.getClass().getName()
                      + ": " + e.toString(), e);
                }
            }
    }
 }
  • 3.1 LoadedApk.makeApplication Application的創(chuàng)建
public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
        ...

        Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }
        try {
            java.lang.ClassLoader cl = getClassLoader();
            if (!mPackageName.equals("android")) {
                initializeJavaContextClassLoader();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
            if (!mActivityThread.mInstrumentation.onException(app, e)) {
                throw new RuntimeException(
                    "Unable to instantiate application " + appClass
                    + ": " + e.toString(), e);
            }
        }
        mActivityThread.mAllApplications.add(app);
        mApplication = app;

        if (instrumentation != null) {
            try {
                instrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!instrumentation.onException(app, e)) {
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
        }

        ...

        return app;
}
  • 3.1.1 Instrumentation.newApplication
    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);
        app.attach(context);
        return app;
    }

AppComponentFactory.instantiateApplication

    public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
            @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (Application) cl.loadClass(className).newInstance();
    }

經(jīng)過 上面的5個步驟就完成了Application的創(chuàng)建和啟動,可以看到在第5步調(diào)用ApplicationonCreate方法之前压汪,第4步進行了ContentProvicer的創(chuàng)建和調(diào)用其onCreate,這里就解釋到了為什么可以在ContentProvicer.onCreate里初始化sdk,下面介紹下ContentProvicer的創(chuàng)建和調(diào)用其onCreate

  • 4.1 ActivityThread.installContentProviders
    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        for (ProviderInfo cpi : providers) {
        
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

installContentProviders完成了ContentProvider的啟動粪牲,它首先遍歷當前進程的ProviderInfo列表并一一調(diào)用其installProvder方法來啟動它,接著將已啟動的ContentProvider發(fā)布到AMS上止剖,AMS會把它們存在ProviderMap里腺阳,這樣外部調(diào)用者就可以直接從AMS中獲取到ContentProvider

  • 4.1.1 ActivityThread.installProvider
    private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ....

        final java.lang.ClassLoader cl = c.getClassLoader();
        LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
        if (packageInfo == null) {
            packageInfo = getSystemContext().mPackageInfo;
        }
        localProvider = packageInfo.getAppFactory()
                .instantiateProvider(cl, info.name);
        provider = localProvider.getIContentProvider();
        if (provider == null) {
            Slog.e(TAG, "Failed to instantiate class " +
                          info.name + " from sourceDir " +
                          info.applicationInfo.sourceDir);
            return null;
        }

         localProvider.attachInfo(c, info);

        ....
}

這里通過類加載器完成了ContentProvider的創(chuàng)建

  • 4.1.1.1 ContentProvider.attachInfo
    private void attachInfo(Context context, ProviderInfo info, boolean testing) {
        mNoPerms = testing;

        /*
         * Only allow it to be set once, so after the content service gives
         * this to us clients can't change it.
         */
        if (mContext == null) {
            mContext = context;
            if (context != null) {
                mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
                        Context.APP_OPS_SERVICE);
            }
            mMyUid = Process.myUid();
            if (info != null) {
                setReadPermission(info.readPermission);
                setWritePermission(info.writePermission);
                setPathPermissions(info.pathPermissions);
                mExported = info.exported;
                mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
                setAuthorities(info.authority);
            }
            ContentProvider.this.onCreate();
        }
    }

installProvider方法里除了完成了ContentProvider的創(chuàng)建還掉用了attachInfo,在改方法里來調(diào)用它的onCreate方法。到此位置ContentProvider的創(chuàng)建已經(jīng)完成穿香,并且它的onCreate也被調(diào)用

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末亭引,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子皮获,更是在濱河造成了極大的恐慌焙蚓,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洒宝,死亡現(xiàn)場離奇詭異购公,居然都是意外死亡,警方通過查閱死者的電腦和手機雁歌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門宏浩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人靠瞎,你說我怎么就攤上這事比庄∏竺茫” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵佳窑,是天一觀的道長制恍。 經(jīng)常有香客問我,道長神凑,這世上最難降的妖魔是什么净神? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮耙厚,結果婚禮上强挫,老公的妹妹穿的比我還像新娘。我一直安慰自己薛躬,他們只是感情好俯渤,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著型宝,像睡著了一般八匠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上趴酣,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天梨树,我揣著相機與錄音,去河邊找鬼岖寞。 笑死抡四,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的仗谆。 我是一名探鬼主播指巡,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼隶垮!你這毒婦竟也來了藻雪?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤狸吞,失蹤者是張志新(化名)和其女友劉穎勉耀,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹋偏,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡便斥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了威始。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片椭住。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖字逗,靈堂內(nèi)的尸體忽然破棺而出京郑,到底是詐尸還是另有隱情,我是刑警寧澤葫掉,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布些举,位于F島的核電站,受9級特大地震影響俭厚,放射性物質(zhì)發(fā)生泄漏户魏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一挪挤、第九天 我趴在偏房一處隱蔽的房頂上張望叼丑。 院中可真熱鬧,春花似錦扛门、人聲如沸鸠信。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽星立。三九已至,卻和暖如春葬凳,著一層夾襖步出監(jiān)牢的瞬間绰垂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工火焰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留劲装,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓昌简,卻偏偏與公主長得像占业,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子江场,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355