天天看點

Activity的啟動流程Activity的啟動流程

Activity的啟動流程

假設MainActivity要調用系統安裝器(這裡暫時就叫InstallerActivity),那麼這個流程大概可以分為以下幾步:

startActivity的流程

1.MainActivity向ActivityManagerService(以下簡稱AMS)送出請求,告訴AMS說我要啟動InstallerActivity

2.AMS接收到請求之後,先把請求記錄下來,咱先處理好MainActivity這家夥再說,于是告訴MainActivity說,好,我知道了,你可以去準備休息了

3.MainActivity接收到AMS讓它休息的資訊後,準備好被子枕頭之後,告訴AMS說,我開始休息了,你不要吵我了。

4.AMS知道MainActivity開始休息之後,終于可以全身心的去處理InstallerActivity的事情了,于是再次把之前的請求記錄取出來,找一下看InstallerActivity是屬于哪個程序的

5.如果InstallerAcivity所屬的程序沒有啟動,那麼AMS會找房屋管理者zygote程序,讓咱們的房屋管路員給InstallerActivity配置設定一套配備了基本家具家電的房子。即讓zygote孵化了一個程序準備給InstallerActivity使用,但是這個時候程序還沒有正在屬于InstallerActivity。

6.當zygote程序孵化出一個程序之後,程序就發送資訊告訴AMS說房子已經準備好啦。

7.AMS收到資訊後,把InstallerActivity的所有資訊,包括它的祖籍,身份證等資訊,發送給房子,告訴房子說,以後InstallerActivity這位美女就住你這裡了,她的所有資訊我都給你了,你快點去找到她,而且你要記住她的身份證哦,以後我可是還會通過身份證找她的哦。

8.房子收到資訊後,隻能默默的把InstallerActivity的身份證記下來,然後通過InstallerActivity的祖籍和名字找到這位美女,并且為這位美女做好登記啥的工作,于是從此以後InstallerActivity就在這個房子裡愉快的住下來啦,至此,InstallerActivity正式啟動完畢。

接下來進一步分析一下每一個步驟中比較關鍵的一些點:

第一步:

MainActivity是怎麼通知AMS的呢?我們都知道調用startActivity來啟動一個Activity,而後又會調用到startActivityForResult(intent, -1),requestCode為-1表示的是MainActivity不關心InstallerActivity是否被正常啟動了。經過一些重載的方法調用之後,終于現出了真身:
           
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
           

繼續跟進Instrumentation類的execStartActivity方法,最終終于發現是在下面這裡向AMS發出資訊的:

int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, , null, options);
           

這裡是個很關鍵的點,app跟AMS的通信是通過Binder來完成的,那麼既然是MainActivity要跟AMS通信,那麼MainActivity自然就是用戶端,AMS是服務端,既然是Binder用戶端,那麼必然有個proxy代理對象來為它處理一些Binder通信中的手續。果不其然,ActivityManagerNative就是一個Binder類,而它的getDefault從單例gDefault中取出了一個IActivityManager接口的對象,顯然的,在用戶端中這個實作了IActivityManager接口的對象必然就是Binder中的代理類了,即是ActivityManagerProxy,它隻是封裝了使用Binder的一些過程,而接口的具體業務邏輯的實作是在服務端中,服務端也會有一個Binder類,它裡面的Stub變量指向的即是接口的具體實作。這是Binder的一些常識,這裡提一下後面就不再說了。好,到這裡,MainActivity最終通過Binder,把資訊發送出去給AMS了。等等,好像漏了什麼重要的東西,我們往回看MainActivity發送給AMS的資訊中都包含了什麼重要的東西。從mInstrumentation.execStartActivity的參數中分析,最重要的其實是這三個東西, mMainThread.getApplicationThread(), mToken和intent,intent是包含了InstallerActivity資訊的類自不必多說,主要是看看mMainThread.getApplicationThread(), mToken是何方神聖。

可以看到,mMainThread是ActivityThread的變量,而ActivityThread就是我們常說的主線程(UI線程)。它的入口是main()方法,在main()方法裡面建立了主線程的Handler,并且開啟了主線程的Looper。

getApplicationThread()傳回的是一個ApplicationThread對象,它是一個Binder對象,ApplicationThread對象會傳遞給AMS,AMS通過這個Binder對象來傳遞資訊給用戶端。

