Android開發之漫漫長途 Ⅳ——Activity的顯示之ViewRootImpl初探

該文章是一個系列文章,是本人在Android開發的漫漫長途上的一點感想和記錄,我會盡可能按照先易後難的順序進行編寫該系列。該系列引用了《Android開發藝術探索》以及《深刻理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相關知識,另外也借鑑了其餘的優質博客,在此向各位大神表示感謝,膜拜!!!另外,本系列文章知識可能須要有必定Android開發基礎和項目經驗的同窗才能更好理解,也就是說該系列文章面向的是Android中高級開發工程師。java


第四篇了,,接着上一篇說 (怎麼感受仍是沒人評論呢)android


在上一篇文章中咱們主要分析了android.app.ActivityThread的main函數以及setContentView。另外咱們還稍微分析了一下咱們本身的源碼,經過WindowManager添加View。咱們知道調用setContentView把咱們本身的xml佈局添加到了DecorView ID爲ID_ANDROID_CONTENT的佈局後,最終仍是會調用WindowManager.addView把DecorView加入PhoneWindow。到這裏呢,咱們把流程梳理一下。仍是上圖:ActivityThread簡化圖app


相信讀者根據上圖再結合前面所講的內容應該對Activity的建立和顯示有了初步的認識。那麼本章咱們來繼續講Activity的顯示。該注意的是本系列並不意在帶領讀者去看清每一步具體的源碼。在前面的文章中我也不多貼出源碼。本系列文章意在讓讀者對Android系統有個更總體的把握。我所寫的每一章知識都有可能在實際工做中用到。就如前面所講解的Android下的進程問題以及Activity的生命週期以及本章要講解的View的五大過程的基礎ViewRootImpl。而理解View的**五大過程(通常文章裏都是三大過程)**以及View的事件體系是更好的去自定義View的基礎。 本章接着第Ⅲ篇的文章來說解View框架樹的動力所在ViewRootImpl框架


上一章的最後咱們講到了使用WindowManager添加View。這一章咱們來具體分析。最近我也發表了三篇文章,可反響度通常,我也是剛寫技術博客。確定有些不足之處。此次稍微改變些風格,在分析的時候貼一些源碼上去。ide


從第一篇文章中咱們就知道了使用WindowManager.addView方法添加View。那麼看看該類的實現吧。函數

public interface WindowManager extends ViewManager {
	//這裏咱們只列出了一部分函數,可是並無addView、updateViewLayout、removeView這三個函數
	public Display getDefaultDisplay();
    public void removeViewImmediate(View view);
    ...
}

好吧,果真沒有這麼簡單,WindowManager是個接口,並且在其方法中沒有找到addView方法,那麼咱們只能看看ViewManager了佈局

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沒有再繼承其餘接口了。(要否則真不知道要找到何時去。) 既然WindowManager是個接口,那確定要找它的實現類了。(在這裏安利一個比較簡單的方法,在Android Studio中)這裏寫圖片描述 這裏咱們很幸運只找到了一個WindowManager的實現類(有的時候可能有不少個,當出現多個的時候,那只有一個個去看了)。這裏咱們來看WindowManagerImpl(看到這個 類的名字咱們內心瞭然一笑,果真是java的命名規範)。this

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;

    private IBinder mDefaultToken;

    public WindowManagerImpl(Context context) {
        this(context, null);
    }

    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }

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

    public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
        return new WindowManagerImpl(displayContext, mParentWindow);
    }

    /**
     * Sets the window token to assign when none is specified by the client or
     * available from the parent window.
     *
     * @param token The default token to assign.
     */
    public void setDefaultToken(IBinder token) {
        mDefaultToken = token;
    }

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

    private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
        // Only use the default token if we don't have a parent window.
        if (mDefaultToken != null && mParentWindow == null) {
            if (!(params instanceof WindowManager.LayoutParams)) {
                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
            }

            // Only use the default token if we don't already have a token.
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
            if (wparams.token == null) {
                wparams.token = mDefaultToken;
            }
        }
    }

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

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

    @Override
    public void requestAppKeyboardShortcuts(
            final KeyboardShortcutsReceiver receiver, int deviceId) {
        IResultReceiver resultReceiver = new IResultReceiver.Stub() {
            @Override
            public void send(int resultCode, Bundle resultData) throws RemoteException {
                List<KeyboardShortcutGroup> result =
                        resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY);
                receiver.onKeyboardShortcutsReceived(result);
            }
        };
        try {
            WindowManagerGlobal.getWindowManagerService()
                .requestAppKeyboardShortcuts(resultReceiver, deviceId);
        } catch (RemoteException e) {
        }
    }

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

