前言:有時候會好奇怒医,打開一個應(yīng)用你所看到的屏幕上每一個界面時怎么顯現(xiàn)出來的咆爽。
下面就會在這個問題進行剖析之前球拦,做一些知識儲備。
1.在完成這個問題之前需要熟悉一下幾個知識點续搀。
Window
WindowManager
WindowManagerService
DecorView
PhoneWindow
RootViewimpl
ViewManager
當(dāng)看到這些的時候塞琼,會想這些是什么玩意全是什么什么window,下面就會對這些進行闡釋禁舷。對這些知識點熟悉后彪杉,就能很容易的理解android的Activity是怎么顯示和加載的了。
2.所分析的源碼是基于Android6.0
Window:就如英文所表示的意思牵咙,它在Android中表示一個窗口并且它是一個用來裝View的抽象概念派近。它可以提供一些背景,標(biāo)題等功能洁桌。幾乎所有的View都是附屬在Window上面的(PopWindow就沒有所依附的Window渴丸,它是直接浮在Window上面的一個容器)。
WindowManager:在Window中有個setWindowManager方法另凌,該方法是為window添加一個window管理器谱轨,一個Window對應(yīng)于一個WindowManager,它繼承與ViewManager接口吠谢,接口中定義了三個方法add, updateViewLayout, removeView.所以WindowManger想對Window中的View進行操作的話土童,是必須利用WindowManager的
WindowManagerService:它是由SystemServer孵化出來的一個Service.它繼承于IWindowManager.Stub,馬上反應(yīng)出就知道它需要進程間通信的工坊。它通信的主要目標(biāo)就還是WindowManager献汗。所以它的作用就是對屏幕里面的窗口進行一一管理。
PhoneWindow:它是Window的唯一實現(xiàn)類王污。它里面包含了一個最頂層的DecorView罢吃。大家使用的View都是存在于DecorView下面的。PhoneWindow里面繼承了一個Callback接口玉掸,該接口里面包含了大量事件處理方法刃麸。分析的點擊事件,就是對這些方法進行分析的司浪。
RootViewImpl:View的測量泊业,布局,繪制就是在它的performTraversals方法里開始的啊易。
以上只是對一些重要的知識點進行一個簡單闡釋吁伺,下面將會從代碼來閱讀每個關(guān)鍵點,最后再給出Android是怎么加載并顯示一個Activity的租谈。
3.源碼閱讀
Window:就如英文所表示的意思篮奄,它在Android中表示一個窗口并且它是一個用來裝View的抽象概念捆愁。它可以提供一些背景,標(biāo)題等功能窟却。幾乎所有的View都是附屬在Window上面的(PopWindow就沒有所依附的Window昼丑,它是直接浮在Window上面的一個容器)。
public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
setWindowManager(wm, appToken, appName, false);
}
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is not used for displaying the
* Window itself -- that must be done by the client.
*
* @param wm The window manager for adding new windows.
*/
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
雖然有兩個setWindowManager 方法來為Window添加WindowManager夸赫,但可以直接看后者菩帝。這里可以看到WindowManager的是一個實現(xiàn)類是WindowManagerImpl.從這里就可以進入到WindowManager去看看它的代碼了。
WindowManager是一個接口繼承于ViewManager接口茬腿。在ViewManager中有三個方法分別是
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
這三個方法就說明了前面為什么說WindowManager是外界對Window訪問的接口了呼奢,你想操作Window中View就需要通過WindowManager來進行添加,更新好布局參數(shù)切平,移除Window中的View握础。
WindowManager中LayoutParams對ViewGroup的LayoutParams進行了一個補充給對應(yīng)的Window添加Type參數(shù)表示W(wǎng)indow的類型,有三種類型悴品,分別是應(yīng)用Window禀综,子Window和系統(tǒng)Window,應(yīng)用類Window對應(yīng)一個Activity他匪,子Window不能單獨存在菇存,它需要附屬在特定的父Window之中,比如常見的Dialog就是一個子Window邦蜜,系統(tǒng)Window是需要聲明權(quán)限在能創(chuàng)建的Window依鸥,比如Toast和系統(tǒng)狀態(tài)欄這些都是系統(tǒng)Window。
對Type類型的定義截取一段:
/**
* Start of window types that represent normal application windows.
*/
public static final int FIRST_APPLICATION_WINDOW = 1;
/**
* End of types of application windows.
*/
public static final int LAST_APPLICATION_WINDOW = 99;
這兩個說明了應(yīng)用層Window數(shù)值大小是1至99
/**
* Start of types of sub-windows. The {@link #token} of these windows
* must be set to the window they are attached to. These types of
* windows are kept next to their attached window in Z-order, and their
* coordinate space is relative to their attached window.
*/
public static final int FIRST_SUB_WINDOW = 1000;
/**
* End of types of sub-windows.
*/
public static final int LAST_SUB_WINDOW = 1999;
這兩個說明了子Window數(shù)值大小是1000至1999
/**
* Start of system-specific window types. These are not normally
* created by applications.
*/
public static final int FIRST_SYSTEM_WINDOW = 2000;
/**
* End of types of system windows.
*/
public static final int LAST_SYSTEM_WINDOW = 2999;
這兩個說明了系統(tǒng)Window數(shù)值大小是 2000至2999
通過Type的設(shè)置可以改變Window所處層級悼沈,也就指定了View顯示的優(yōu)先級贱迟。
WindowManager的實現(xiàn)類是WindowManagerImpl,在它里面主要是去看對View操作的三個方法:add update remove
@Override'
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
android.util.SeempLog.record_vg_layout(383,params);
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
WindowManagerimpl的add方法實際上是調(diào)用了WindowManagerGlobal的add方法,方法如下:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
root = new ViewRootImpl(view.getContext(), display);
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 {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
從這個方法中可以提煉出幾個關(guān)鍵點絮供,
1.ViewRootImpl 在它里面開始對Window所加載的View進行繪制
2.mView mRoots mParms為數(shù)組衣吠,它們之間形成一一對應(yīng)的關(guān)系
3.display: 提供有關(guān)邏輯顯示器大小和密度的信息
對ViewRootImpl放到以后的文章中進行詳解,它對于理解自定義View有很大的幫助壤靶。
PhoneWindow:大部分View都有它本身所依附的Window缚俏,而PhoneWindow是Window的唯一實現(xiàn)類,所以可以直接說大部分View都是依附在PhoneWindow上贮乳。
PhoneWinodw中有個關(guān)鍵的DecotView變量忧换。
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
從它的注釋就可以知道,它是界面顯示View中的頂層View向拆,而我們所操作看到和操作的View都是它的子View亚茬。DecorView本身是繼承于FrameLayout。它內(nèi)部有兩個變量
// 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;
注釋說明它們兩個在DecorView的頂部和底部的指示欄浓恳,中間就由ViewGroup呈現(xiàn)的Content區(qū)域刹缝。
所以Activit碗暗,PhoneWindow,DecorView梢夯,View的關(guān)系可以用如下圖來說明: