android Window和ViewRootImpl

1.android的窗口結構(Window,PhoneWindow,DecorView)

每一個Activity都包含一個Window對象,Window對象通常由PhoneWindow實現。activity--phonewindow--decorview這裏沒啥問題,但是decorview的內部描述可以參照下面兩張圖來理解。decorview是一個framelayout下面包含statusBarbackground(狀態欄)+viewroot(Linearlayout來着),viewroot下面包含header(viewstub,有titlebar和actionbar)+content(framelayout),這個content就是我們setcontent的佈局。

1.1看看源碼來驗證下面兩張圖是否正確,首先window的源碼

/**
 * Abstract base class for a top-level window look and behavior policy.  An
 * instance of this class should be used as the top-level view added to the
 * window manager. It provides standard UI policies such as a background, title
 * area, default key processing, etc.
 *
 * <p>The only existing implementation of this abstract class is
 * android.view.PhoneWindow, which you should instantiate when needing a
 * Window.
 * 這裏說window作爲一個頂級view被添加到windowmanager中,它提供了一套標準的ui規則例如背景、標題欄區域等
 * 如果你要使用它,就需要實現它的唯一實現類PhoneWindow
 */
public abstract class Window {
    ...
    @Nullable
    public View findViewById(@IdRes int id) {
        return getDecorView().findViewById(id);
    }

/**
 * Convenience for * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}
 * to set the screen content from a layout resource.  The resource will be * inflated, adding all top-level views to the screen. * * @param layoutResID Resource ID to be inflated.
 * @see #setContentView(View, android.view.ViewGroup.LayoutParams)
 * 這裏就是setcontentView的入口,我們在activity中調用setcontentView其實會直接走到這裏
 */
     public abstract void setContentView(@LayoutRes int layoutResID);
     ...
}

1.2PhoneWindow的源碼,window的唯一實現

public class PhoneWindow extends Window implements MenuBuilder.Callback {

    private final static String TAG = "PhoneWindow";

    ...

    // This is the top-level view of the window, containing the window decor.
    // window的唯一實現類PhoneWindow的成員裏面出現了DecorView
    private DecorView mDecor;

    // This is the view in which the window contents are placed. It is either
    // mDecor itself, or a child of mDecor where the contents go.
    private ViewGroup mContentParent;

    private ViewGroup mContentRoot;
    ...
}

1.3DecorView的源碼(最頂層view)

/**
* DecorView繼承自FrameLayout
*/
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {

        // (狀態欄)View added at runtime to draw under the status bar area
        private View mStatusGuard;
        // (導航欄)View added at runtime to draw under the navigation bar area
        private View mNavigationGuard;
        //viewRoot
        ViewGroup mContentRoot;
        /* package */int mDefaultOpacity = PixelFormat.OPAQUE;

        /** The feature ID of the panel, or -1 if this is the application's DecorView */
        private final int mFeatureId;

        private final Rect mDrawingBounds = new Rect();

        private final Rect mBackgroundPadding = new Rect();

        private final Rect mFramePadding = new Rect();

        private final Rect mFrameOffsets = new Rect();
        ....
 }

2.Window和WindowManager

這裏主要涉及到幾個接口和類:ViewManager,ViewGroup,WindowManager,WindowManagerImpl,Window

2.1ViewManager接口

/** 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()}.
  * ViewManager接口定義了一組規則,也就是add、update、remove的操作View接口
  */
public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

2.2ViewManager的實現ViewGroup

這個就比較熟悉了,我們在日常開發中常使用這幾個方法。

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);
    }
}

2.3Windowmanager源碼

窗口管理器,這也是一個繼承於ViewManager的接口,具體的實現是在實現類WindowManagerImpl裏面

