在Android一個(gè)View是怎么展示在屏幕上斥季?過(guò)程是什么樣的呢累驮?現(xiàn)提出問(wèn)題再帶著問(wèn)題去查看源碼。
Android所有展示的頁(yè)面所有的View都被裝載在Window中躁锡,Window是一個(gè)抽象類稚铣,它的唯一實(shí)現(xiàn)類是PhoneWindow墅垮,而Activity中有一個(gè)window的對(duì)象這點(diǎn)從Activity的源碼就可以看到,如下:
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) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window); //注釋:1
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mReferrer = referrer;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
if (voiceInteractor != null) {
if (lastNonConfigurationInstances != null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
Looper.myLooper());
}
}
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//注釋:2
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
如果對(duì)attach()方法不是很了解可以查詢下Android Activity的啟動(dòng)流程 。
注釋1:new 了一個(gè)PhoneWindow的對(duì)象峡钓,這樣activity就擁有了window對(duì)象能岩,
但是這樣遠(yuǎn)遠(yuǎn)不夠?qū)iew展示出來(lái)萧福,因?yàn)檫@只是有了一個(gè)窗口真正的繪制工作并沒有開始。
再看注釋2:Activity中window調(diào)用了setWindowManager方法膏燕,WindowManager又是干什么用的呢?這里引用皇叔的博客中的一段話來(lái)下個(gè)定義:
WindowManager對(duì)Window進(jìn)行管理篷就,說(shuō)到管理那就離不開對(duì)Window的添加近忙、更新和刪除的操作及舍,在這里我們把它們統(tǒng)稱為Window的操作。對(duì)于Window的操作,最終都是交由WMS來(lái)進(jìn)行處理更振。
如果大家熟悉AMS的話對(duì)這種方式應(yīng)該不會(huì)陌生饭尝,它們其實(shí)相似。接著查看setWindowManager实撒,他是Window的一個(gè)方法涉瘾,
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is <em>not</em> 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);
}//注釋3
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
看注釋3的位置立叛,先判斷傳入的WM是否為空秘蛇,如果為空則再次執(zhí)行(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)
下面看getSystemService方法,這個(gè)方法是通過(guò)Binder機(jī)制來(lái)獲取WMS妖泄,再看getSystemService返回的對(duì)象是什么艘策?具體的實(shí)現(xiàn)是在ContextImpl中,
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
SystemServiceRegistry又是個(gè)什么東東呢审残?看一下:
static{
...
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
...
}
SystemServiceRegistry 的靜態(tài)代碼塊中會(huì)調(diào)用多個(gè)registerService方法搅轿,這里只列舉了和本文有關(guān)的一個(gè)。registerService方法會(huì)將傳入的服務(wù)的名稱存入到SYSTEM_SERVICE_NAMES中既穆。從上面代碼可以看出雀鹃,傳入的Context.WINDOW_SERVICE對(duì)應(yīng)的就是WindowManagerImpl實(shí)例黎茎,因此得出結(jié)論,Context的getSystemService方法得到的是WindowManagerImpl實(shí)例踢代。
在返回到注釋3的位置的下一行代碼調(diào)用了WindowManagerImpl對(duì)象的createLocalWindowManager方法嗅骄,這個(gè)方法又是干嘛的呢?
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
這個(gè)方法很簡(jiǎn)單就是就是又一次創(chuàng)建了WindowManagerImpl對(duì)象慕爬,和通過(guò)之前那次不同的是這次傳入了PhoneWindow對(duì)象医窿,這時(shí)WindowManager開始真正和WIndow關(guān)聯(lián)起來(lái)炊林。
哪View是被怎么Add到Window上的呢?這里以最典型的應(yīng)用程序窗口Activity為例隔显,Activity在啟動(dòng)過(guò)程中饵逐,如果Activity所在的進(jìn)程不存在則會(huì)創(chuàng)建新的進(jìn)程倍权,創(chuàng)建新的進(jìn)程之后就會(huì)運(yùn)行代表主線程的實(shí)例ActivityThread捞烟,不了解的請(qǐng)查看Android應(yīng)用程序進(jìn)程啟動(dòng)過(guò)程(前篇)這篇文章题画。ActivityThread管理著當(dāng)前應(yīng)用程序進(jìn)程的線程德频,這在Activity的啟動(dòng)過(guò)程中運(yùn)用的很明顯,不了解的請(qǐng)查看Android深入四大組件(一)應(yīng)用程序啟動(dòng)過(guò)程(后篇)這篇文章竞思。當(dāng)界面要與用戶進(jìn)行交互時(shí)盖喷,會(huì)調(diào)用ActivityThread的handleResumeActivity方法难咕,如下所示。
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);//注釋4
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
...
注意注釋4:調(diào)用了在注釋3位置返回的WindowManagerImpl對(duì)象的addview方法,這個(gè)方法是ViewManager中的方法沾歪,ViewManager是一個(gè)接口雾消,WindowManager是個(gè)接口同時(shí)又實(shí)現(xiàn)了ViewManager接口立润,而WindowManagerImpl又實(shí)現(xiàn)了WindowManager媳板,這是只捋清楚這個(gè)方法的層級(jí),清楚后放到一邊繼續(xù)查看WindowManagerImpl中的addview方法
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);//注釋5
}
看注釋5:又調(diào)用了mGlobal的addview方法破讨,如下
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
//熟悉的LayoutParams出來(lái)了
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
//根據(jù)父window調(diào)整當(dāng)前window的尺寸
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.
//查看系統(tǒng)屬性的更改
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);
}
}
}
//添加View的所有準(zhǔn)備工作就緒開始添加
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);//注釋6
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
注釋6:調(diào)用了ViewRootImpl中的setView方法:
...
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
...
主要就是調(diào)用了mWindowSession的addToDisplay方法隙笆。mWindowSession是IWindowSession類型的,它是一個(gè)Binder對(duì)象瘸爽,用于進(jìn)行進(jìn)程間通信铅忿,IWindowSession是Client端的代理辆沦,它的Server端的實(shí)現(xiàn)為Session,此前包含ViewRootImpl在內(nèi)的代碼邏輯都是運(yùn)行在本地進(jìn)程的肢扯,而Session的addToDisplay方法則運(yùn)行在WMS所在的進(jìn)程蔚晨。
@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);
}
addToDisplay方法中會(huì)調(diào)用了WMS的addWindow方法,并將自身也就是Session银择,作為參數(shù)傳了進(jìn)去累舷,每個(gè)應(yīng)用程序進(jìn)程都會(huì)對(duì)應(yīng)一個(gè)Session被盈,WMS會(huì)用ArrayList來(lái)保存這些Session。這樣剩下的工作就交給WMS來(lái)處理袜瞬,在WMS中會(huì)為這個(gè)添加的窗口分配Surface身堡,并確定窗口顯示次序,可見負(fù)責(zé)顯示界面的是畫布Surface汞扎,而不是窗口本身擅这。WMS會(huì)將它所管理的Surface交由SurfaceFlinger處理,SurfaceFlinger會(huì)將這些Surface混合并繪制到屏幕上一忱。
結(jié)論:Activity持有Window的對(duì)象,View在Window上的增刪等操作又是通過(guò)WindowManager來(lái)管理的票渠,而WindowManager又是通過(guò)Binder機(jī)制獲取到的WMS的映射芬迄,WMS把View真正顯示到屏幕上禀梳。
部分源碼線上鏈接https://www.androidos.net.cn/android/8.0.0_r4/xref/frameworks/base/core/java/android/app/ContextImpl.java
https://www.androidos.net.cn/android/8.0.0_r4/xref/frameworks/base/core/java/android/app/SystemServiceRegistry.java