Android10_原理機制系列_Activity窗口添加到WMS過程


前言

首先看一個Android界面的佈局層次結構,最直觀的看一下:html

ui_hierarchyviewer

咱們能清晰看到,這個界面分紅了3部分:頂部狀態欄(statusbar)、底部導航欄(navigationbar)、應用界面。java

題外話:
查看佈局的層次結構,工具或途徑能夠參考下面的。
Android Studio:Tools->Layout Inspector->選擇要查看的進程;
SDK Tools:tools/hierarchyviewer.bat。 不過最新推薦用tools/monitor.bat代替單獨的hierarchyviewer.bat;hierarchyviewer.bat在工程目錄下也存在prebuilts/devtools/toolswindows

該篇主要介紹Activity中窗口的建立過程 以及 添加到WMS的過程。
第二部分 綜述總結,先將上述兩個過程的內容作了比較簡潔的總結。
第三部分 Activity窗口添加過程,跟蹤源碼詳細 講述了 Activity窗口的建立過程 以及 添加到WMS過程。
因爲第三部分 跟蹤源碼,這個過程比較長,涉及比較多,相對枯燥。因此把總結先放到了第二部分。這樣,若是瞭解過源碼或這個過程的,能夠只看第二部分。沒了解過的,也能夠經過第二部分有個大概瞭解,再查看第三部分,若遇到不太清楚的部分,能夠再回到第二部分,對比理解。session

該篇也是基於Android10的源碼。
如有不對或不足,歡迎指點。

app

綜述總結

前言已經介紹了爲何將總結放在了前面。下面具體看下。ide

第二部分,主要介紹了下面幾個內容:工具

  • Window類型:窗口類型介紹
  • 幾個重要類:窗口建立添加到WMS過程當中常見的一些類,瞭解下他們之間的關係
  • Activity建立窗口添加到WMS綜述:簡單總結了下 Activity的建立過程 和 添加到WMS過程
  • Activity中的一些結構示意圖:整個過程,Activity中關聯的一些類/結構的 關係,理解這個我的以爲頗有必要
  • Token傳遞到WMS:Token是很重要的參數,參與整個過程。這裏將該篇涉及的過程當中的 Token的傳遞過程單獨總結了下

Window類型

//WindowManager.java
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
    public static final int FIRST_APPLICATION_WINDOW = 1;
    public static final int LAST_APPLICATION_WINDOW = 99;
    
    public static final int FIRST_SUB_WINDOW = 1000;
    public static final int LAST_SUB_WINDOW = 1999;
    
    public static final int FIRST_SYSTEM_WINDOW = 2000;
    public static final int LAST_SYSTEM_WINDOW = 2999;
    //狀態欄
    public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;
    //搜索欄
    public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;
    //來電顯示
    public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
    //警告窗口,常見如:低電量警告
    public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;
    //鎖屏
    public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;
    //toast
    public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;
    public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;//顯示在全部窗口之上,覆蓋
    //來電優先,即便鎖屏狀態下
    public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;
    //輸入法窗口
    public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;
    //壁紙
    public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;
}

應用窗口(1 ~ 99): FIRST_APPLICATION_WINDOW ~ LAST_APPLICATION_WINDOW。對應一個Activity,token需設置成Activity的token。 如:Activity。
子窗口(1000 ~ 1999): FIRST_SUB_WINDOW ~ LAST_SUB_WINDOW。必需要有一個父窗口,token需設置成父窗口的token。 如:PopupWindow,依附於Activity。
系統窗口(2000 ~ 2999): FIRST_SYSTEM_WINDOW ~ LAST_SYSTEM_WINDOW。系統級的 不須要對應Activity 也不須要有父窗口,應用進程通常沒有權限建立,只有系統進程能夠建立。如:上面列出了部分常見的系統窗口,狀態欄、來電、toast、輸入法等等。

佈局

幾個重要類

下面幾個類是後續常常看到的,這裏主要看下他們直接的繼承關係,後面看到比較容易理解。學習

public abstract class Window {}
public class PhoneWindow extends Window implements MenuBuilder.Callback {}

public interface WindowManagerPolicy extends WindowManagerPolicyConstants {}
public class PhoneWindowManager implements WindowManagerPolicy {}

public interface ViewManager {
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}
public interface WindowManager extends ViewManager {}
public final class WindowManagerImpl implements WindowManager {}

