Android源碼-深入理解Window和WindowManager

前言

Window 表示一個(gè)窗口的概念虏束,Android中所有的視圖都是通過Window來呈現(xiàn)的洪添,不管是Activity绅项、Dialog、還是Toast赋续,它們的視圖實(shí)際上都是附加在Window上的男翰,因此,Window實(shí)際是View的管理者纽乱。Window是一個(gè)非常重要的子系統(tǒng)奏篙,這也是我們常說的WMS(WindowManagerService)。下面我們就分析一下Window迫淹、WMSView建立關(guān)聯(lián)以及交互的一個(gè)基本過程为严。

Window體系相關(guān)UML類圖

Window.png
  • Session :是一個(gè)Binder對(duì)象敛熬,代表一個(gè)活躍的客戶端會(huì)話,在每個(gè)進(jìn)程中都有一個(gè)
    SessionWindowManager交互的對(duì)象第股。
  • WindowManagerService :也是一個(gè)Binder對(duì)象应民,負(fù)責(zé)對(duì)窗口的管理。
  • Window :應(yīng)用程序用來與窗口管理器交談的界面夕吻。
  • PhoneWindowWindow 的具體實(shí)現(xiàn)诲锹。
  • WindowManagerImpl : 負(fù)責(zé)與系統(tǒng)窗口管理器通信、綁定到上下文涉馅、顯示的操作归园。
  • ViewRootImpl :負(fù)責(zé)View的(測(cè)量、擺放稚矿、繪制)三大流程庸诱。
  • WindowManagerGlobalWindowManager 的具體實(shí)現(xiàn)捻浦。

WindowManager

WindowManager聯(lián)系上的第一步就是通過ContextgetSystemService()方法,在分析文章Android源碼中單例模式 中我們知道桥爽,各種系統(tǒng)服務(wù)會(huì)注冊(cè)到ContextImpl的一個(gè)map容器中朱灿,然后通過該服務(wù)的字符串鍵獲取,WindowManager也是ContextImpl中注冊(cè)的眾多服務(wù)之一钠四,我們看下下面這段程序:

        //窗口服務(wù)
        registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx);
            }});

最后一行代碼中盗扒,我們看到了WindowManager在Java層的具體實(shí)現(xiàn),也就是WindowManagerImpl缀去。那Activity或者Dialog又是如何獲取到WindowManager對(duì)象呢侣灶?我們從上述代碼知道,WindowManager是注冊(cè)到ContextImpl中的朵耕,而getSystemService也是Context定義的接口炫隶,因此,我們就從Dialog的構(gòu)造函數(shù)和Activity入手阎曹,因?yàn)?code>Context是傳到Dialog構(gòu)造函數(shù)的伪阶。

Dialog構(gòu)造函數(shù)


    Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
        //獲取WindowManager
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

        final Window w = new PhoneWindow(mContext);
        mWindow = w;
        //設(shè)置Window回調(diào)
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setOnWindowSwipeDismissedCallback(() -> {
            if (mCancelable) {
                cancel();
            }
        });
        //設(shè)置Window的WindowManager對(duì)象
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);
    }

Activity的attch方法

    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,
            Window window) {
        ...
         //創(chuàng)建Window并設(shè)置window的監(jiān)聽
        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        ...
        //Window設(shè)置WindowManager對(duì)象
        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());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }

無論是Dialog還是Activity都是通過Window對(duì)象的setWindowManager方法將WindowManagerWindow關(guān)聯(lián)。該函數(shù)是在Window中处嫌,看看實(shí)現(xiàn):

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

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

最后調(diào)用的是WindowManagerImplcreateLocalWindowManager方法栅贴,這里與ContextImpl注冊(cè)的WindowManagerImpl不同的是,這里多了一個(gè)parentWindow參數(shù)熏迹,也就是說檐薯,此時(shí)構(gòu)建的WindowManagerImpl對(duì)象是與具體的Window關(guān)聯(lián)的,而ContextImpl注冊(cè)的并沒有此參數(shù)注暗。這是Window已經(jīng)和WindowManager建立了初步聯(lián)系坛缕。為什么這么說呢?我們看下WindowManagerImpl 的具體實(shí)現(xiàn):

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

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }

    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

    @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }

    @Override
    public Display getDefaultDisplay() {
        return mContext.getDisplay();
    }
}

顯然WindowManagerImpl還沒有實(shí)現(xiàn)對(duì)視圖的操作捆昏,Dialog是在show()方法里添加的赚楚,添加、更新骗卜、刪除都交給了WindowManagerGlobal這個(gè)類宠页,通過以上分析對(duì)VIew的操作實(shí)際上是調(diào)用的是WindowManagerGlobal的方法,繼續(xù)跟蹤:

WindowManagerGlobal對(duì)View的操作

看上面UML類圖我看到WindowManagerGlobal有幾個(gè)重要的屬性寇仓,

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

在上面聲明中举户,沒mViews存儲(chǔ)的是所有Window所對(duì)應(yīng)的ViewmRoots存儲(chǔ)的是所有Window多對(duì)用的ContextImpl,mParams存儲(chǔ)的是所有Window的布局參數(shù)遍烦,而mDyingViews則存儲(chǔ)了那些整被刪除的View對(duì)象俭嘁,或者說那些已經(jīng)調(diào)用removeView方法但是刪除操作還未完成的Window對(duì)象,在addView中將Window一系列對(duì)象添加到容器中乳愉。

WindowManagerGlobal的addView過程


    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ...//省略參數(shù)檢查代碼
        ViewRootImpl root;
        View panelParentView = null;
            ...
            //創(chuàng)建ViewRootImpl
            root = new ViewRootImpl(view.getContext(), display);
            //設(shè)置參數(shù)
            view.setLayoutParams(wparams);
            //添加到容器列表中
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        // 調(diào)用ViewRootImpl的setView方法將VIew顯示到手機(jī)上
            root.setView(view, wparams, panelParentView);
    }

上面程序主要完成以下工作;

  • 構(gòu)建ViewRootImpl兄淫;
  • 將布局參數(shù)設(shè)置給View屯远;
  • 存儲(chǔ)這些ViewRootImplView捕虽、LayoutParam到列表中慨丐;
  • 通過ViewRootImplsetViewView顯示到窗口。

很多人對(duì)ViewRootImpl并不陌生泄私,從UML類圖可以看出這個(gè)類里面有一個(gè)我們熟知的performTraversals方法房揭,ViewRootImpl收到系統(tǒng)繪制View的消息后performTraversals就會(huì)調(diào)用視圖樹的各個(gè)節(jié)點(diǎn)的meaturelayout晌端、draw方法來繪制整顆視圖樹捅暴。

從上述代碼分析來看,第一個(gè)重要步驟就是創(chuàng)建了ViewRootImpl對(duì)象咧纠,我們看看它的構(gòu)造方法:

    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        //獲取Window Session蓬痒,也就是也WindowManagerService建立聯(lián)系
        mWindowSession = WindowManagerGlobal.getWindowSession();
        //保存當(dāng)前線程,漆羔,更新Ui的 線程只能是創(chuàng)建ViewRootImpl時(shí)的線程梧奢,
        //我們?cè)陂_發(fā)中,如果在子線程更新UI會(huì)拋出異常演痒,但并不是因?yàn)橹挥蠻I線程才能更新UI
        //而是因?yàn)閂iewRootImpl是在UI線程中創(chuàng)建的
        mThread = Thread.currentThread();
        ...
    }

獲取WindowManagerService

我們看下ViewRootImpl的構(gòu)造函數(shù)中是如何獲取到WindowManagerService的:

    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    //獲取WindowManagerService
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            imm.getClient(), imm.getInputContext());
            }
            return sWindowSession;
        }
    }
    //獲取WindowManagerService
    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                //aidl
                sWindowManagerService = IWindowManager.Stub.asInterface(
                ServiceManager.getService("window"));
            }
            return sWindowManagerService;
        }
    }

getWindowSession方法中亲轨,FrameWork層首先通過getWindowManagerService方法獲取IWindowManager對(duì)象,該函數(shù)中通過ServiceManager.getService方法獲取WMS鸟顺,并且將WMS轉(zhuǎn)換為IWindowManager類型惦蚊,我們先看看ServiceManager.getService方法:

    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return Binder.allowBlocking(getIServiceManager().getService(name));
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }

從程序中可以看到ServiceManager.getService返回的是一個(gè)IBinder對(duì)象,也就是說Android Framework層與WMS之間也是通過Binder機(jī)制進(jìn)行通訊讯嫂。獲取WMS之后蹦锋,又調(diào)用IWindowManager.Stub類的asInterface方法,看到這里我們就會(huì)想起AIDL欧芽,詳情請(qǐng)看這篇文章理解AIDL 晕粪,將獲取到的WMSIBinder對(duì)象轉(zhuǎn)換成WindowManager對(duì)象,最后渐裸,通過openSession函數(shù)來與WMS建立一個(gè)通信會(huì)話,相當(dāng)于Framework層與native層建立了一個(gè)長(zhǎng)期合作的”辦事處“装悲,雙方有什么需求都通過這個(gè)Session來交換信息昏鹃。

