Android窗口機制(三)Window和WindowManager的建立與Activity

Android窗口機制系列

Android窗口機制(一)初識Android的窗口結構
Android窗口機制(二)Window,PhoneWindow,DecorView,setContentView源碼理解
Android窗口機制(三)Window和WindowManager的建立與Activity
Android窗口機制(四)ViewRootImpl與View和WindowManager
Android窗口機制(五)最終章:WindowManager.LayoutParams和Token以及其餘窗口Dialog,Toastjava

前兩篇文章跟你們介紹了Window,PhoneWindow,DecorView他們間的聯繫,以及他們之間的理解。講到Window你們確定會想到常見的WindowManager,二者確定是發生過關係的。此外對於Window和WindowManager的建立問題,正是下面要介紹的。android

瞭解他們前,咱們先來看個結構app

Paste_Image.pngide

ViewManager

/** Interface to let you add and remove child views to an Activity. To get an instance
  * of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
  */
public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

ViewManager接口定義了一組規則,也就是add、update、remove的操做View接口。也就是說ViewManager是用來添加和移除activity中View的接口,能夠經過Context.getSystemService()獲取實例。
咱們看下ViewManager的實現類佈局

public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    private static final String TAG = "ViewGroup";
    ...
    public void addView(View child, LayoutParams params) {
        addView(child, -1, params);
    }
    
     /*
     * @param child the child view to add
     * @param index the position at which to add the child or -1 to add last
     * @param params the layout parameters to set on the child
     */
    public void addView(View child, int index, LayoutParams params) {
        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
        // therefore, we call requestLayout() on ourselves before, so that the child's request
        // will be blocked at our level
        requestLayout();
        invalidate(true);
        addViewInner(child, index, params, false);
    }
    ...

能夠看到ViewGroup裏面實現了ViewManager接口,View經過ViewGroup的addView方法添加到ViewGroup中,而ViewGroup層層嵌套到最頂級都會顯示在在一個窗口Window中post

WindowManager

咱們仍是看下源碼說明學習

/*   The interface that apps use to talk to the window manager.
Use Context.getSystemService(Context.WINDOW_SERVICE) to get one of these.
*/
public interface WindowManager extends ViewManager {
    public static class BadTokenException extends RuntimeException{...}
    public static class InvalidDisplayException extends RuntimeException{...}
    public Display getDefaultDisplay();
    public void removeViewImmediate(View view);
    public static class LayoutParams extends ViewGroup.LayoutParams
        implements Parcelable

能夠看到WindowManager是一個接口,並且它繼承與ViewManager。WindowManager字面理解就是窗口管理器,每個窗口管理器都與一個的窗口顯示綁定。獲取實例能夠經過
Context.getSystemService(Context.WINDOW_SERVICE)獲取。既然繼承了ViewManager,那麼它也就能夠進行添加刪除View的操做了,不過它的操做放在它的實現類WindowManagerImpl裏面。成員變量裏面this

  • BadTokenException:則是addView時它的LayoutParams無效則會被拋出,或是添加第二個View的時候沒有移除第一個View則會被拋出
  • InvalidDisplayException:若是一個窗口是在一個二級的顯示上而指定的顯示找不到則會被拋出
  • getDefaultDisplay:返回當前WindowManager管理的顯示Display
  • removeViewImmediate:表示從窗口上移除View,通常是當View調用了onDetachedFromWindow也就是從Window上分開後,把它移除。
  • LayoutParams:靜態內部類。顯然是Window的佈局參數,裏面定義了一系列的窗口屬性。

WindowManagerImpl

WindowManager的實現類spa

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Display mDisplay;
    private final Window mParentWindow;
     @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
    ...
     @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }
}

能夠看到WindowManagerImpl裏面有一個成員變量WindowManagerGlobal,而真正的實現則是在WindowManagerGlobal了,相似代理,只不過WindowManagerGlobal是個沒有實現WindowManager的類的,本身定義了套實現。代理

public final class WindowManagerGlobal {
    private static final String TAG = "WindowManager";
     public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            ...
     }
}

聯繫