/** A window in the window manager. */
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {}
  1. Window是一個抽象類,Activity、Toast、Dialog等都是靠Window來呈現。
    PhoneWindow是Window的具體實現類(幾乎是惟一實現類)。
  2. WindowManager是個接口,繼承接口 ViewManager(ViewManager定義了3個操做:增長、更新、移除)。
    WindowManagerImpl是WindowManager的實現類。
    不過查看WindowManagerImpl中 關於ViewManager的3個操做能夠看出,這3個實現 最終是交由WindowManagerGlobal完成的。
  3. WindowState維護着窗口的全部信息。WMS經過WindowState對窗口進行管理、保存狀態等。

Activity建立窗口添加到WMS綜述

這是跟蹤的代碼過程,這裏彙總下 方便後續查看理解。 紅色是比較主要的幾個節點方法。
wms_activity_windowui

//attach()
-performLaunchActivity()
--activity.attach()//建立了PhoneWindow(mWindow)。mWindowManager保存的是 從mWindow處獲取的 setWindowManager()建立的WindowManagerImpl
---mWindow.setWindowManager()//PhoneWindow內部 建立了WindowManagerImpl(mWindowManager),並保存了appToken、appName。

//onCreate()
-setContentView()
--installDecor()
---generateDecor()//建立了DecorView(mDecor)
---generateLayout()//將activity的佈局做爲子視圖(ViewGroup)添加到mDecor中

//onResume()
-r.activity.makeVisible()//
--wm.addView(mDecor, ...)//wm即mWindowManager(WindowManagerImpl對象)
---WindowManagerGlobal.addView()//建立了ViewRootImpl。addView的view是mDecor,ViewRootImpl中建立了mWindow(這裏是一個IBinder,而非attach()中建立的)
----ViewRootImpl.setView()//openSession()建立了Session(IWindowSession的代理類),view也是mDecor。mDecor傳入到ViewRootImpl的mView
-----Session.addToDisplay()//經過Session進入system_server進程
------mService.addWindow()//進入WMS,執行addWindow()添加窗口

attach階段:
一個Activity 建立了一個PhoneWindow對象 ,PhoneWindow經過setWindowManager() 建立了WindowManagerImpl
即Activity 對應一個PhoneWindow,並獲得了一個WindowManager(WindowManagerImpl,Window建立的)。
onCreate階段:
建立了DecorView ,並將 activity的佈局添加到DecorView中
onResume階段:
建立了ViewRootImpl,經過setView()最終由Session進入system_server進程。最終執行addWindow添加窗口到WMS。

Activity中的一些結構示意圖

下面是我學習總結中 根據理解畫的,方便本身查看時一眼可得。
(如有什麼不對,多謝指點)

wms_actui_layout

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
}

public final class WindowManagerGlobal {
    private static IWindowManager sWindowManagerService;//WMS客戶端,
    private static IWindowSession sWindowSession;//Session
    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
}
  1. 一個Activity對應了一個PhoneWindow對象。即 每一個Activity對應一個Window (具體實現類是PhoneWindow)。
  2. 一個PhoneWindow 持有一個 DecorView 實例, DecorView實際是一個FrameLayout,它是Activity中全部View的根(最頂層的View)。
  3. 一個PhoneWindow有一個WindowManagerImpl。WindowManagerImpl持有一個單例WindowManagerGlobal。

Token傳遞到WMS

Activity啓動時 AMS會爲其建立一個ActivityRecord。能夠參考:AMS之應用的第一次啓動過程

下面先看下ActivityRecord中關於token的幾處代碼:

final class ActivityRecord extends ConfigurationContainer {
    final IApplicationToken.Stub appToken; // window manager token
    // TODO: Remove after unification
    AppWindowToken mAppWindowToken;
        
    ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,...) {
        appToken = new Token(this, _intent);
    }
    
    void createAppWindowToken() {
        mAppWindowToken = createAppWindow(mAtmService.mWindowManager, appToken,...);
    }
    
    static class Token extends IApplicationToken.Stub {
        Token(ActivityRecord activity, Intent intent) {
            weakActivity = new WeakReference<>(activity);
            name = intent.getComponent().flattenToShortString();
        }
    }
}

ActivityRecord中的成員變量 appToken ,這個很重要,後續不少地方會一直涉及到。