WindowManagerImpl的源碼如上所示,咱們能夠看到WindowManagerImpl的addView方法,WindowManagerImpl把工做交給了WindowManagerGlobalcode

/**
WindowManagerGlobal 源碼比較長,這裏咱們只列出了一部分
*/
public final class WindowManagerGlobal {
    private WindowManagerGlobal() {
    }

    public static void initialize() {
        getWindowManagerService();
    }

    public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }

    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

    public static IWindowSession peekWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            return sWindowSession;
        }
    }

	//addView方法
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
	    ...  //參數檢查

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
	        //① 若是當前窗口須要被添加爲另外一個窗口的附屬窗口(子窗口),則須要父窗口視本身的狀況對當前窗口的佈局參數進行調整
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        }

        ViewRootImpl root;
        View panelParentView = null;

       
        int index = findViewLocked(view, false);
          if (index >= 0) {
              if (mDyingViews.contains(view)) {
                  mRoots.get(index).doDie();
              } else {
              //同一個View不容許被添加2次
                  throw new IllegalStateException("View " + view
                          + " has already been added to the window manager.");
              }
          }
		//② 建立一個ViewRootImpl對象並保存在root變量中
         root = new ViewRootImpl(view.getContext(), display);

         view.setLayoutParams(wparams);
		//③ 保存做爲窗口的控件、佈局參數以及新建的ViewRootImpl
         mViews.add(view);
         mRoots.add(root);
         mParams.add(wparams);

         // do this last because it fires off messages to start doing things
         try {
	         // ④ 將做爲窗口的控件設置給ViewRootImpl.這個動做將致使ViewRootImpl向WMS添加新的窗口、申請Surface以及託管控件在Surface上的重繪工做。這纔是真正意義上完成了窗口的添加工做。
             root.setView(view, wparams, panelParentView);
         } catch (RuntimeException e) {
             // BadTokenException or InvalidDisplayException, clean up.
             if (index >= 0) {
                 removeViewLocked(index, true);
             }
             throw e;
         }
        }
    }

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

    public void removeView(View view, boolean immediate) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate);
            if (curView == view) {
                return;
            }

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

   
}

咱們能夠看到WindowManagerGlobal的私有構造函數以及getInstance()這個熟悉的靜態方法名字。能夠看出WindowManagerGlobal是個典型的單例。 WindowManagerGlobal 的addView方法並不複雜,其主要的關鍵點咱們已經標註並寫了註釋。也就是說WindowManagerGlobal的職責以下:xml

  1. 贊成管理整個進程中全部窗口的信息。包括控件、佈局參數以及ViewRootImpl這三個元素。(這一點從第③個註釋能夠看出)
  2. WindowManagerGlobal將窗口的建立、銷燬、佈局更新等任務交給了ViewRootImpl完成。

本篇總結 本篇文章分析了WindowManager的addView的過程,WindowManager是個接口,它的實現類是WindowManagerImpl類,而WindowManagerImpl又把相關邏輯交給了WindowManagerGlobal處理。WindowManagerGlobal是個單例類,它在進程中只存在一個實例,是它內部的addView方法最終建立了咱們的核心類ViewRootImpl。ViewRootImpl實現了ViewParent接口,做爲整個控件樹的根部,它是控件樹正常運做的動力所在,控件的測量、佈局、繪製以及輸入事件的派發處理竇世友ViewRootImpl出發。它是WindowManagerGlobal的實際工做者。


下篇預告 在下一篇文章中咱們將深刻介紹ViewRootImpl的工做流程。測量、佈局、以及繪製。


此致,敬禮

相關文章
相關標籤/搜索