activity顯示過程梳理

簡單整理下activity 顯示流程, 主要爲acitvity -> WindowManager -> WMS -> SurfaceFlinger創建鏈接的過程。java

本篇爲雞生蛋系列第四篇文章android

  1. Linux input系統數據上報流程
  2. Android InputManager分析
  3. AMS startActivity()
  4. activity顯示過程梳理
  5. HomeLauncher啓動

第4篇都已經拖了快兩年了, 幾回都想寫來着, 但是動力不足, 但是又想完成, 哎...segmentfault

[代碼: Android 11]session

http://aosp.opersys.com/xref/...app

setContentView()

搞應用的人都熟悉,onCreate()裏, 經過setContentView()把應用佈局設置下,以後就顯示出來了界面,
那這個流程是咋個樣子的呢?ide

AppCompat的setContentView()就不看了,
經過在frameworks/base/目錄搜索 setContentView , 其入口主要有Dialog和Activity的,函數

frameworks/base/core/java/android/app/
Dialog.java
Activity.java

繼續看Activity的, 其進一歩調用了window的setContentView(),佈局

frameworks/base/core/java/android/app/Activity.java
public void setContentView(@LayoutRes int layoutResID) {
    // 調用mWindow的setContentView()
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

public Window getWindow() {
    return mWindow;
}

那這個mWindow是啥呢?ui

@UnsupportedAppUsage
final void attach(Context context, ActivityThread aThread,
......
        Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
......
    // mWinow爲PhoneWindow
    mWindow = new PhoneWindow(this, window, activityConfigCallback);

它實際上是 new PhoneWindow(),this

這裏順便回顧下, attach()是在activity啓動時會調用, (詳細的可看下該系列第二篇文章 AMS startActivity())

其代碼以下:

frameworks/base/core/java/android/app/ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
            ......
            // 調用activity 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, window, r.configCallback,
                    r.assistToken);

咱們回到PhoneWindow 繼續看 setContentView(),
PhoneWindow有三個setContentView()方法, 最終都是將Decor Layout和ViewGroup關係創建起來,

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
    ......
    if (mContentParent == null) {
        // 安裝Decor,
        // mContentParent會在該函數裏賦值 mContentParent = generateLayout(mDecor);
        installDecor();
    }
    ......
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
        // 將咱們應用程序的佈局加入
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    ......
}

// 應用程序除了經過指定資源ID方式調用setContentView()
// 也可經過傳入參數View方式調用,
// 最終也是將該view加入 mContentParent
@Override
public void setContentView(View view) {
    setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}

@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
......
    if (mContentParent == null) {
        // 和參數爲資源ID方式同樣,先安裝Decor
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        view.setLayoutParams(params);
        final Scene newScene = new Scene(mContentParent, view);
        transitionTo(newScene);
    } else {
        // 加入view
        mContentParent.addView(view, params);
    }
    mContentParent.requestApplyInsets();
......
}

關於 setContentView()更詳細的代碼解讀可看
https://www.jianshu.com/p/2ee...
我以爲這篇文章寫得很好, 這裏就再也不講了.

show

原本覺得應用 onCreate() --> setContentView()以後, 應用就顯示出來了, 但是來來回回研究代碼好像都不太對,
這時候再來看看文章前面提到的Dialog咋顯示的呢?

咱們知道對於Dialog用法通常爲:

// 僞代碼描述
Dialog mDialog = new Dialog();
mDialog 資源參數等設置
// 調用show就顯示出來了
mDialog.show();

先new 一個Dialog()對象,而後設置資源參數等, 再調用其show()方法就能夠將其顯示出來.

那咱們就先看看其show()方法是咋玩的.

frameworks/base/core/java/android/app/Dialog.java
public void show() {
    ......// 獲取mDecor
    mDecor = mWindow.getDecorView();
    ......// 屬性
    WindowManager.LayoutParams l = mWindow.getAttributes();
    ......
    // 添加view
    mWindowManager.addView(mDecor, l);

show() 裏經過WindowManager的addView()方法, 與WinowManager聯繫上.

上面的mWindowManager即爲WINDOW_SERVICE,

mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

須要注意的是 mWindowManager 爲 WindowManagerImpl, 此時還處於用戶進程中, 非WindowManagerService.
可經過看其 registerService() 註冊服務時, 其確實爲 new WindowManagerImpl()

frameworks/base/core/java/android/app/SystemServiceRegistry.java
registerService(Context.WINDOW_SERVICE, WindowManager.class,
        new CachedServiceFetcher<WindowManager>() {
    @Override
    public WindowManager createService(ContextImpl ctx) {
        return new WindowManagerImpl(ctx);
    }});

那麼activity的mWindowManager.addView()在哪兒呢

這個時候就要推薦一本書了
<<深刻理解Android內核設計思想>> 林學森 著

這裏面surface window view固然還有別的方面內容都講得不錯,
要是我能達到做者的高度這輩子也就值了,哎......

引用書中的話

  那麼Activity 應用進程在何時會調用 addView,進而由WMS來處理addWindow 呢?
  Activity從啓動到最終在屏幕上顯示出來,分別要經歷onCreate->onStart->onResume三個狀態遷移。其中 onResume是當界面即將可見時纔會調用的,緊接着 ActivityThread 就會經過WindowManagerImpl來把應用程序窗口添加到系統中。

具體代碼以下:

frameworks/base/core/java/android/app/ActivityThread.java
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    ......
    // performResumeActivity()最終會調用activity的onResume()
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);

    if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow();
        // 獲取decor, DecorView是activity 整棵View樹最外圍
        View decor = r.window.getDecorView();
        // 可見性
        decor.setVisibility(View.INVISIBLE);
        ViewManager wm = a.getWindowManager();
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;
        ......
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                // *** 添加到view裏
                wm.addView(decor, l);