ActivityRecord中有個 appToken ,其是一個IBinder(內部類Token繼承了IApplicationToken接口)。Token內部持有Activity的弱引用。
在ActivityRecord中會經過createAppWindow()建立並保存 AppWindowToken對象 到mAppWindowToken。
mAppWindowToken:這個appToken會被封裝在其中。路徑:ActivityStack.startActivityLocked()->ActivityRecord.createAppWindowToken()。AppWindowToken是WindowToken子類。WindowToken能夠標誌一個窗口。

這個appToken,會在Activity.attach()中做爲參數傳遞到Activity。
Activity保存到mToken。
而後經過 Activity.attach()->mWindow.setWindowManager() 傳入到Window(PhoneWindow)中。
Window保存到mAppToken。
WindowManagerGlobal.addView()->Window.adjustLayoutParamsForSubWindow()保存到WindowManager.LayoutParams中的token變量中。
最後WindowManager.LayoutParams(其中token即ActivityRecord中的appToken)做爲參數傳入ViewRootImpl.setView()。
ViewRootImpl中mWindowAttributes拷貝了WindowManager.LayoutParams,做爲參數經過Session.addToDisplay()傳入WMS中,進行後續操做。

這是整個添加窗口(到addWindow())過程 appToken參與的過程及傳遞過程。
appToken如何參與窗口的添加,這個在 「第三部分的2.8:mService.addWindow()」 註釋中能大體看到,比較詳細。


Activity窗口添加過程

這裏主要介紹 Activity對應Window的建立 以及 Window添加到WMS的過程。

Activity窗口建立

AMS之應用的第一次啓動過程 中,從點擊應用圖標到activity建立並執行onCreate()。 下面部分是後面部分的截取,不清楚能夠參考下那篇文章。

1.1:handleLaunchActivity()

這裏從handleLaunchActivity()開始。

//ActivityThread.java
@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
        PendingTransactionActions pendingActions, Intent customIntent) {
    ...
    WindowManagerGlobal.initialize();
    final Activity a = performLaunchActivity(r, customIntent);
    ...
}

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
    }
    try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        if (activity != null) {
            Window window = null;
            ...
            //attach(),注意這個r.token。參考1.2
            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);
            if (r.isPersistable()) {
                //callActivityOnCreate() 最終執行到的activity的onCreate()方法。  
                //參考1.4
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
        }
    }
    ...
    return activity;
}

WindowManagerGlobal.initialize();是獲取WMS的IBinder代理類,用於與WMS通訊。這裏不列出代碼了。
接着要看的是activity.attach()。注意做爲參數傳入attach()的 r.token 是個IBinder,來自ActivityClientRecord,簡單看標識了一個Activity。

1.2:activity.attach()

//Activity.java
final void attach(Context context, ActivityThread aThread,
       Instrumentation instr, IBinder token, ...) {
    ...
    //建立PhoneWindow
    mWindow = new PhoneWindow(this, window, activityConfigCallback);//建立PhoneWindow 
    //設置軟鍵盤
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }
    //token保存到mToken。        
    mToken = token;
    ...
    //mToken傳入到Window,參考1.3
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    //mWindowManager即setWindowManager()中建立的WindowManagerImpl。
    mWindowManager = mWindow.getWindowManager();
    ...
}

首先建立了Activityd對應的Window,是PhoneWindow-Window的實現類。 接着看mWindow.setWindowManager()

1.3:mWindow.setWindowManager()

//Window.java
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    //ActivityClientRecord.token
    mAppToken = appToken;
    mAppName = appName;
    mHardwareAccelerated = hardwareAccelerated;
    if (wm == null) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    //建立了WindowManagerImpl,注意WindowManagerImpl中mParentWindow是this,非空
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

這裏建立了WindowManagerImpl對象,即WindowManager的實現類。並保存了appToken、appName、mWindowManager。
經過setWindowManager(),即爲Window(或者PhoneWindow)設置建立了WindowManager(WindowManagerImpl)。

1.4:setContentView()

mInstrumentation.callActivityOnCreate()最終有調用到Activity的onCreate()。
自定義Activity,設置佈局都執行了setContentView(),下面直接來看下這個方法。

//Activity.java
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);//
    initWindowDecorActionBar();
}


public Window getWindow() {
    return mWindow;//即PhoneWindow對象
}

