從源碼的角度看Activity是如何啟動的

来源:http://www.cnblogs.com/ghylzwsb/archive/2017/07/05/Activity.html
-Advertisement-
Play Games

大家好,今天想與大家一起分享的是Activity。我們平時接觸的最多的就是Activity了,作為四大組件中最為重要的老大,Activity究竟是如何啟動的呢?這篇文章將會從源碼的角度為大家進行全方位的解析,為了方便大家理解整個的過程,我會用流程圖的方式將整個過程串起來,希望對大家有所幫助。 ...


歡迎訪問我的個人博客,原文鏈接:http://wensibo.top/2017/07/03/Binder/ ,未經允許不得轉載!

大家好,今天想與大家一起分享的是Activity。我們平時接觸的最多的就是Activity了,作為四大組件中最為重要的老大,Activity究竟是如何啟動的呢?這篇文章將會從源碼的角度為大家進行全方位的解析,為了方便大家理解整個的過程,我會用流程圖的方式將整個過程串起來,希望對大家有所幫助。

開始吧!

一般我們啟動Activity有兩種方法,這裡我就不再詳細說這兩種方法的用法了,不過他們都是調用了同樣的一個邏輯startActivity。所以我們分析Activity的啟動流程就從這個方法開始。

 public void startActivity(Intent intent, @Nullable Bundle options) {
    if (options != null) {
        startActivityForResult(intent, -1, options);
    } else {
        startActivityForResult(intent, -1);
    }
}

可以看到儘管startActivity()有多種重載方式,但是最終調用的還是startActivityForResult,所以我們只需要看startActivityForResult裡面的實現邏輯即可。這裡需要註意的一點就是調用了startActivityForResult方法時傳入的一個參數為-1,為什麼是-1呢?還記得我們如果需要下一個Activity返回數據給目前這個Activity的時候都是調用startActivityForResult方法,不會去調用startActivity,因為startActivity儘管最後還是調用startActivityForResult,但是他設置了requestCode參數為-1,二在startActivityForResult方法中會判斷requestCode是否大於等於0,如果小於0就不會返回結果,因此我們都會選擇startActivityForResult方法以取回結果,並且設置其code參數大於等於0。下麵我們來看看startActivityForResult的實現:

public void startActivityForResult(
@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {
        //mParent是一個Activity對象,表示該Activity是否由父Activity啟動
    if (mParent == null) {
        options = transferSpringboardActivityOptions(options);
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                ar.getResultData());
        }
        //這裡就是判斷requestCode的邏輯
        if (requestCode >= 0) {
            mStartedActivity = true;
        }

        cancelInputsAndStartExitTransition(options);
    } else {
        if (options != null) {
            mParent.startActivityFromChild(this, intent, requestCode, options);
        } else {
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
}

在startActivityForResult方法中,首先會判斷該Activity是否由父Activity啟動,即mParent,如果他是第一個Activity,就會調用Instrumentation的execStartActivity方法,這裡再看一下判斷requestCode的邏輯:

if (requestCode >= 0) {
    mStartedActivity = true;
}

可以看到在這裡確實判斷了requestCode的大小,大於等於0的時候才會返回結果,否則是不會的。
繼續說回execStartActivity方法,這裡就是正真執行Activity啟動的操作,解釋一下他的幾個參數:

  • this,為啟動Activity的對象
  • mMainThread.getApplicationThread(),為Binder對象,是主進程的context對象
  • token,也是一個Binder對象,指向了服務端一個ActivityRecord對象
  • target,為啟動的Activity
  • intent,啟動的Intent對象
  • requestCode,請求碼
  • options,參數

這裡的第一個Binder對象在我們的整個分析過程中將扮演者非常重要的作用,如果你對Binder不熟悉的話,請到這裡瞭解有關Binder機制的內容。
接下來是execStartActivity方法:

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    ...
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess();
        int result = ActivityManagerNative.getDefault()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

這裡最重要的就是調用了ActivityManagerNative.getDefault().startActivity(),但是ActivityManagerNative.getDefault()是什麼東西呢?我們繼續看getDefault()的源碼:

static public IActivityManager getDefault() {
    return gDefault.get();
}


private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
    protected IActivityManager create() {
        IBinder b = ServiceManager.getService("activity");
        if (false) {
            Log.v("ActivityManager", "default service binder = " + b);
        }
        IActivityManager am = asInterface(b);
        if (false) {
            Log.v("ActivityManager", "default service = " + am);
        }
        return am;
    }
};

可以看到其中聲明瞭一個Singleton封裝類,其類型是IActivityManager,註意到其中調用了asInterface方法,接著看他做了什麼?