而mToken是一個IBinder對象,它其實就是MainActivity的身份證,ActivityThread中有一個存有目前已打開的Activity的Map叫做mActivities,而這個Map中的key值就是這個mToken對象,AMS中也有一個儲存了目前運作中的Acitivity的Map,它的Map的key也是這個mToken對象。當AMS需要通知MainActivity執行生命周期的時候,它不是直接跟MainActivity聯系的,而是把資訊通過ApplicationThread發送給App,App再根據接收到的資訊,同時以傳送過來的mToken為key,從mActivities這個Map中找到對應身份的Activity,然後回調Activity的生命周期方法。Activity無論是在AMS還是在ActivityThread中,它的資料結構都不是以Activity對象來存儲的,在AMS中是以ActivityRecord對象來存儲,而在ActivityThread中是以ActivityClientRecord對象來存儲。

第二步

第二步是在framework中執行的,其他的細節對應用開發程式員來說不需要知道太多,工作中使用場景不多,但是有一點要注意,通常如果我們打開一個Activity而這個Activity沒有在AndroidManifest中注冊的話,會抛出一個Activity Not Find的異常,而檢查Activity是否有在App的AndroidManifest中注冊的邏輯就是在這一步中,通過PMS(PackageManagerService)來校驗的,這一點對于插件化來說是一個關鍵知識點。

第三步,第四步

第三步中,AMS把MainActivity的資訊封裝為ActivityRecord對象,通過ApplicationThread這個Binder對象把資訊發送App,App接收到資訊後,把Activity的資訊封裝成ActivityClientRecord對象,并取出的token變量,它是一個IBinder對象,其實就是第一步文中所說的mToken變量,然後App以這個token變量為key從mActivities中找到對應的Activity,并執行生命周期回調。

第四步中又回到了Framework中,按下不提。

第五步,第六步

這裡的知識點對應用開發工程師來說使用場景也不多,但是有兩點比較重要:

1.AMS和zygote的通信是通過socket來進行的,為啥這裡使用socket不是Binder呢?具體我也不知道,但是其中一個比較可信的原因應該是因為AMS通知zygote孵化一個新的程序是一個很簡單的通信,通信過程中不需要發送複雜的資料,是以用socket完全滿足需求。這裡正對應了上面所說的“但是這個時候程序還沒有正在屬于InstallerActivity”,其實這個時候AMS根本沒有把過多的資訊告訴zygote,zygote也不知道這個新程序到底是哪個App使用的,那麼這個新程序是怎麼和App綁在一起的呢?這個後面有說,這裡也是插件化的關鍵知識點之一。

2.Android的SDK的代碼和Java虛拟機在zygote程序起來的時候就已經初始化了,這意味着什麼呢?我們知道新程序是通過zygote程序fork出來的,而fork程序的時候,可以選擇讓fork出來的子程序繼承父程序,也就是說,當新程序被fork出來的時候,Android的SDK代碼和Java虛拟機已經都被初始化好了,是以,我們開發App的時候,即使在新開的程序裡沒做啥業務邏輯,但是這個程序總有一些資源開銷是必須的,就比如AndroidSDK的代碼和Java虛拟機的開銷。

第七步,第八步

這裡就可以解釋zygote孵化出來的新程序是怎麼跟App綁定起來的了,這裡也是插件化的一個重要知識點。AMS把需要被啟動的Activity的資訊通過ApplicationThread發送給程序,即調用了ApplicationThread的scheduleLaunchActivity方法:

public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.referrer = referrer;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profilerInfo = profilerInfo;

            r.overrideConfig = overrideConfig;
            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r);
           

scheduleLaunchActivity方法中把傳遞過來的資訊封裝成一個ActivityClientRecord對象,而後通過一個名為mH的Handler對象把操作從Binder線程池轉移到主線程中,建立Activity的邏輯在handleMessage方法裡的LAUNCH_ACTIVITY分支開始:

public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
           

緊接着調用了getPackageInfoNoCheck方法:

private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
            ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
            boolean registerPackage) {
        final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
        synchronized (mResourcesManager) {
            WeakReference<LoadedApk> ref;
            if (differentUser) {
                // Caching not supported across users
                ref = null;
            } else if (includeCode) {
                ref = mPackages.get(aInfo.packageName);
            } else {
                ref = mResourcePackages.get(aInfo.packageName);
            }

            LoadedApk packageInfo = ref != null ? ref.get() : null;
            if (packageInfo == null || (packageInfo.mResources != null
                    && !packageInfo.mResources.getAssets().isUpToDate())) {
                if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
                        : "Loading resource-only package ") + aInfo.packageName
                        + " (in " + (mBoundApplication != null
                                ? mBoundApplication.processName : null)
                        + ")");
                packageInfo =
                    new LoadedApk(this, aInfo, compatInfo, baseLoader,
                            securityViolation, includeCode &&
                            (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != , registerPackage);

                if (mSystemThread && "android".equals(aInfo.packageName)) {
                    packageInfo.installSystemApplicationInfo(aInfo,
                            getSystemContext().mPackageInfo.getClassLoader());
                }

                if (differentUser) {
                    // Caching not supported across users
                } else if (includeCode) {
                    mPackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                } else {
                    mResourcePackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                }
            }
            return packageInfo;
        }
    }
           

分析一下代碼的主幹邏輯,先根據Activity所屬App的包名,從mPackages中查找是否緩存有這個包名對應的LoadedApk對象,如果有,則直接傳回緩存的LoadedApk對象,如果沒有,則new一個LoadedApk對象,并把這個新new出來的對象放置到mPackages中緩存起來。這個mPackages是一個Map,它緩存了LoadedApk的弱引用:

final ArrayMap<String, WeakReference<LoadedApk>> mPackages
            = new ArrayMap<String, WeakReference<LoadedApk>>()
           

而LoadedApk對象正是App在記憶體中的表現形式。最終用ActivityClientRecord中的packageInfo變量指向了這個LoadedApk對象。

接下來調用了handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason)方法:

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {

        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
        }
    }
           

可見,performLaunchActivity方法傳回了一個Activity對象,明顯的,我們需要啟動的Activity就是在performLaunchActivity方法中建立的:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }

        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
            if (localLOGV) Slog.v(
                    TAG, r + ": app=" + app
                    + ", appName=" + app.getPackageName()
                    + ", pkg=" + r.packageInfo.getPackageName()
                    + ", comp=" + r.intent.getComponent().toShortString()
                    + ", dir=" + r.packageInfo.getAppDir());

            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                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, window);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != ) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }
                r.activity = activity;
                r.stopped = true;
                if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
                if (!r.activity.mFinished) {
                    if (r.isPersistable()) {
                        if (r.state != null || r.persistentState != null) {
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
                        }
                    } else if (r.state != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                    }
                }
                if (!r.activity.mFinished) {
                    activity.mCalled = false;
                    if (r.isPersistable()) {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state,
                                r.persistentState);
                    } else {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
                    }
                    if (!activity.mCalled) {
                        throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " did not call through to super.onPostCreate()");
                    }
                }
            }
            r.paused = true;

            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }

        return activity;
    }
           

我們的Activity經過重重困難後,在這裡終于被建立出來了!最關鍵的代碼在這裡:

Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }
           

前面說過,r是一個ActivityClientRecord對象,它的變量packageInfo是一個LoadedApk對象,這裡使用了LoadedApk中的ClassLoader來把Activity的執行個體new了出來,然後執行了一系列的建立Context,綁定Context的操作,接着終于看到調用了我們熟悉的attach方法,最後調用了mInstrumentation.callActivityOnCreate方法,callActivityOnCreate方法方法中回調了Activity的onCreate方法,我們看一下兩個重載的performCreate方法,在這裡面回調了onCreate方法,是不是一股熟悉的感覺:

final void performCreate(Bundle icicle) {
        restoreHasCurrentPermissionRequest(icicle);
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

    final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        restoreHasCurrentPermissionRequest(icicle);
        onCreate(icicle, persistentState);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }
           

最後,以這個ActivtyClientRecord的token變量為key,把這個ActivityClientRecord對象緩存在ActivityThread中的mActivities這個Map中:

總結

講了這麼多,我們提煉一下比較有用的資訊:

1.啟動Activity時,通過ActivityManagerNative擷取到一個Binder代理對象,它實作了IActivityManager接口,通過它向AMS發出了啟動Activity的請求。