//PhoneWindow.java
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
@Override
public void setContentView(int layoutResID) {
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
    // decor, when theme attributes and the like are crystalized. Do not check the feature
    // before this happens.
    if (mContentParent == null) {
        installDecor();//
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    ...
}

private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        //生成DecorView,參考1.5
        mDecor = generateDecor(-1);
        ...
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        //佈局添加到DecorView,參考1.5
        mContentParent = generateLayout(mDecor);

        // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
        mDecor.makeOptionalFitsSystemWindows();

        final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                R.id.decor_content_parent);
        if (decorContentParent != null) {
        } else {
            mTitleView = findViewById(R.id.title);
        }
        ...
    }
}

這裏主要關注下兩個方法:mDecor = generateDecor(-1);mContentParent = generateLayout(mDecor);
先看下他們的相關代碼:

1.5:generateDecor()和generateLayout()

protected DecorView generateDecor(int featureId) {
    // System process doesn't have application context and in that case we need to directly use
    // the context we have. Otherwise we want the application context, so we don't cling to the
    // activity.
    Context context;
    ...
    return new DecorView(context, featureId, this, getAttributes());//
}

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
}

protected ViewGroup generateLayout(DecorView decor) {
    ...
    mDecor.startChanging();
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ...
    mDecor.finishChanging();
    return contentParent;
}

//DecorView.java
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
    ...
    mDecorCaptionView = createDecorCaptionView(inflater);
    final View root = inflater.inflate(layoutResource, null);
    if (mDecorCaptionView != null) {
        if (mDecorCaptionView.getParent() == null) {
            addView(mDecorCaptionView,
                    new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mDecorCaptionView.addView(root,
                new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
    } else {

        // Put it below the color views.
        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }
    mContentRoot = (ViewGroup) root;
    initializeElevation();
}

經過generateDecor()建立了一個DecorView。DecorView實際是一個FrameLayout。
而後經過generateLayout(),最終將activity的佈局做爲子視圖(ViewGroup)添加到DecorView中。

上面能夠看到,activity生成到執行onCreate(),這個過程,activity生成了關聯的PhoneWindow,而後建立了WindowManagerImpl、DecorView。
下面看下Window添加到WMS的過程,看這些建立的對象以前如何聯繫 造成的一開始介紹的結構示意圖。

Window添加到WMS過程

AMS之應用的第一次啓動過程 中主要講到onCreate,其實onResume也在其中在,這裏不詳細解釋了,再次列出相關代碼:

//ActivityStackSupervisor.java:
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
        boolean andResume, boolean checkConfig) throws RemoteException {
    ...
    try {
        ...
        try {
            // Create activity launch transaction.
            final ClientTransaction clientTransaction = ClientTransaction.obtain(
                    proc.getThread(), r.appToken);

            final DisplayContent dc = r.getDisplay().mDisplayContent;
            clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
            ...
            // Set desired final state.
            final ActivityLifecycleItem lifecycleItem;
            if (andResume) {
                lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
            } else {
                lifecycleItem = PauseActivityItem.obtain();
            }
            clientTransaction.setLifecycleStateRequest(lifecycleItem);

            // Schedule transaction.
            mService.getLifecycleManager().scheduleTransaction(clientTransaction);
            ...
        } 
    }
    ...
    return true;
}

經過 LaunchActivityItem 關聯 最終執行結果是建立了應用的Activity 並 執行了attach()和onCreate()。 andResume爲true(傳入的參數爲true,能夠參考那篇這段代碼往前看), 經過 ResumeActivityItem 關聯 最終執行到的是 ActivityThread.handleResumeActivity()。

這裏從ActivityThread.handleResumeActivity()來看。

2.1:ActivityThread.handleResumeActivity()

//ActivityThread.java
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    ...
    // TODO Push resumeArgs into the activity for consideration
    //執行onStart()->onResume()。參考2.2
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    final Activity a = r.activity;
    if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();
        decor.setVisibility(View.INVISIBLE);
        ViewManager wm = a.getWindowManager();
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
        ...
    } 
    ...
    // The window is now visible if it has been added, we are not
    // simply finishing, and we are not starting another activity.
    if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
        ...
        r.activity.mVisibleFromServer = true;
        mNumVisibleActivities++;
        if (r.activity.mVisibleFromClient) {
            //參考2.3
            r.activity.makeVisible();
        }
    }
    ...
}

