1. 程式人生 > >Android 開發之漫漫長途 Ⅳ——Activity 的顯示之 ViewRootImpl 初探

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

?wxfrom=5&wx_lazy=1

作者:wangle12138

地址:http://www.cnblogs.com/wangle12138/p/7825484.html

宣告:本文是 wangle12138 原創,轉發請聯絡原作者授權。

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

第四篇了,,接著上一篇說 (怎麼感覺還是沒人評論呢)

在上一篇文章中我們主要分析了android.app.ActivityThread的main函式以及setContentView。另外我們還稍微分析了一下我們自己的原始碼,通過WindowManager新增View。我們知道呼叫setContentView把我們自己的xml佈局新增到了DecorView ID為IDANDROIDCONTENT的佈局後,最終還是會呼叫WindowManager.addView把DecorView加入PhoneWindow。到這裡呢,我們把流程梳理一下。還是上圖:

cmOLumrNib1c5cJibsCZFaaX24Siaicrgw4wqcBxxJ9cFLOrchDYdibibTp2pd5v6ibiaG9pKOibOU7w0TOsQKjjNiaxH0IQ

相信讀者根據上圖再結合前面所講的內容應該對Activity的建立和顯示有了初步的認識。那麼本章我們來繼續講Activity的顯示。該注意的是本系列並不意在帶領讀者去看清每一步具體的原始碼。在前面的文章中我也很少貼出原始碼。本系列文章意在讓讀者對Android系統有個更整體的把握。我所寫的每一章知識都有可能在實際工作中用到。就如前面所講解的Android下的程序問題以及Activity的生命週期以及本章要講解的View的五大過程的基礎ViewRootImpl。而理解View的五大過程(一般文章裡都是三大過程)

以及View的事件體系是更好的去自定義View的基礎。
本章接著第Ⅲ篇的文章來講解View框架樹的動力所在ViewRootImpl

上一章的最後我們講到了使用WindowManager新增View。這一章我們來具體分析。最近我也發表了三篇文章,可反響度一般,我也是剛寫技術部落格。肯定有些不足之處。這次稍微改變些風格,在分析的時候貼一些原始碼上去。

從第一篇文章中我們就知道了使用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中)

cmOLumrNib1c5cJibsCZFaaX24Siaicrgw4wJFSXn0uqeMGkM8rOXExGn6gibbrfQX34jWkS69X8Dc9SwtY83VDxic0Q

這裡我們很幸運只找到了一個WindowManager的實現類(有的時候可能有很多個,當出現多個的時候,那只有一個個去看了)。這裡我們來看WindowManagerImpl(看到這個 類的名字我們心裡瞭然一笑,果然是java的命名規範)。

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把工作交給了WindowManagerGlobal

/**
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的職責如下:

  1. 同意管理整個程序中所有視窗的資訊。包括控制元件、佈局引數以及ViewRootImpl這三個元素。(這一點從第③個註釋可以看出)

  2. WindowManagerGlobal將視窗的建立、銷燬、佈局更新等任務交給了ViewRootImpl完成。

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

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

cmOLumrNib1c26eqUQX091Un4Z1XwRc03bpApJIicV25FD1IKsJFAuu67uQmzeEf7wWYyfQdRFLVK7jWES3WdlTA

cmOLumrNib1c26eqUQX091Un4Z1XwRc035nqERGicvSum0zEp31eud3XAkw4AvszeBtf9vrTg2xBfeicwjLaBRXBA