2.一個程序剛起來的時候都是無歸屬的,是因為程序中的LoadedApk對象才賦予了程序具體的内容,而LoadedApk儲存在ActivityThread中的mPackages這個Map中。

3.啟動Activity的具體操作在H對象中,H對象繼承與Handler,它通過待啟動的Activity所屬的包名,從mPackages中取出LoadedApk對象,并使用LoadedApk對象中的ClassLoader加載出Activity的執行個體,并執行一系列的綁定和生命周期回調操作。

4.當AMS要執行某個Activity的生命周期方法時,并不是直接跟Activity通信的,而是以一個IBinder對象為唯一辨別(token),通過ApplicationThread跟App的程序通信,告訴App的程序說,幫我執行token這家夥的某某生命周期。當然,由于App作為Binder通信中的服務端是運作在Binder線程池中的,是以ApplicationThread會把操作通過Handler轉移到主線程ActivityThread中。

5.啟動一個Activity時,會校驗這個Activity的合法性,比如是否在App的AndroidManifest中注冊,而且這個過程是在AMS中通過調用PMS來執行的。

到這裡,Activity的啟動流程大緻已經講解完畢了,接下來一個例子說明它的實際運用,需求是這樣的:當app請求調用系統安裝器來安裝應用的時候,把請求攔截下來經過一些加工後并轉發給一個跳闆Activity(為什麼要這樣做呢?因為當App中使用了第三方SDK,比如廣點通廣告SDK時,有時候會需要控制它們的一些行為)

總體思路是,因為App是通過IActivityManager對象來跟AMS進行通信的,它是Binder的代理對象,而且在App程序中是一個單例,儲存在ActivityManagerNative的gDefault變量裡,而gDefault變量是一個Singleton對象,我們要動手腳的IActivityManager對象就是這個Singleton對象的mInstance變量。是以我們要做的事情就是:生成一個IActivityManager接口的動态代理類,并把這個動态代理類指派給mInstance變量,然後我們就可以在動态代理類中根據startActivity時傳入的Intent對象裡面的值,判斷這個intent是不是用來打開系統安裝器的,如果是,那麼我們把方法攔截下來,并轉而打開跳闆Activity。

流程分為以下幾步,代碼自行實作,這裡就不貼出來了:

1.反射擷取android.app.ActivityManagerNative的class類

2.反射擷取android.app.ActivityManagerNative中的gDefault變量,得到它的Field類

3.反射擷取android.util.Singleton的class類

4.反射擷取android.util.Singleton中的變量mInstance,得到它的Filed類,它是一個泛型,但是實際上它是一個IActivityManager對象

5.因為我們要擷取android.util.Singleton類中的mInstance變量的執行個體,是以我們要先拿到Singleton的執行個體,它的引用是android.app.ActivityManagerNative中的gDefault變量,而且還是static型的變量,那麼我們就不需要再拿android.app.ActivityManagerNative的執行個體了,直接用反射拿到gDefault的值,也就是Singleton的執行個體

6.用第5步中拿到的Singleton執行個體,反射擷取它裡面的mInstance變量的執行個體,這樣我們就拿到了原始的IActivityManager對象,為啥要拿原始的IActivityManager對象呢?明顯的我們又不是要攔截所有的IActivityManager接口中的方法,不攔截的時候還是需要用這個原始的IActivityManager對象去執行啊。

7.寫一個繼承于InvocationHandler的類,重寫invoke方法:

@Override
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
           

通過method參數判斷是否是startActivityForResult方法,然後從args參數中擷取到Intent對象,根據Intent對象中的值判斷是否是打開系統安裝器的請求,如果是,攔截掉它并執行你的業務邏輯即可,如果不是,執行return method.invoke(原始的IActivityManager, args);讓它繼續完成執行即可。

8.調用Proxy.newProxyInstance方法生成一個代理類,這個方法的聲明是:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
           

ClassLoader我們就傳入IActivityManager的原始執行個體的ClassLoader就好了,interfaces是需要傳入一個接口,我們反射拿到android.app.IActivityManager的Class類并傳入即可,而InvocationHandler需要的就是第7步中我們寫的繼承于InvocationHandler的類,裡面包含了我們的業務邏輯。最後,把newProxyInstance方法生成的Object對象指派給Singleton對象中的mInstance變量(當然是要基于第五步中擷取到的Singleton對象的執行個體),到此大功告成。