注意 View decor = r.window.getDecorView(); 獲取了DecorView對象,最後經過 a.mDecor = decor; 將DecorView賦到了Activity中。
這裏關注兩個:
performResumeActivity()
r.activity.makeVisible();

2.2:performResumeActivity()

//ActivityThread.java
@VisibleForTesting
public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
        String reason) {
    final ActivityClientRecord r = mActivities.get(token);
    ...
    try {
        r.activity.onStateNotSaved();
        r.activity.mFragments.noteStateNotSaved();
        ...
        r.activity.performResume(r.startsNotResumed, reason);

        r.state = null;
        r.persistentState = null;
        r.setState(ON_RESUME);

        reportTopResumedActivityChanged(r, r.isTopResumedActivity, "topWhenResuming");
    } 
    return r;
}

//Activity.java
final void performResume(boolean followedByPause, String reason) {
    performRestart(true /* start */, reason);
    mInstrumentation.callActivityOnResume(this);
}

final void performRestart(boolean start, String reason) {
    mInstrumentation.callActivityOnRestart(this);
}

//Instrumentation.java
public void callActivityOnRestart(Activity activity) {
    activity.onRestart();
}

public void callActivityOnResume(Activity activity) {
    activity.mResumed = true;
    activity.onResume();
    ...
}

performResumeActivity()中 r.activity.performResume()回調Activity的performResume()方法。最終執行了Activity的onResume()方法。
performResume()在執行onResume前,調用了performRestart(),最終調用的是activity的onStart()。這裏能夠看出 onStart()執行在onResume()以前。

2.3:r.activity.makeVisible()

//Activity.java
void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

//WindowManagerImpl.java
public final class WindowManagerImpl implements WindowManager {
    @UnsupportedAppUsage
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        //mParentWindow即建立WindowManagerImpl是 傳入的。參考2.4
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
}

這裏的getWindowManager()獲取到的是前面講到的 attach()時經過setWindowManager()建立的WindowManagerImpl對象。
前面也講過,addView()等3個操做定義實現 最終在WindowManagerGlobal中,這裏就能夠看到。

2.4:WindowManagerGlobal.addView()

//WindowManagerGlobal
@UnsupportedAppUsage
private final ArrayList<View> mViews = new ArrayList<View>();
@UnsupportedAppUsage
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    ...
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    if (parentWindow != null) {
        //調整Window參數,這個過程將token設置其中了
        //參考2.4.1
        parentWindow.adjustLayoutParamsForSubWindow(wparams);
    } 
    ...
    ViewRootImpl root;
    View panelParentView = null;
    synchronized (mLock) {
        ...
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
        try {
            //將view及相關參數設置到ViewRootImpl中。ViewRootImpl會向WMS添加新窗口、申請Surface及繪製工做等。
            //參考2.6
            root.setView(view, wparams, panelParentView);
        } 
        ...
    }
}

//ViewRootImpl.java
@UnsupportedAppUsage
final IWindowSession mWindowSession;
public ViewRootImpl(Context context, Display display) {
    mContext = context;
    //建立了Session(),參考2.5
    mWindowSession = WindowManagerGlobal.getWindowSession();
    mDisplay = display;
    mBasePackageName = context.getBasePackageName();
    mThread = Thread.currentThread();
    mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
    //這裏的mWindow不是前面Activity中的PhoneWindow,它是W extends IWindow.Stub
    mWindow = new W(this);
    mViewVisibility = View.GONE;
    //建立AttachInfo
    mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
                context);
    ...
}

static class W extends IWindow.Stub {...}

//ViewRootImpl.java
public final class ViewRootImpl implements ViewParent,

//View.java
final static class AttachInfo {
    AttachInfo(IWindowSession session, IWindow window, Display display,
            ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer,
            Context context) {
        mSession = session;
        mWindow = window;
        mWindowToken = window.asBinder();
        mDisplay = display;
        mViewRootImpl = viewRootImpl;
        mHandler = handler;
        mRootCallbacks = effectPlayer;
        mTreeObserver = new ViewTreeObserver(context);
    }
}

這裏主要看到,建立了ViewRootImpl對象。這個類實現了View與WindowManager之間必要的協議。
注意建立中的mWindow = new W(this);,這個W繼承IWindow.Stub。