從上面代碼分析可知,其最終也是經過wm.addView(), 最終和WMS聯繫上, 和前面的Dialog流程大致差很少,
稍微有點小區別是其經過 createLocalWindowManager()建立出來的.

frameworks/base/core/java/android/view/Window.java
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    ......
    if (wm == null) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    // wm最終爲createLocalWindowManager()建立出來的
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

從上面代碼可知Dialog的show()和Activity 顯示都最終調用了 WindowManagerImpl 的 addView()方法,
那這以後是咋和SurfaceFlinger聯繫在一塊兒的呢? 咱們接着addView()看看。

addView() -> WMS

這些代碼真是一層套一層, WindowManagerImpl addView() 最終又調用了 WindowManagerGlobal addView()

frameworks/base/core/java/android/view/WindowManagerImpl.java
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
            mContext.getUserId());
}

frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow, int userId) {
        ......
        root = new ViewRootImpl(view.getContext(), display);

        // do this last because it fires off messages to start doing things
        try {
            // ViewRootImpl setView()
            root.setView(view, wparams, panelParentView, userId);

WindowManagerGlobal setView() 進一經過 ViewRootImpl setView()

frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
        int userId) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            ......// final IWindowSession mWindowSession;
                 res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                         getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                         mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                         mAttachInfo.mDisplayCutout, inputChannel,
                         mTempInsets, mTempControls);

進一步調用IWindowSession addToDisplayAsUser(), 其最終實現爲

frameworks/base/services/core/java/com/android/server/wm/Session.java
public int addToDisplayAsUser(IWindow window,......) {
    return mService.addWindow(...);
}

上面的mService爲

final WindowManagerService mService;

到這裏才終於和WMS扯上點關係,

從上分析, ViewRootImpl經過IWindowSession爲橋樑,創建與WMS聯繫

ViewRootImpl <-> IWindowSession <-> WindowManagerService

WMS -> SurfaceFlinger

從上面終於看到應用activity經過一系列的調用, 終於和WMS關聯上了, 那與SurfaceFlinger咋關聯上的呢?
那就繼續看addWindow()流程吧

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
        int requestUserId) {
        // 好多好多的檢查
        ....
        // WMS用WindowState管理window
        final WindowState win = new WindowState(this, session, client, token, parentWindow,
                appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
                session.mCanAddInternalSystemWindow);
        ....
        if  (openInputChannels) {
            // 與 input創建聯繫
            win.openInputChannel(outInputChannel);
        }
        ....
        // 與SurfaceFlinger創建聯繫
        win.attach();

addWindow()是一個比較長也比較重要的函數, 從中咱們能夠了解到 WMS用 WindowState 描述窗口, 並經過openInputChannel()與輸入關聯上,
而其與SurfaceFlinger是經過 WindowState.attach() --> Session.windowAddedLocked() --> SurfaceSession() --> SurfaceComposerClient() 聯繫上的.

代碼以下:

frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void attach() {
    if (DEBUG) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
    mSession.windowAddedLocked(mAttrs.packageName);
}

frameworks/base/services/core/java/com/android/server/wm/Session.java
void windowAddedLocked(String packageName) {......
    if (mSurfaceSession == null) {......
        mSurfaceSession = new SurfaceSession();

SurfaceSession()就會與SF創建聯繫, 可看下代碼註釋, 再也不詳細解說.

frameworks/base/core/java/android/view/SurfaceSession.java
public SurfaceSession() {
    // 進構造函數調用JNI nativeCreate()
    mNativeClient = nativeCreate();
}

frameworks/base/core/jni/android_view_SurfaceSession.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz) {
    // JNI裏new了個SurfaceComposerClient, 咱們知道其爲SurfaceFlinger的client端,
    // 用於和SF通訊.
    // 若是不熟悉該部分的可繼續日後看代碼
    SurfaceComposerClient* client = new SurfaceComposerClient();

frameworks/native/libs/gui/SurfaceComposerClient.cpp
// SurfaceComposerClient的構造函數裏並無作啥, 其繼承RefBase, 因此接下來看其onFirstRef()
void SurfaceComposerClient::onFirstRef() {
    // 這裏的ComposerService::getComposerService()獲得的爲 mComposerService, 也便是SurfaceFlinger服務
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if (sf != nullptr && mStatus == NO_INIT) {
        sp<ISurfaceComposerClient> conn;
        // 建立鏈接
        conn = sf->createConnection();

/*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() {
    // getInstance()獲取實例
    ComposerService& instance = ComposerService::getInstance();
    ......
    // 返回實例的 mComposerService
    return instance.mComposerService;

// 由於ComposerService繼承自單例, 調用其getInstance()若是沒建立對象的話, 會建立一個,
// 因此看下其構造函數
ComposerService::ComposerService()
: Singleton<ComposerService>() {
    Mutex::Autolock _l(mLock);
    // 構造函數裏調用connectLocked()
    connectLocked();
}

void ComposerService::connectLocked() {
    // 獲取SurfaceFlinger服務並賦值給 mComposerService
    const String16 name("SurfaceFlinger");
    while (getService(name, &mComposerService) != NO_ERROR) {

再回頭看下

void SurfaceComposerClient::onFirstRef() {
        conn = sf->createConnection();

其在SurfaceFlinger建立了一個client進行管理

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
    const sp<Client> client = new Client(this);
    return client->initCheck() == NO_ERROR ? client : nullptr;
}

NOTE

Window <1--1> WMS的WindowState <1--1> SurfaceSession <1--1> SurfaceComposerClient <1--1> SurfaceFlinger進程裏的Client

它們是一一對應的

相關文章
相關標籤/搜索