ViewRootImpl的setView方法

WMS建立Session后就到了ViewRootImplsetView方法了,該方法會(huì)向WMS發(fā)起顯示Dialog或者Activity中的DecorView請(qǐng)求诀诊,具體代碼:

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
                // 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.
                //請(qǐng)求布局
                requestLayout();
                //向WMS發(fā)起請(qǐng)求
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);

            }
        }
    }

setView過程比較復(fù)雜洞渤,但我們只需要關(guān)注兩步:

  • requestLayout();
  • WMS發(fā)起顯示當(dāng)前Window請(qǐng)求

ViewRootImpl的requestLayout過程

我們?cè)賮砜聪?code>requestLayout方法

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            //發(fā)起繪制
            scheduleTraversals();
        }
    }

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //注意第二個(gè)參數(shù),第一個(gè)第三個(gè)省略
            mChoreographer.postCallback(...  , mTraversalRunnable , ... );
            ...
        }
    }
    
    //創(chuàng)建子線程去繪制
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

    void doTraversal() {
        //繪制入口
        performTraversals();
    }

最終會(huì)執(zhí)行performTraversals();方法属瓣,這是一個(gè)極其復(fù)雜有非常重要的函數(shù)载迄。主要做了如下操作:

  • 獲取Surface對(duì)象讯柔,同于圖形繪制
  • 測(cè)量視圖樹中各個(gè)View的大小,performMeasure()
  • 擺放整個(gè)視圖樹,performLayout()
  • 繪制整棵視圖樹,performDraw()

代碼如下:

    private void performTraversals() {
        //會(huì)調(diào)用View的onMeasure
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        //會(huì)調(diào)用View的onLayout
        performLayout(lp, mWidth, mHeight);
        //會(huì)調(diào)用View的Draw
        performDraw();
    }

performDraw方法中,Framework層獲取到圖形繪制表面的Surface對(duì)象护昧,然后獲取它的可繪制區(qū)域魂迄,也就是我們的Canvas對(duì)象,然后Framework在這個(gè)Canvas對(duì)象上繪制惋耙,具體代碼如下;

    private void performDraw() {
      
            draw(fullRedrawNeeded);
    }

    private void draw(boolean fullRedrawNeeded) {
        //獲取繪制表面
        Surface surface = mSurface;
        if (!surface.isValid()) {
            return;
        }
        ...
        if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
            //使用GPU繪制捣炬,也就是硬件加速
            if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                ...
                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
            } else {
                //使用CPU繪制圖形
                if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                    return;
                }
            }
        }

    }

draw方法中會(huì)獲取到需要繪制的區(qū)域,以及判斷是否使用GPU進(jìn)行繪制绽榛。通常情況下使用的是CPU繪制湿酸,也就是調(diào)用的是drawSoftware()。我們看看該函數(shù)的實(shí)現(xiàn):

    //使用CPU繪制
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {

        // Draw with software renderer.
        final Canvas canvas;
        try {
            //獲取指定區(qū)域的獲取指定區(qū)域的Canvas對(duì)象對(duì)象灭美,用于繪制
            canvas = mSurface.lockCanvas(dirty);
        }
        ...
        try {
            ...
            //從DecorView開始繪制推溃,也就是整個(gè)Window的根視圖,整棵樹都會(huì)繪制
            mView.draw(canvas);     
        } finally {
            try {
                //釋放Canvas鎖届腐,然后通知Surface更新這塊區(qū)域铁坎,與開頭照應(yīng)
                surface.unlockCanvasAndPost(canvas);
        }
        return true;
    }

綜上所述,上述的視圖樹繪制代碼主要分為下面幾個(gè)步驟:

  • 判斷是CPU還是GPU繪制
  • 獲取繪制表面的Surface對(duì)象
  • 通過Surface對(duì)象獲取并鎖住Canvas繪圖對(duì)象
  • DecorView開始發(fā)起整顆樹的繪制流程
  • Surface對(duì)象解鎖Canvas梯捕,并通知SurfaceFlinger更新視圖

了解具體View的三大流程請(qǐng)看文章:

以上就是整個(gè)視圖的繪制過程厢呵,但是此時(shí)Dialog或者ActivityView并不能顯示在手機(jī)屏幕上,WMS只是負(fù)責(zé)管理手機(jī)上的View傀顾,也就是說WMS管理當(dāng)前狀態(tài)下那個(gè)View應(yīng)該顯示在最上層襟铭。其實(shí)WMS管理的并不是Window,而是View短曾,只不過他管理的是屬于某個(gè)WIndow下的View寒砖。

ViewRootImpl請(qǐng)求WMS添加Window過程

我們上面只是分析了ViewRootImplrequestLayout過程,下面再回到ViewRootImplsetView方法嫉拐,繪制完成接著會(huì)通過WindowSession最終來完成WIndow的添加過程哩都,在下面的代碼中mWindowSession的類型是IWindowSession,它也是一個(gè)Binder對(duì)象婉徘,真正的實(shí)現(xiàn)類是Session漠嵌,也就是Window的添加過程是一次IPC調(diào)用。

    
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
             getHostVisibility(), mDisplay.getDisplayId(),
             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
             mAttachInfo.mOutsets, mInputChannel);