static public IActivityManager asInterface(IBinder obj) {
    if (obj == null) {
        return null;
    }
    IActivityManager in =
        (IActivityManager)obj.queryLocalInterface(descriptor);
    if (in != null) {
        return in;
    }

    return new ActivityManagerProxy(obj);
}


//ActivityManagerProxy:startActivity
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(callingPackage);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(startFlags);
        if (profilerInfo != null) {
            data.writeInt(1);
            profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        if (options != null) {
            data.writeInt(1);
            options.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }

可以看到asInterface返回了一個ActivityManagerProxy對象,也就是說ActivityManagerNative.getDefault()返回的就是一個ActivityManagerProxy對象,通過之前的BInder機制的文章我們可以知道Proxy是運行在客戶端的,客戶端通過將參數寫入Proxy類,接著Proxy就會通過Binder去遠程調用服務端的具體方法,因此,我們只是借用ActivityManagerProxy來調用ActivityManagerService的方法,他們之間的關係如下所示:
通信方式
到目前為止Activity的啟動流程就是如下所示了,可以看到Activity的啟動邏輯來到了AMS中。

在AMS中啟動Activity

@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle options) {
    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
        resultWho, requestCode, startFlags, profilerInfo, options,
        UserHandle.getCallingUserId());
}

@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
    enforceNotIsolatedCaller("startActivity");
    userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
            false, ALLOW_FULL_ONLY, "startActivity", null);
    // TODO: Switch to user app stacks here.
    return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
            resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
            profilerInfo, null, null, options, false, userId, null, null);
}

在startActivity中直接調用了startActivityAsUser方法,而在startActivityAsUser中則是調用mStackSupervisor.startActivityMayWait方法:

final int startActivityLocked(IApplicationThread caller,
    Intent intent, String resolvedType, ActivityInfo aInfo,
    IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
    IBinder resultTo, String resultWho, int requestCode,
    int callingPid, int callingUid, String callingPackage,
    int realCallingPid, int realCallingUid, int startFlags, Bundle options,
    boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
    ActivityContainer container, TaskRecord inTask) {
    int err = ActivityManager.START_SUCCESS;

    ...
    err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
        startFlags, true, options, inTask);

    ...
    return err;
    }

這個方法中主要構造了ActivityManagerService端的Activity對象:ActivityRecord,並根據Activity的啟動模式執行了相關邏輯。然後調用了startActivityUncheckedLocked方法,而在startActivityUncheckedLocked中則調用了startActivityUncheckedLocked方法,startActivityUncheckedLocked方法則會調用startActivityLocked方法,startActivityLocked又會調用resumeTopActivitiesLocked方法,其最後調用了resumeTopActivityLocked方法。
經過一系列的調用之後,最終來到了startPausingLocked方法,它會執行Activity的onPause方法,從而結束當前的Activity。
首先來看startPausingLocked方法:

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,boolean dontWait) {
    ...
    if (prev.app != null && prev.app.thread != null) {
        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
        try {
            EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
                    prev.userId, System.identityHashCode(prev),
                    prev.shortComponentName);
            mService.updateUsageStats(prev, false);
            prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                    userLeaving, prev.configChangeFlags, dontWait);
        } catch (Exception e) {
            // Ignore exception, if process died other code will cleanup.
            Slog.w(TAG, "Exception thrown during pause", e);
            mPausingActivity = null;
            mLastPausedActivity = null;
            mLastNoHistoryActivity = null;
        }
    } else {
        mPausingActivity = null;
        mLastPausedActivity = null;
        mLastNoHistoryActivity = null;
    }
    ...
}

這裡有一個很重要的地方就是prev.app.thread,其實他就是一個IApplicationThread類型的對象,而ApplicationThread則是ActivityThread的一個內部類,它繼承了IApplicationThread,並且都是Binder對象,所以說Appcation是一個客戶端,而ActivityThread中是一個服務端,到現在為止,Activity的調用來到了ActivityThread中,如下圖所示:

在ActivityThread中pause掉當前Activity

在ActivityThread中則是調用了schedulePauseActivity來執行pause操作:

public final void schedulePauseActivity(IBinder token, boolean finished,boolean userLeaving, int configChanges, boolean dontReport) {
    sendMessage(
        finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
        token,
        (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
        configChanges);
}

可以看到在schedulePauseActivity中則是通過handler來發送消息,消息類型為PAUSE_ACTIVITY_FINISHING,那接下來就應該看收到消息之後如何來處理了,

private void handlePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport) {
    ActivityClientRecord r = mActivities.get(token);
    if (r != null) {
        //Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
        if (userLeaving) {
            performUserLeavingActivity(r);
        }

        r.activity.mConfigChangeFlags |= configChanges;
        performPauseActivity(token, finished, r.isPreHoneycomb());

        // Make sure any pending writes are now committed.
        if (r.isPreHoneycomb()) {
            QueuedWork.waitToFinish();
        }

        // Tell the activity manager we have paused.
        if (!dontReport) {
            try {
                ActivityManagerNative.getDefault().activityPaused(token);
            } catch (RemoteException ex) {
            }
        }
        mSomeActivitiesChanged = true;
    }
}