/*   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
}

2.4WindowManagerImpl的實現(window的添加移除更新)

windowmanagerimpl並沒有自己來實現window的添加移除更新操作,直接交給WindowManagerGlobal(藝術探索說是工廠模式可我看源碼怎麼是標準的單例啊)來處理。

public final class WindowManagerImpl implements WindowManager {
    //真正的實現是在WindowManagerGlobal裏面做的
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Display mDisplay;
    private final Window mParentWindow;

    //#分析1:添加window
     @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
    
    //#分析2:移除window
     @Override
    public void removeView(View view) {
        applyDefaultToken(params);
        mGlobal.removeView(view, false);
    }
    
    //#分析3:更新window
    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }
}

3.window的創建過程

Window如何創建,如何跟WindowManager綁定,如何跟activity綁定的,這裏涉及到activity的啓動過程。activity啓動有兩種,一種是launch進入啓動的Activity,另一種而是在已有的Activity中調用startActivity,啓動期間通過Binder驅動ActivityWindowService,ActivityThread,ApplicationThread,ActivityStack ,Activity之間進行通信。現先看ActivityThread的andleLaunchActivity方法,創建activity:

3.1window的創建,在activity.attach時創建Window,爲Window設置WindowManager

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

    // Initialize before creating the activity,在WindowManagerImpl裏有這個成員,創建了WindowManagerServer(WMS)
    WindowManagerGlobal.initialize();
    //創建activity#分析1
    Activity a = performLaunchActivity(r, customIntent);

    if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        Bundle oldState = r.state;
        //調用activity的onresume#分析5
        handleResumeActivity(r.token, false, r.isForward,
        ...
}

/**
** #分析1
**/
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    Activity activity = null;
    try { 
        //Activity通過ClassLoader創建出來
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        //創建activity#分析2
        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進行綁定#分析3
            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#分析4
            mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            ...
            //調用Activity的onstart方法(是不是很熟悉)
            activity.performStart();
            //調用activitu的OnRestoreInstanceState方法進行Window數據恢復(是不是很熟悉) 
         mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                r.persistentState);
            ...
        }
    return activity;
}

/**
** #分析2:創建activity
**/
public Activity newActivity(ClassLoader cl, String className,
        Intent intent)
        throws InstantiationException, IllegalAccessException,
        ClassNotFoundException {
    return (Activity)cl.loadClass(className).newInstance();
}

/**
** #分析3:activity創建完attach,這時候創建Window,爲Window設置WindowManager
**/
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;
}

/**
** #分析4:調用activity.oncreate方法
**/
public void callActivityOnCreate(Activity activity, Bundle icicle) {
    prePerformCreate(activity);
    activity.performCreate(icicle);
    postPerformCreate(activity);
}

3.2在onResume裏面,將當前的DecorView與WindowManager綁定一起(wm.addView(decor),也就是咋這時候viewrootimpl才真正的被創建起來

**
** #分析5:調用activity.onresume獲取到在前面activity.attach創建的window,
** 進而獲取到DecorView與WindowManager
** 就是在這裏將DecorView與WindowManager綁定在一起的。
**/
final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {

    //調用activity.onResume,把activity數據記錄更新到ActivityClientRecord#分析6
    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) {
            //由於前面設置了INVISIBLE,所以現在要把DecorView顯示出來了
            //這裏也證實了view是在onResume的時候才真正被看到的。
                r.activity.makeVisible();
            }
        }

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

**
** #分析6:調用activity.onResume方法
**/
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
    r.activity.performResume();
...
    r.paused = false;
    r.stopped = false;
    r.state = null;
    r.persistentState = null;
     
    return r;
}

3.3將decorview附屬到window上,從setContentView添加布局入口查起,主要包含兩部分:創建decorview和將decorview添加到contentParent

  • 3.3.1創建decorview
/**
** activity
**/
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

/**
** getWindow()得到的其實就是activity裏面的PhoneWindow
**/
@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) {
        //創建DecorView,並添加到mContentParent上--#分析1
        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 {
        //將要加載的資源inflate轉化爲view樹,並添加到mContentParent上
        //爲什麼叫setContentView,將view設到contentParent裏面
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        //最後調用Callback來通知界面發生改變。Callback是Window裏面的一個接口,
        //裏面聲明瞭當界面更改觸摸時調用的各種方法
        cb.onContentChanged();
    }
}

/**
** #分析1創建並添加DecorView
**/
private void installDecor() {
    //首次調用會走這裏
    if (mDecor == null) {
        //調用該方法創建new一個DecorView
        mDecor = generateDecor();//return new DecorView(getContext(), -1);
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    }
    //#分析2一開始DecorView未加載到mContentParent,所以此時mContentParent=null
    if (mContentParent == null) {
    //該方法將mDecorView添加到Window上綁定佈局
    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);
        
    ...//添加其他資源
    ...//設置轉場動畫
    }
}