大概瞭解了上述類的分類和各自的做用,那麼他們之間如何聯繫,Window如何建立如何與WindowManager綁定與Activity綁定呢,這個時候就須要一個場景來逐一理解。咱們都知道每個Activity都是與一個Window綁定一塊兒的,那麼Window的建立以及WindowManager的綁定會不會在建立啓動Activity的過程當中就綁定的呢。

對於Activity的啓動過程,是有兩種,一種是點擊程序進入啓動的Activity,另外一種而是在已有的Activity中調用startActivity,啓動期間經過Binder驅動ActivityWindowService,ActivityThread,ApplicationThread,ActivityStack ,Activity之間進行通訊,爲當前Activity建立進程分配任務棧後啓動Activity。這裏就跳過前面不少步驟,直接到了ActivityThread.handleLaunchActivity去查看Activity的建立

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       ...

        // Initialize before creating the activity
        WindowManagerGlobal.initialize();

        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
            ...
    }

能夠看到 WindowManagerGlobal.initialize()則經過WindowManagerGlobal建立了WindowManagerServer,接下來調用了performLaunchActivity

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        Activity activity = null;
        try { //Activity經過ClassLoader建立出來
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);  
        } ...
        try {
            //建立Application
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            ...
            if (activity != null) {
                //建立Activity所需的Context
                Context appContext = createBaseContextForActivity(r, activity);
                ...
                //將Context與Activity進行綁定
                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);
                ...
                    //調用activity.oncreate
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                ...
                     //調用Activity的onstart方法
                     activity.performStart();
                           //調用activitu的OnRestoreInstanceState方法進行Window數據恢復 
             mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
                ...
        return activity;
    }

先經過調用 activity = mInstrumentation.newActivity建立Activity,能夠看到裏面是經過ClassLoader來加載的

public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
    }

接着建立Activity所需的Application和Context,再調用到activity.attach

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
        //ContextImpl的綁定
        attachBaseContext(context);
        //在當前Activity建立Window
        mWindow = new PhoneWindow(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        ...
        //爲Window設置WindowManager
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        //建立完後經過getWindowManager就能夠獲得WindowManager實例
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }

能夠看到在Activity建立到attach的時候,對應的Window窗口也被建立起來,並且Window也與WindowManager綁定。而mWindow,和mWindowManager則是Activity的成員變量。能夠看到這裏WindiwManager的建立是context.getSystemService(Context.WINDOW_SERVICE)

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        ...
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

接着建立WindowManager的實現類,咱們平時在Activity中使用getWindow()和getWindowManager,就是返回對應這兩個成員變量。

回到前面那個方法,調用了activity.attach後建立了Window和WindowManager,以後調用了

mInstrumentation.callActivityOnCreate(activity, r.state ...);

該方法則是調用activity.oncreate方法的

public void callActivityOnCreate(Activity activity, Bundle icicle) {
        prePerformCreate(activity);
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }
final void performCreate(Bundle icicle) {
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

以後直接調用了

activity.performStart();

來調用activity.onstart()方法
一樣以後也調用了

mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state...);

看到onRestoreInstanceState是否是很熟悉,沒錯就是Activity數據恢復調用的方法

public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) {
        activity.performRestoreInstanceState(savedInstanceState);
    }
final void performRestoreInstanceState(Bundle savedInstanceState) {
        onRestoreInstanceState(savedInstanceState);
        restoreManagedDialogs(savedInstanceState);
    }

裏面經過Bundle來保存恢復Window窗口信息
performLaunchActivity調用完後回到handleLaunchActivity

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       ...

        //初始化WindowManagerGlobal,爲以後addView準備
        WindowManagerGlobal.initialize();

        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
            ...
    }

