該文章是一個系列文章,是本人在Android開發的漫漫長途上的一點感想和記錄,我會盡量按照先易后難的順序進行編寫該系列。該系列引用了《Android開發藝術探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相關知識,另外也借鑒了其他的優質博客,在此向各位大神表示感謝,膜拜!!!另外,本系列文章知識可能需要有一定Android開發基礎和項目經驗的同學才能更好理解,也就是說該系列文章面向的是Android中高級開發工程師。
第四篇了,,接着上一篇說 (怎么感覺還是沒人評論呢)
在上一篇文章中我們主要分析了android.app.ActivityThread的main函數以及setContentView。另外我們還稍微分析了一下我們自己的源碼,通過WindowManager添加View。我們知道調用setContentView把我們自己的xml布局添加到了DecorView ID為ID_ANDROID_CONTENT的布局后,最終還是會調用WindowManager.addView把DecorView加入PhoneWindow。到這里呢,我們把流程梳理一下。還是上圖:
相信讀者根據上圖再結合前面所講的內容應該對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中)
這里我們很幸運只找到了一個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的職責如下:
- 同意管理整個進程中所有窗口的信息。包括控件、布局參數以及ViewRootImpl這三個元素。(這一點從第③個注釋可以看出)
- WindowManagerGlobal將窗口的創建、銷毀、布局更新等任務交給了ViewRootImpl完成。
本篇總結
本篇文章分析了WindowManager的addView的過程,WindowManager是個接口,它的實現類是WindowManagerImpl類,而WindowManagerImpl又把相關邏輯交給了WindowManagerGlobal處理。WindowManagerGlobal是個單例類,它在進程中只存在一個實例,是它內部的addView方法最終創建了我們的核心類ViewRootImpl。ViewRootImpl實現了ViewParent接口,作為整個控件樹的根部,它是控件樹正常運作的動力所在,控件的測量、布局、繪制以及輸入事件的派發處理竇世友ViewRootImpl出發。它是WindowManagerGlobal的實際工作者。
下篇預告
在下一篇文章中我們將深入介紹ViewRootImpl的工作流程。測量、布局、以及繪制。
此致,敬禮