/**
** #分析2將decorview添加到contentParent
**/
protected ViewGroup generateLayout(DecorView decor) {
    // Apply data from current theme.
    //根據當前設置的主題來加載默認佈局
    TypedArray a = getWindowStyle();
    //如果你在theme中設置了window_windowNoTitle,則這裏會調用到,其他方法同理,
    //我們在activity裏面設置的requestFeature,Flags全屏等等就在這裏起作用。
    //這裏也說明了我們要在setContentView之前調用requesetFeature的原因,爲什麼在之後設置無效。
    //這裏是根據你在theme中的設置去設置的
    if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
        requestFeature(FEATURE_NO_TITLE);
    } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
        // Don't allow an action bar if there is no title.
        requestFeature(FEATURE_ACTION_BAR);
    }
    //是否有設置全屏
    if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
        setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
    }
    
    ...//省略其他加載資源
    
    // 添加布局到DecorView,前面說到,DecorView是繼承與FrameLayout,
    //它本身也是一個ViewGroup,而我們前面創建它的時候,只是調用了new DecorView,
    //此時裏面並無什麼東西。而下面的步奏則是根據用戶設置的Feature來創建相應的默認
   //佈局主題。舉個例子,如果我在setContentView之前調用了requestWindowFeature(Window.FEATURE_NO_TITLE),
   //這裏則會通過getLocalFeatures來獲取你設置的feature,進而選擇加載對應的佈局,
   //此時則是加載沒有標題欄的主題,對應的就是R.layout.screen_simple

    int layoutResource;
    int features = getLocalFeatures();
    // System.out.println("Features: 0x" + Integer.toHexString(features));
    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
        layoutResource = R.layout.screen_swipe_dismiss;
    } ... //省略其他判斷方法
    } else {
        // Embedded, so no decoration is needed.
        layoutResource = R.layout.screen_simple;
        // System.out.println("Simple!");
    }

    mDecor.startChanging();
    //選擇對應佈局創建添加到DecorView中,之前的decorview是空的只是加載了主題等,這裏纔是真正的添加內容
    View in = mLayoutInflater.inflate(layoutResource, null);
    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    mContentRoot = (ViewGroup) in;
    //ID_ANDROID_CONTENT = com.android.internal.R.id.content
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ...
    return contentParent;
}
  • 3.3.2將decorview添加到contentParent
public void setContentView(int layoutResID) {
        installDecor();
        *********略***************
        //將要加載的資源inflate轉化爲view樹,並添加到mContentParent上
        //爲什麼叫setContentView,將view設到contentParent裏面
        mLayoutInflater.inflate(layoutResID, mContentParent);
}

4.window的內部機制(添加、刪除、更新)

首先聲明幾個成員:

mViews:所有window對應的views

mRoots:所有window對應的ViewRootImpl

mParams:所有window對應的佈局參數

mDyingViews:待刪除(或者可以說正在被刪除)的view對象

4.1window的添加

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>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();

/**
**具體實現的地方
**/
public final class WindowManagerGlobal {
    private static final String TAG = "WindowManager";
     //#分析1
     public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ...
        root = new ViewRootImpl(view.getContext(), display);
        //設置LayoutParams
        view.setLayoutParams(wparams);
        
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        // do this last because it fires off messages to start doing things
        try {
            //ViewRootImpl開始繪製view#添加window分析1
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
     }
}

/**
 ** We have one child
 **#添加window分析1
 **/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            *********略********
            mAdded = true;
            int res; /* = WindowManagerImpl.ADD_OKAY; */
            // 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.
            #添加window分析2
            requestLayout();
            try {
                **************略****************
                //#添加window分析3:WindowSession最終來完成Window的添加過程,WindowSession是一個binder對象,
                //也就是說addToDisplay是IPC過程,遠程調用了Session中的addToDisPlay方法
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mInputChannel);
            } catch (RemoteException e) {
            }
            ******************略*****************
    }
}