調用了performLauncherActiviy來建立Activity以及Activity所須要的Context,Window,調用了Activity的onCreate,onStart方法,而接下來調用了handleResumeActivity方法

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {

        //調用activity.onResume,把activity數據記錄更新到ActivityClientRecord
        ActivityClientRecord r = performResumeActivity(token, clearHide);
 
        if (r != null) {
            final Activity a = r.activity;
            //activity.mStartedActivity是用來標記啓動Activity,有沒有帶返回值,通常咱們startActivity(intent)是否默認是startActivityForResult(intent,-1),默認值是-1,因此這裏mStartedActivity = false
            boolean willBeVisible = !a.mStartedActivity;
            ...
            //mFinished標記Activity有沒有結束,而r.window一開始activity並未賦值給ActivityClientRecord,因此這裏爲null
            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;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    //把當前的DecorView與WindowManager綁定一塊兒
                    wm.addView(decor, l);
                }

            ...
            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
             //標記當前的Activity有沒有設置新的配置參數,好比如今手機是橫屏的,而以後你轉成豎屏,那麼這裏的newCofig就會被賦值,表示參數改變
                if (r.newConfig != null) {
                    r.tmpConfig.setTo(r.newConfig);
                    if (r.overrideConfig != null) {
                        r.tmpConfig.updateFrom(r.overrideConfig);
                    }
                    //而後調用這個方法回調,表示屏幕參數發生了改變
                    performConfigurationChanged(r.activity, r.tmpConfig);
                ...
                WindowManager.LayoutParams l = r.window.getAttributes();
                ...//改變以後update更新當前窗口的DecorView
                    if (r.activity.mVisibleFromClient) {
                        ViewManager wm = a.getWindowManager();
                        View decor = r.window.getDecorView();
                        wm.updateViewLayout(decor, l);
                    }
                }
                //參數沒改變
                r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                if (r.activity.mVisibleFromClient) {
                //因爲前面設置了INVASIBLE,因此如今要把DecorView顯示出來了
                    r.activity.makeVisible();
                }
            }

           //通知ActivityManagerService,Activity完成Resumed
             ActivityManagerNative.getDefault().activityResumed(token);
           
    }

handleResumeActivity方法一開始就調用了activity = performResumeActivity()方法

public final ActivityClientRecord performResumeActivity(IBinder token,
            boolean clearHide) {
        ActivityClientRecord r = mActivities.get(token);
      ...
                r.activity.mStartedActivity = false;
                r.activity.onStateNotSaved();
                r.activity.mFragments.noteStateNotSaved();
       ...    //Activity調用onResume,就再也不貼出來了,裏面還有判斷要不呀奧onReStart,這個想必知道Activity生命週期的人就秒懂了
                r.activity.performResume();
       ...
                r.paused = false;
                r.stopped = false;
                r.state = null;
                r.persistentState = null;
         
        return r;
    }

performResumeActivity則是讓Activity調用onResume方法,同時把Activity的信息記錄在ActivityClientRecord
以後進入到這裏這個判斷方法。前面代碼註釋也有說到一些,這裏再說詳細一點。

boolean willBeVisible = !a.mStartedActivity;
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;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    //把當前的DecorView與WindowManager綁定一塊兒
                    wm.addView(decor, l);
                }

r.window一開始null的,activity並無把它的window賦值給它
a.finished表示Activity是否結束
activity.mStartedActivity是用來標記啓動Activity需不須要帶返回值,通常咱們startActivity(intent)是否默認是startActivityForResult(intent,-1),默認值是-1,因此這裏mStartedActivity = false

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

接着獲取當前Activity的Window進而獲取到DecorView,再獲取當前Activity的WindowManager,將DecorView與WindowManager綁定一塊兒。
注意:
這裏的DecorView是setContentView以後的DecorView,也就是裝載咱們的佈局內容的。前面講到在handleLaucheActivity中,它會先調用performLaunchActivity,再調用handleResumeActivity方法,而在performLaunchActivity方法中先建立Activity對象,接着調用activity.attach方法,來綁定Context,同時在attach中建立了PhoneWindow以及WindowManager,attach以後,就調用了activity.oncreate方法,要知道,咱們的setContentView是放在onCreate方法中的。有看過上一篇setContentView的源碼這裏應該就會懂。DecorView是PhoneWindow的成員變量,因此setContentView能夠說是將DecorView建立添加到Window上面的,調用setContentView後已是把你的佈局文件添加到DecorView了。
回到前面,由於是onCreate以後的,因此這裏調用

View decor = r.window.getDecorView();

