和Jayce一起學(xué)習(xí) Jetpack -- startup的源碼解析

和Jayce一起學(xué)習(xí)Jetpack -- 開篇
Google 官方解釋
和Jayce一起學(xué)習(xí)Jetpack -- startup的用法

這次解析我們從 定義在 manifest中的 InitializationProvider 入手


@RestrictTo(RestrictTo.Scope.LIBRARY)
public final class InitializationProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        Context context = getContext();
        if (context != null) {
            AppInitializer.getInstance(context).discoverAndInitialize();
        } else {
            throw new StartupException("Context cannot be null");
        }
        return true;
    }

    @Nullable
    @Override
    public Cursor query(
            @NonNull Uri uri,
            @Nullable String[] projection,
            @Nullable String selection,
            @Nullable String[] selectionArgs,
            @Nullable String sortOrder) {
        throw new IllegalStateException("Not allowed.");
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        throw new IllegalStateException("Not allowed.");
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        throw new IllegalStateException("Not allowed.");
    }

    @Override
    public int delete(
            @NonNull Uri uri,
            @Nullable String selection,
            @Nullable String[] selectionArgs) {
        throw new IllegalStateException("Not allowed.");
    }

    @Override
    public int update(
            @NonNull Uri uri,
            @Nullable ContentValues values,
            @Nullable String selection,
            @Nullable String[] selectionArgs) {
        throw new IllegalStateException("Not allowed.");
    }
}

這個provider 也很簡單 關(guān)鍵的代碼都在 AppInitializer.getInstance(context).discoverAndInitialize(); 這一句。


    void discoverAndInitialize() {
        try {
            Trace.beginSection(SECTION_NAME);
            ComponentName provider = new ComponentName(mContext.getPackageName(),
                    InitializationProvider.class.getName());
            ProviderInfo providerInfo = mContext.getPackageManager()
                    .getProviderInfo(provider, GET_META_DATA);
            Bundle metadata = providerInfo.metaData;
            String startup = mContext.getString(R.string.androidx_startup);
            if (metadata != null) {
                Set<Class<?>> initializing = new HashSet<>();
                Set<String> keys = metadata.keySet();
                for (String key : keys) {
                    String value = metadata.getString(key, null);
                    if (startup.equals(value)) {
                        Class<?> clazz = Class.forName(key);
                        if (Initializer.class.isAssignableFrom(clazz)) {
                            Class<? extends Initializer<?>> component =
                                    (Class<? extends Initializer<?>>) clazz;
                            mDiscovered.add(component);
                            if (StartupLogger.DEBUG) {
                                StartupLogger.i(String.format("Discovered %s", key));
                            }
                            doInitialize(component, initializing);
                        }
                    }
                }
            }
        } catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) {
            throw new StartupException(exception);
        } finally {
            Trace.endSection();
        }
    }
  • 這個方法,首先第一步 獲取定義在 provider 里面的 metadata。
  • 第二步就是便利 metadata 獲取所有value為 R.string.androidx_startup 的key值狮荔。
  • 第三步 通過name 獲取class
  • 執(zhí)行 doInitialize(component, initializing);
<T> T doInitialize(
            @NonNull Class<? extends Initializer<?>> component,
            @NonNull Set<Class<?>> initializing) {
        synchronized (sLock) {
            boolean isTracingEnabled = Trace.isEnabled();
            try {
                if (isTracingEnabled) {
                    // Use the simpleName here because section names would get too big otherwise.
                    Trace.beginSection(component.getSimpleName());
                }
                if (initializing.contains(component)) {
                    String message = String.format(
                            "Cannot initialize %s. Cycle detected.", component.getName()
                    );
                    throw new IllegalStateException(message);
                }
                Object result;
                if (!mInitialized.containsKey(component)) {
                    initializing.add(component);
                    try {
                        Object instance = component.getDeclaredConstructor().newInstance();
                        Initializer<?> initializer = (Initializer<?>) instance;
                        List<Class<? extends Initializer<?>>> dependencies =
                                initializer.dependencies();

                        if (!dependencies.isEmpty()) {
                            for (Class<? extends Initializer<?>> clazz : dependencies) {
                                if (!mInitialized.containsKey(clazz)) {
                                    doInitialize(clazz, initializing);
                                }
                            }
                        }
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initializing %s", component.getName()));
                        }
                        result = initializer.create(mContext);
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initialized %s", component.getName()));
                        }
                        initializing.remove(component);
                        mInitialized.put(component, result);
                    } catch (Throwable throwable) {
                        throw new StartupException(throwable);
                    }
                } else {
                    result = mInitialized.get(component);
                }
                return (T) result;
            } finally {
                Trace.endSection();
            }
        }
    }

從這個方法中可以看到這里面的邏輯是

  • 判斷是否有依賴,如果有首先創(chuàng)建依賴介粘,并且調(diào)用create
  • 如果沒有則創(chuàng)建當前的實例,并且調(diào)用create
  • 判斷去重晚树,避免重復(fù)創(chuàng)建姻采。

不自動加載組件的邏輯

AppInitializer.getInstance(context)
    .initializeComponent(ExampleLoggerInitializer.class);

######

  public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
        return doInitialize(component, new HashSet<Class<?>>());
    }

這里的邏輯就是不自動沖xml中讀取自動創(chuàng)建, 改為手動調(diào)用doInitialize 爵憎,內(nèi)部邏輯是一樣慨亲。
是不是很簡單。

總結(jié)

結(jié)合content provider的特性宝鼓,我們很容易可以知道 這些創(chuàng)建的組件調(diào)用u會在 application 的oncreatae之前調(diào)用刑棵,但是content provider的oncreate 也是在主線程調(diào)用,還是會占用主線程的資源愚铡。
綜合來說蛉签,startup還是以解偶 啟動流程,規(guī)范化啟動流程為主沥寥。而獨立模塊化的啟動組件碍舍,也方便各個擊破地提高我們的啟動速度。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末邑雅,一起剝皮案震驚了整個濱河市片橡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌淮野,老刑警劉巖捧书,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異骤星,居然都是意外死亡经瓷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門妈踊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來了嚎,“玉大人,你說我怎么就攤上這事⊥嵊荆” “怎么了萝勤?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長呐伞。 經(jīng)常有香客問我敌卓,道長,這世上最難降的妖魔是什么伶氢? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任趟径,我火速辦了婚禮,結(jié)果婚禮上癣防,老公的妹妹穿的比我還像新娘蜗巧。我一直安慰自己,他們只是感情好蕾盯,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布幕屹。 她就那樣靜靜地躺著,像睡著了一般级遭。 火紅的嫁衣襯著肌膚如雪望拖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天挫鸽,我揣著相機與錄音说敏,去河邊找鬼。 笑死丢郊,一個胖子當著我的面吹牛盔沫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蚂夕,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼迅诬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了婿牍?” 一聲冷哼從身側(cè)響起侈贷,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎等脂,沒想到半個月后俏蛮,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡上遥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年搏屑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粉楚。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡辣恋,死狀恐怖亮垫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伟骨,我是刑警寧澤饮潦,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站携狭,受9級特大地震影響继蜡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜逛腿,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一稀并、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧单默,春花似錦碘举、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至枚抵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間明场,已是汗流浹背汽摹。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留苦锨,地道東北人逼泣。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像舟舒,于是被迫代替她去往敵國和親拉庶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359