在Session內(nèi)部會(huì)通過WindowManagerService來實(shí)現(xiàn)Window的添加盖呼,代碼如下:

frameworks\base\services\core\java\com\android\server\wm\Session.java

    @Override
    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的添加過程就交給了WindowManagerService去處理了儒鹿,在WMS內(nèi)部會(huì)保留一個(gè)單獨(dú)的Session。具體WindowWMS內(nèi)部如何添加的几晤,本篇不對(duì)分析约炎,至此對(duì)于View的繪制以及視圖如何添加到Window整個(gè)流程已經(jīng)很明了了。關(guān)于WMSSurface系統(tǒng)的細(xì)節(jié)可以參考市面上關(guān)于源碼的書籍。我們看下Window的刪除過程

WindowManagerGlobal的removeView過程

Window的刪除過程和添加過程一樣圾浅,都是先通過WIndowManagerImpl后掠手,在進(jìn)一步通過WindowManagerGlobal來實(shí)現(xiàn)刪除,下面是WindowManagerGlobalremoveView的實(shí)現(xiàn):

    public void removeView(View view, boolean immediate) {
        ...
        synchronized (mLock) {
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate);
        }
      ...
    }

removeView 的過程很清晰狸捕,首先通過findViewLoched來查找待刪除View的索引喷鸽,這個(gè)查找過程就是建立的數(shù)組遍歷,然后再調(diào)用removeViewLocked來做進(jìn)一步的刪除府寒,如下所示:

    private void removeViewLocked(int index, boolean immediate) {
        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());
            }
        }
        boolean deferred = root.die(immediate);
        if (view != null) {
            view.assignParent(null);
            if (deferred) {
                mDyingViews.add(view);
            }
        }
    }

removeViewLocked是通過ViewRootImpl來完成刪除操作的魁衙。在WIndowManager中提供了兩個(gè)接口removeViewremoveViewImmediate,分別表示異步刪除和同步刪除株搔,一般不使用同步刪除剖淀,以免發(fā)生意外的錯(cuò)誤,這里主要說下異步刪除的情況纤房,具體的異步刪除操作是由ViewRootImpldie方法完成纵隔,在異步刪除的情況下,die方法只是發(fā)送了一個(gè)請(qǐng)求刪除的消息后就立刻返回了炮姨,這個(gè)時(shí)候View并沒有完成刪除操作捌刮,所以最后會(huì)將其添加到WindowManagerGlobal的待刪除列表mDyingViews中,看下ViewRootImpldie方法實(shí)現(xiàn):

    boolean die(boolean immediate) {
        //同步刪除  直接調(diào)用doDie舒岸,并返回
        if (immediate && !mIsInTraversal) {
            doDie();
            return false;
        }
        ...
        //發(fā)送handler消息
        mHandler.sendEmptyMessage(MSG_DIE);
        return true;
    }

在die方法內(nèi)部只是做了簡(jiǎn)單的判斷绅作,如果是異步刪除,那么就發(fā)送一個(gè)MSG_DIE 的消息蛾派,ViewRootImpl中的Handler會(huì)調(diào)用doDie方法俄认,如果是同步刪除,就不發(fā)送消息洪乍,直接調(diào)用doDie方法眯杏,這就是這兩種方法的區(qū)別。在doDie方法中會(huì)調(diào)用dispatchDepatchedFromWindow方法壳澳,真正刪除View的邏輯在dispatchDepatchedFromWindow內(nèi)部實(shí)現(xiàn)岂贩,代碼如下:

    void doDie() {
        dispatchDetachedFromWindow();
        //將WindowManagerGlobal的列表中移除保存的ViewRootImpl、View巷波、Param
        WindowManagerGlobal.getInstance().doRemoveView(this);
    }
    
   void dispatchDetachedFromWindow() {
        //調(diào)用VIew的dispatchDetachedFromWindow();
        mView.dispatchDetachedFromWindow();
        ...
        //Session中的remove
        mWindowSession.remove(mWindow);
        ...
    }
        
    public void remove(IWindow window) {
        //WMS移除Window
        mService.removeWindow(this, window);
    }
    //WindowManagerGlobal中移除保存的ViewRootImpl萎津、View、Param
    void doRemoveView(ViewRootImpl root) {
        synchronized (mLock) {
            final int index = mRoots.indexOf(root);
            if (index >= 0) {
                mRoots.remove(index);
                mParams.remove(index);
                final View view = mViews.remove(index);
                mDyingViews.remove(view);
            }
        }
    }