建立ViewRootImpl對象時 建立了一個mAttachInfo = View.AttachInfo(), AttachInfo是一系列綁定信息。mWindowSession、mWindow做爲參數傳入。AttachInfo建立時注意mWindowToken = window.asBinder();
mWindowSession在後續2.5/2.6/2.7中講到,它是Session對象,它是IWindowSession的代理類,經過他能夠與WMS通訊的binder接口
mWindow這裏是W對象,它是IWindow.Stub,經過new建立,後續能看到會傳入WMS,它是WMS回調應用(與應用通訊)的binder接口
mWindowToken,也就是W的IBinder對象,也是WMS與應用通訊的接口

建立ViewRootImpl對象後,WindowManagerGlobal將View、ViewRootImpl、LayoutParams保存到相應的ArrayList中。前面也講到過,WindowManagerGlobal是單例的,應用進程中只有一個。最後經過root.setView()將View(這裏是DecorView)傳入到ViewRootImpl中。

2.4.1:adjustLayoutParamsForSubWindow()

前面看到mAppToken是從Activity的傳入的。
這裏mAppToken被設置到WindowManager.LayoutParams裏,後面能夠看到最終傳入到WMS參與處理。

//Window.java
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
    CharSequence curTitle = wp.getTitle();
    //子窗口,該篇中是應用窗口,因此不走這,也瞭解下。       
    if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
            wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
        if (wp.token == null) {
            View decor = peekDecorView();
            if (decor != null) {
                wp.token = decor.getWindowToken();
            }
        }
        ...
    //系統窗口,也不走這
    } else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
            wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
        ...
    //應用窗口,該篇走這
    } else {
        if (wp.token == null) {
            //設置到了WindowManager.LayoutParams中
            wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
        }
        ...
    }
    ...
}

2.4.2:AttachInfo在其中瞭解下

ViewRootImpl與各個View。經過下面的過程,AttachInfo綁定信息被設置到各個View中了,即各個View可以獲取到各類相關信息。
2.6執行到ViewRootImpl.setView()後,參考過程:setView()->requestLayout()->scheduleTraversals()->mTraversalRunnable->doTraversal()->performTraversals()->host.dispatchAttachedToWindow(mAttachInfo, 0)->View.dispatchAttachedToWindow()->ViewGroup.dispatchAttachedToWindow()。
屬同個ViewGroup的 AttachInfo是同樣的。

//ViewGroup.java
@Override
@UnsupportedAppUsage
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
    mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
    super.dispatchAttachedToWindow(info, visibility);
    mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;

    final int count = mChildrenCount;
    final View[] children = mChildren;
    for (int i = 0; i < count; i++) {
        final View child = children[i];
        child.dispatchAttachedToWindow(info,
                combineVisibility(visibility, child.getVisibility()));
    }
    final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
    for (int i = 0; i < transientCount; ++i) {
        View view = mTransientViews.get(i);
        view.dispatchAttachedToWindow(info,
                combineVisibility(visibility, view.getVisibility()));
    }
}
}

上述過程 performTraversals() 大體瞭解下:從上而下遍歷視圖樹,每一個View繪製本身,ViewGroup通知子View進行繪製。測量performMeasure() 執行佈局performLayout() 繪製performDraw()。
Android繪製 重要的部分就在這裏,須要瞭解的能夠仔細研究下這個方法(performTraversals()),這裏不做關注。

2.5:WindowManagerGlobal.getWindowSession()

// WindowManagerGlobal.java   
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowSession == null) {
            try {
                InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                IWindowManager windowManager = getWindowManagerService();
                //建立Session對象
                sWindowSession = windowManager.openSession(
                        new IWindowSessionCallback.Stub() {
                            @Override
                            public void onAnimatorScaleChanged(float scale) {
                                ValueAnimator.setDurationScale(scale);
                            }
                        });
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        return sWindowSession;
    }
}

//WindowManagerService.java
@Override
public IWindowSession openSession(IWindowSessionCallback callback) {
    return new Session(this, callback);
}

獲取Sessiond對象,若是沒有則經過 windowManager.openSession() 建立。Session是IWindowSession的代理類,而後返回給ViewRootImpl中的mWindowSession。

2.6:ViewRootImpl.setView()

//ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            mWindowAttributes.copyFrom(attrs);
            ...
            // Schedule the first layout -before- adding to the window
            // manager, to make sure we do the relayout before receiving
            // any other events from the system.
            requestLayout();//TODO
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                //參考2.7,進入system_server進程
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                        mTempInsets);
                setFrame(mTmpFrame);
            }
            ...
        }
    }
}