/**
 **#添加window分析2
 **/
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        //添加window分析4
        //這裏view的繪製的入口了,最終會調用performTraversals方法來完成View的繪製。
        scheduleTraversals();
    }
}

/**
 **#添加window分析3
 **這裏的mservice就是windowmanagerservice,添加window的過程最終是由它來完成的
 **/
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
        Rect outOutsets, InputChannel outInputChannel) {
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
            outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

/**
 **#添加window分析4
 **/
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        ...
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
      ...
    }
}

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}

void doTraversal() {
    ...
    performTraversals();
    ...
}

//有沒有很熟悉,view的繪製三部曲
private void performTraversals() {  
    ......  
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
    performLayout(lp, desiredWindowWidth, desiredWindowHeight);
    ......  
    performDraw();
    }
    ......  
}

4.2window的刪除

/**
**#刪除window分析1
**/
public void removeView(View view, boolean immediate) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }

    synchronized (mLock) {
        //先遍歷知道index下標
        int index = findViewLocked(view, true);
        //找到當前view
        View curView = mRoots.get(index).getView();
        //移除#刪除window分析1
        removeViewLocked(index, immediate);
        if (curView == view) {
            return;
        }

        throw new IllegalStateException("Calling with view " + view
                + " but the ViewAncestor is attached to " + curView);
    }
}

/**
**#刪除window分析2
**/
private void removeViewLocked(int index, boolean immediate) {
    //根據下標得到viewroot
    ViewRootImpl root = mRoots.get(index);
    View view = root.getView();

    if (view != null) {
        InputMethodManager imm = InputMethodManager.getInstance();
        if (imm != null) {
            imm.windowDismissed(mViews.get(index).getWindowToken());
        }
    }
    //執行die刪除#刪除window分析3
    boolean deferred = root.die(immediate);
    if (view != null) {
        view.assignParent(null);
        if (deferred) {
            mDyingViews.add(view);
        }
    }
}

/**
**#刪除window分析3,immediate表示是否立即刪除:true同步操作, false異步操作,大多數情況使用false
**/
boolean die(boolean immediate) {
    // Make sure we do execute immediately if we are in the middle of a traversal or the damage
    // done by dispatchDetachedFromWindow will cause havoc on return.
    if (immediate && !mIsInTraversal) {
        //#刪除window分析4
        doDie();
        return false;
    }

    if (!mIsDrawing) {
        destroyHardwareRenderer();
    } else {
        Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
                "  window=" + this + ", title=" + mWindowAttributes.getTitle());
    }
    //一般情況我們會通過handler發個消息通知移除window
    mHandler.sendEmptyMessage(MSG_DIE);
    return true;
}

/**
**#刪除window分析4
**/
void doDie() {
    checkThread();
    if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
    synchronized (this) {
        if (mRemoved) {
            return;
        }
        mRemoved = true;
        if (mAdded) {
            //這裏胡調用mWindowSession.remove(mWindow);,它同樣是一個IPC過程,最終會調用WMS的removeWindow
            dispatchDetachedFromWindow();
        }
        *************略**************
    }
    //刷新數據
    WindowManagerGlobal.getInstance().doRemoveView(this);
}

4.3window的更新

/**
**#更新window
**/
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
    //更新view的LayoutParams
    view.setLayoutParams(wparams);

    synchronized (mLock) {
        int index = findViewLocked(view, true);
        ViewRootImpl root = mRoots.get(index);
        mParams.remove(index);
        mParams.add(index, wparams);
        //更新viewroot的LayoutParams,並且重新繪製view(調用了requestLayout)
        root.setLayoutParams(wparams, false);
    }
}

5.ViewRootImpl與View和WindowManager之間關係

在前面的圖二中decorView下面除了statusbarbackground還有個ViewRootImpl(2.2就替換ViewRoot爲ViewRootImpl了),它是一個view視圖頂層結構,WindowManagerGlobal的大部分實現都是和ViewRootImpl相關的,它類似於view和WindowManager之間的橋樑,真正操縱繪製view的是viewrootimpl,view通過windowmanager調用viewrootimpl。調用順序如圖:view -> WindowManager -> WindowManagerImpl -> WindowManagerGlobal -> ViewRootImpl.

6.WindowManager.LayoutParams和Token以及其他窗口Dialog,Toast

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章