即可以獲得當前Activity.Window下的DecorView,接下來經過建立好的WindowManager將DecorView與它綁定到一塊兒.

以後到了newConfig參數這裏,前面註釋已經解釋很清楚

if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
             //標記當前的Activity有沒有設置新的配置參數,好比如今手機是橫屏的,而以後你轉成豎屏,那麼這裏的newCofig就會被賦值,表示參數改變
                if (r.newConfig != null) {
                    r.tmpConfig.setTo(r.newConfig);
                    if (r.overrideConfig != null) {
                        r.tmpConfig.updateFrom(r.overrideConfig);
                    }
                    //而後調用這個方法回調,表示屏幕參數發生了改變
                    performConfigurationChanged(r.activity, r.tmpConfig);
                ...
                WindowManager.LayoutParams l = r.window.getAttributes();
                ...//改變以後update更新當前窗口的DecorView
                    if (r.activity.mVisibleFromClient) {
                        ViewManager wm = a.getWindowManager();
                        View decor = r.window.getDecorView();
                        wm.updateViewLayout(decor, l);
                    }
                }

這裏咱們看下performConfigurationChanged,能夠先大膽猜想下這個方法確定是來通知Activity參數改變的一個方法

private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) {
      
        Activity activity = (cb instanceof Activity) ? (Activity) cb : null;
        if (shouldChangeConfig) {
            cb.onConfigurationChanged(config);
             ...
        }
    }

果真,這裏調用到了一個接口回調,注意ComponentCallbacks2這個參數,看它傳進來的參數,是r.activity,也就是說Activity裏面應該是實現了這個接口,接着經過回調去通知參數更改

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback {...}

果真如此。
以後來到了最後一步,handleResumeActivity方法裏的最後

if (r.activity.mVisibleFromClient) {
                //因爲前面設置了INVASIBLE,因此如今要把DecorView顯示出來了
                    r.activity.makeVisible();
                }

要知道,前面咱們的DecorView但是設置了invisible(不知道是否是爲了防止更新閃爍的問題),以後多是要把它設置回來,就是在makevisible方法中

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

能夠看到若是當前DecorView還未添加到WindwManager的話,則從新添加,最後設置爲VISIBLE。
而咱們日常在activity中使用setVisibility,也就是在設置DecorView是VISIBLE仍是INVASIBLE

public void setVisible(boolean visible) {
        if (mVisibleFromClient != visible) {
            mVisibleFromClient = visible;
            if (mVisibleFromServer) {
                if (visible) makeVisible();
                else mDecor.setVisibility(View.INVISIBLE);
            }
        }
    }

至此,Activity被啓動起來,視圖(DecorView)也被建立(Window)管理(WindowManager)起來了。

小結

  • 一個流程圖總結上面流程

Paste_Image.png

  • ViewManager接口定義了一組規則,也就是add、update、remove的操做View接口。ViewGroup實現了該接口
  • WindowManager的實現類是WindowManagerImpl,而它則是經過WindowManagerGlobal代理實現。WindowManager用來在應用與Window之間的接口、窗口順序、消息等的管理

小感言

在看Window和WindowManager的時候,一直不知道要如何根據源碼去學這兩個類。結合之前本身學習的狀況,以爲仍是得根據裏面的使用例子,再結合源碼起來學,纔是最有效的。想到Activity啓動會與Window有關,就着手去找,找着找着就越有思路講。這也算是閱讀源碼的方法之一吧。

上面把DecorView添加到WindowManager,調用到的是WindowManagerGlobal.addView方法,而
該方法中真正把View傳遞給WindowManager的是經過ViewRoot的setView()方法,ViewRoot實現了View和WindowManager之間的消息傳遞。下篇文章將介紹ViewRoot與View和WindowManager之間的聯繫。

上面有什麼口誤或者思路不正確的話還麻煩各位讀者糾正。

Android窗口機制(四)ViewRootImpl與View和WindowManager:http://www.jianshu.com/p/9da7bfe18374

做者:Hohohong 連接:https://www.jianshu.com/p/6afb0c17df43 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。

相關文章
相關標籤/搜索