以上代碼主要做了一下事情:

  • 垃圾回收相關(guān)的工作抹镊,比如清除數(shù)據(jù)和消息姜性,移除回調(diào)
  • 通過Sessionremove方法刪除Window,同樣也是一個(gè)IPC過程髓考,最終會(huì)調(diào)用WMSremoveView方法
  • 調(diào)用ViewdispatchDetachedFromWindow();方法,對(duì)于ViewdispatchDetachedFromWindow()我們不陌生弃酌,當(dāng)ViewWindow中移除時(shí)氨菇,這個(gè)方法就會(huì)被調(diào)用儡炼,可以在這個(gè)方法內(nèi)部做一些資源回收的工作,比如終止動(dòng)畫查蓉、停止線程乌询。
  • 調(diào)用WindowManagerGlobaldoRemoveView方法刷新數(shù)據(jù),包括mViews豌研、mRoots妹田、mParams、mDyingViews鹃共,需要將當(dāng)前Window所關(guān)聯(lián)的這三類對(duì)象從列表中刪除鬼佣。

WindowManagerGlobal的updateViewLayout過程

Window的刪除過程我們已經(jīng)分析完了,下面看下WIndow的更新過程霜浴,還是要從WindowManagerGlobalupdateViewLayout說起晶衷,代碼如下:

    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        ...
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        view.setLayoutParams(wparams);

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false);
        }
    }

updateViewLayout方法做的事情就比較簡(jiǎn)單了,首先他需要更新View的LayoutParams并替換老的LayoutParams阴孟,接著再更新ViewRootImpl中的LayoutParams,這一步是通過ViewRootImplsetLayoutParams方法來實(shí)現(xiàn)的晌纫。在ViewRootImplsetLayoutParams中會(huì)通過scheduleTraversales方法來對(duì)View重新測(cè)量布局以及繪制這三個(gè)過程,在performTraversales會(huì)通過WindowSession來更新Window視圖永丝,這個(gè)過程最終是由WMSrelayoutWindow來具體實(shí)現(xiàn)的锹漱,同樣也是一個(gè)IPC過程。

參考

  • 《Android開發(fā)藝術(shù)探索》
  • 《Android源碼設(shè)計(jì)模式》
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末慕嚷,一起剝皮案震驚了整個(gè)濱河市哥牍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌闯冷,老刑警劉巖砂心,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蛇耀,居然都是意外死亡愕鼓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門露该,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扎附,“玉大人,你說我怎么就攤上這事撩炊⊥庥溃” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵拧咳,是天一觀的道長(zhǎng)伯顶。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么祭衩? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任灶体,我火速辦了婚禮,結(jié)果婚禮上掐暮,老公的妹妹穿的比我還像新娘蝎抽。我一直安慰自己,他們只是感情好路克,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布樟结。 她就那樣靜靜地躺著,像睡著了一般精算。 火紅的嫁衣襯著肌膚如雪瓢宦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天殖妇,我揣著相機(jī)與錄音刁笙,去河邊找鬼。 笑死谦趣,一個(gè)胖子當(dāng)著我的面吹牛疲吸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播前鹅,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼摘悴,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了舰绘?” 一聲冷哼從身側(cè)響起蹂喻,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎捂寿,沒想到半個(gè)月后口四,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡秦陋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年蔓彩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驳概。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赤嚼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出顺又,到底是詐尸還是另有隱情更卒,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布稚照,位于F島的核電站蹂空,受9級(jí)特大地震影響俯萌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜上枕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一绳瘟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧姿骏,春花似錦、人聲如沸斤彼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)琉苇。三九已至嘲玫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間并扇,已是汗流浹背去团。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留穷蛹,地道東北人土陪。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像肴熏,于是被迫代替她去往敵國(guó)和親鬼雀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容