res = mWindowSession.addToDisplay():mWindowSession是上面返回的建立的Session,mWindowSession.addToDisplay()即經過binder進入system_server進程,執行的Session.addToDisplay()。
mView即DecorView。
這裏的mWindow是2.4中講到的,是 W 繼承IWindow.Stub。這是一個IBinder對象,在應用進程建立ViewRootImpl時被建立。
這裏mWindowSession.addToDisplay()日後能夠看到被傳入到WMS。

2.7:Session.addToDisplay()

//Session.java
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    final WindowManagerService mService;
    public Session(WindowManagerService service, IWindowSessionCallback callback) {
        mService = service;
        ...
    }

    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
            Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState) {
        //參考2.8
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
                outInsetsState);
    }
}

進入WMS,添加Window。

2.8:mService.addWindow()

終於到最後WMS.addWindow(),這裏完成窗口添加。能夠仔細看下下面源碼及註釋,這個方法即便縮減了不少仍是比較長,須要耐心。

//WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState) {
    int[] appOp = new int[1];
    //檢查權限,無權限不能添加窗口
    int res = mPolicy.checkAddPermission(attrs, appOp);
    if (res != WindowManagerGlobal.ADD_OKAY) {
        return res;
    }

    boolean reportNewConfig = false;
    WindowState parentWindow = null;
    ...
    final int type = attrs.type;
    synchronized (mGlobalLock) {
        ...
        //獲取窗口要添加到的DisplayContent。即顯示在哪一個屏幕上
        final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
        
        if (displayContent == null) {
            Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
                    + displayId + ".  Aborting.");
            return WindowManagerGlobal.ADD_INVALID_DISPLAY;
        }
        if (!displayContent.hasAccess(session.mUid)) {
            Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
                    + "does not have access: " + displayId + ".  Aborting.");
            return WindowManagerGlobal.ADD_INVALID_DISPLAY;
        }

        if (mWindowMap.containsKey(client.asBinder())) {
            Slog.w(TAG_WM, "Window " + client + " is already added");
            return WindowManagerGlobal.ADD_DUPLICATE_ADD;
        }
        //添加子窗口,父窗口必須存在。
        if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
            parentWindow = windowForClientLocked(null, attrs.token, false);
            if (parentWindow == null) {
                Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
                      + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
            }
            //這裏能夠看出WMS要求 窗口的層級 最多爲兩層
            if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                    && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
                        + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
            }
        }
        if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
            Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display.  Aborting.");
            return WindowManagerGlobal.ADD_PERMISSION_DENIED;
        }

        AppWindowToken atoken = null;
        final boolean hasParent = parentWindow != null;
        //獲取WindowToken,對於子窗口使用父窗口的token。
        //經過attrs.token從mTokenMap取出:private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();
        //關於Activity窗口:WindowToken,前面講過ActivityRecord 建立時會建立AppWindowToken,這個過程當中appToken和AppWindowToken被保存到mTokenMap中
        WindowToken token = displayContent.getWindowToken(
                hasParent ? parentWindow.mAttrs.token : attrs.token);

        // If this is a child window, we want to apply the same type checking rules as the
        // parent window type.
        final int rootType = hasParent ? parentWindow.mAttrs.type : type;
        boolean addToastWindowRequiresToken = false;
        //如下是WindowToken和窗口之間的關係
        if (token == null) {
            //如下窗口類型,WindowToken不能爲空
            if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
                Slog.w(TAG_WM, "Attempted to add application window with unknown token "
                      + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
            if (rootType == TYPE_INPUT_METHOD) {
                Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
                      + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
            if (rootType == TYPE_VOICE_INTERACTION) {
                Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
                      + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
            if (rootType == TYPE_WALLPAPER) {
                Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
                      + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
            if (rootType == TYPE_DREAM) {
                Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
                      + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
            if (rootType == TYPE_QS_DIALOG) {
                Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
                      + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
            if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
                Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
                        + attrs.token + ".  Aborting.");
                return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
            }
            if (type == TYPE_TOAST) {
                // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
                if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
                        parentWindow)) {
                    Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            }
            final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
            final boolean isRoundedCornerOverlay =
                    (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
            //token爲空,除上述窗口類型,其餘是容許的。此時新建WindowToken
            token = new WindowToken(this, binder, type, false, displayContent,
                    session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
        //token不爲空,且是應用窗口類型,token須要時AppWindowToken類型
        } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
            //判斷token是不是AppWindowToken類型
            //前面知道,Activity建立的是AppWindowToken,即得到的atoken非空
            atoken = token.asAppWindowToken();
            if (atoken == null) {
                Slog.w(TAG_WM, "Attempted to add window with non-application token "
                      + token + ".  Aborting.");
                return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
            } else if (atoken.removed) {
                Slog.w(TAG_WM, "Attempted to add window with exiting application token "
                      + token + ".  Aborting.");
                return WindowManagerGlobal.ADD_APP_EXITING;
            } else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null) {
                Slog.w(TAG_WM, "Attempted to add starting window to token with already existing"
                        + " starting window");
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }
        //其餘各類窗口類型處理
        } else if (rootType == TYPE_INPUT_METHOD) {
            ...
        }...
        //WindowState維護了全部窗口的信息,它是WMS實際管理的「窗口」
        //它與Z-Order密切相關(多個Window層疊佈局),其屬性mLayer 越大,窗口越靠前。
        final WindowState win = new WindowState(this, session, client, token, parentWindow,
                appOp[0], seq, attrs, viewVisibility, session.mUid,
                session.mCanAddInternalSystemWindow);
        if (win.mDeathRecipient == null) {
            // Client has apparently died, so there is no reason to
            // continue.
            Slog.w(TAG_WM, "Adding window client " + client.asBinder()
                    + " that is dead, aborting.");
            return WindowManagerGlobal.ADD_APP_EXITING;
        }
        ...
        origId = Binder.clearCallingIdentity();
        //建立SurfaceSession,實現與SurfaceFlinger通訊。參考2.9 簡單說明下
        win.attach();
        //將WindowState對象加入到mWindowMap中
        mWindowMap.put(client.asBinder(), win);
        win.initAppOpsState();
        ...
        final AppWindowToken aToken = token.asAppWindowToken();
        win.mToken.addWindow(win);
        win.applyAdjustForImeIfNeeded();
        ...
    }
    ...
    return res;
}

WMS經過WindowState對窗口進行管理、保存狀態等。
添加窗口都須要指明其WindowToken;同時窗口需指明其DisplayContent 以肯定顯示到哪一個屏幕設備。
具體請看上面註釋,比較詳細了。
看完,大體能明白窗口類型、WindowToken在窗口添加中的做用。瞭解到token的做用。
好比:若添加的時子窗口,則必須有父窗口,且窗口的層級最多爲兩層。WindowToken爲null,能夠明顯看出那些狀況不容許添加。添加的窗口時應用窗口時,WindowToken要是AppWindowToken。等等。

addWindow()暫時就說這些。
這個添加後的結果,經過res 最終反回到了 2.6:ViewRootImpl.setView() 中,根據結果 繼續處理。

2.9:win.attach()

爲何win.attach()是建立與SurfaceFlinger通訊的?簡單瞭解下。
跟蹤下去是建立了SurfaceSession對象,這個建立進入native,最終建立了一個與SurfaceFlinger通訊的 SurfaceComposerClient。 所以,能夠與SurfaceFlinger進行通訊。
ViewRootImpl建立時 就建立的mSurface,mSurface是ViewRootImpl的成員變量,此時建立的Surface什麼都沒有,最後經過relayoutWindow()進入WMS 一步步向SurfaceFlinger發出請求。
這時幾處相關代碼。

//WindowState.java
void attach() {
    if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
    mSession.windowAddedLocked(mAttrs.packageName);
}

//Session.java
void windowAddedLocked(String packageName) {
    mPackageName = packageName;
    mRelayoutTag = "relayoutWindow: " + mPackageName;
    if (mSurfaceSession == null) {
        mSurfaceSession = new SurfaceSession();
        ...
    }
    mNumWindow++;
}

//ViewRootImpl.java    
// These can be accessed by any thread, must be protected with a lock.
// Surface can never be reassigned or cleared (use Surface.clear()).
@UnsupportedAppUsage
public final Surface mSurface = new Surface();
private final SurfaceControl mSurfaceControl = new SurfaceControl();

這裏只說到添加窗口到WMS,關於窗口添加後的處理、界面的計算顯示更新等等,之後再總結。

相關文章
相關標籤/搜索