可以看到在方法內部通過調用performPauseActivity方法來實現對當前Activity的onPause生命周期方法的回調,具體是調用了performPause方法:

final void performPause() {
    mDoReportFullyDrawn = false;
    mFragments.dispatchPause();
    mCalled = false;
    onPause();
    mResumed = false;
    if (!mCalled && getApplicationInfo().targetSdkVersion
            >= android.os.Build.VERSION_CODES.GINGERBREAD) {
        throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onPause()");
    }
    mResumed = false;
}

可以看到最終是調用了Activity的onPause()方法,接著我們回到handlePauseActivity的第二個方法ActivityManagerNative.getDefault().activityPaused(token),這是應用進程告訴服務進程,當前的Activity已經執行完成onPause方法了,其最後會調用completePauseLocked方法:

private void completePauseLocked(boolean resumeNext) {
    ...
    if (resumeNext) {
        final ActivityStack topStack = mStackSupervisor.getFocusedStack();
        if (!mService.isSleepingOrShuttingDown()) {
            mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
        } else {
            mStackSupervisor.checkReadyForSleepLocked();
            ActivityRecord top = topStack.topRunningActivityLocked(null);
            if (top == null || (prev != null && top != prev)) {
                // If there are no more activities available to run,
                // do resume anyway to start something.  Also if the top
                // activity on the stack is not the just paused activity,
                // we need to go ahead and resume it to ensure we complete
                // an in-flight app switch.
                mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
            }
        }
    }
}

可以看到經過了一系列的邏輯之後,又調用了resumeTopActivitiesLocked方法,而在該方法中則是調用了startSpecificActivityLocked 來啟動新的Activity。來看看目前的流程圖:

啟動新的Activity

在startSpecificActivityLocked方法中,其實現細節則是和調用Activity的pause方法一樣,都是通過Handler機制,發送一個啟動Activity的消息,接著處理該消息最後啟動Activity。其調用的是performLaunchActivity方法:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
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);
            }
        }
        ...

        activity.mCalled = false;
        if (r.isPersistable()) {
           mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
           mInstrumentation.callActivityOnCreate(activity, r.state);
        }
        ...
        if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
        ...
        return activity;
    }

可以看到最終的Activity對象終於創建出來了,可以看到其過程是使用反射機制創建的,而反射機制在Android系統中的應用也是隨處可見。在接下來的過程中還會繼續執行Activity的onCreate等一系列的生命周期方法。
最後再來看一下整個過程最終的流程圖:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 終於考試完了,今天突然想起來前陣子找實習的時候,今日頭條面試官問我,js執行會阻塞DOM樹的解析和渲染,那麼css載入會阻塞DOM樹的解析和渲染嗎?所以,接下來我就來對css載入對DOM樹的解析和渲染做一個測試。 為了完成本次測試,先來科普一下,如何利用chrome來設置下載速度 1. 打開chro ...
  • content-box:padding和border不被包含在定義的width和height之內。對象的實際寬度等於設置的width值和border、padding之和,即 ( Element width = width + border + padding )此屬性表現為標準模式下的盒模型。bor ...
  • 1. 簡介 本次demo中一共封裝了兩個組件:ImageEditButton 和 ImageEditContainer。其中ImageEditContainer 是在 ImageEditButton,兩個組件可單獨使用。 在demo中,實現了 圖片選擇(拍照+本地),裁剪,壓縮,保存本地 以及對已選 ...
  • 1、錯誤信息:[Vue warn]: Property or method "object" is not defined on the instance but referenced during render. Make sure to declare reactive data propert ...
  • 一,效果圖。 二,代碼。 ViewController.h ViewController.m ...
  • 1.Android開發環境 Android常用的開發環境包括兩個:Eclipse + ADT 和Android Studio,Android Studio作為google官方推出的開發環境自然有得天獨厚的優勢,所以推薦使用Android Studio進行開發。關於安裝我就不介紹了。 以後有空我會總結 ...
  • 1.許可權被分為了普通和危險兩種 2.打電話的Demo ...
  • 平臺 Ubuntu14.04 64 現象 在AndrodStudio中點擊模擬器的啟動按鈕後,模擬器界面彈出後,又立刻閃退。 解決 一、查看模擬器的信息 從上面可以看到模擬器的存放路徑以及名稱: 路徑:/home/pengdonglin/.android/avd/4_WVGA_Nexus_S_API ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...