前言
首先看一個Android介面的佈局層次結構,最直觀的看一下:
我們能清晰看到,這個介面分成了3部分:頂部狀態列(statusbar)、底部導航欄(navigationbar)、應用介面。
題外話:
檢視佈局的層次結構,工具或途徑可以參考下面的。
Android Studio:Tools->Layout Inspector->選擇要檢視的程式;
SDK Tools:tools/hierarchyviewer.bat。 不過最新推薦用tools/monitor.bat代替單獨的hierarchyviewer.bat;hierarchyviewer.bat在工程目錄下也存在prebuilts/devtools/tools
該篇主要介紹Activity中視窗的建立過程 以及 新增到WMS的過程。
第二部分 綜述總結,先將上述兩個過程的內容做了比較簡潔的總結。
第三部分 Activity視窗新增過程,跟蹤原始碼詳細 講述了 Activity視窗的建立過程 以及 新增到WMS過程。
由於第三部分 跟蹤原始碼,這個過程比較長,涉及比較多,相對枯燥。所以把總結先放到了第二部分。這樣,如果瞭解過原始碼或這個過程的,可以只看第二部分。沒了解過的,也可以通過第二部分有個大概瞭解,再檢視第三部分,若遇到不太清楚的部分,可以再回到第二部分,對比理解。
該篇也是基於Android10的原始碼。
若有不對或不足,歡迎指點。
綜述總結
前言已經介紹了為什麼將總結放在了前面。下面具體看下。
第二部分,主要介紹了下面幾個內容:
- Window型別:視窗型別介紹
- 幾個重要類:視窗建立新增到WMS過程中常見的一些類,瞭解下他們之間的關係
- Activity建立視窗新增到WMS綜述:簡單總結了下 Activity的建立過程 和 新增到WMS過程
- Activity中的一些結構示意圖:整個過程,Activity中關聯的一些類/結構的 關係,理解這個個人覺得很有必要
- Token傳遞到WMS:Token是很重要的引數,參與整個過程。這裡將該篇涉及的過程中的 Token的傳遞過程單獨總結了下
Window型別
//WindowManager.java
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
public static final int FIRST_APPLICATION_WINDOW = 1;
public static final int LAST_APPLICATION_WINDOW = 99;
public static final int FIRST_SUB_WINDOW = 1000;
public static final int LAST_SUB_WINDOW = 1999;
public static final int FIRST_SYSTEM_WINDOW = 2000;
public static final int LAST_SYSTEM_WINDOW = 2999;
//狀態列
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
//搜尋欄
public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
//來電顯示
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;
//警告視窗,常見如:低電量警告
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;
//鎖屏
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
//toast
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;
public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6;//顯示在所有視窗之上,覆蓋
//來電優先,即使鎖屏狀態下
public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7;
//輸入法視窗
public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;
//桌布
public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;
}
應用視窗(1 ~ 99): FIRST_APPLICATION_WINDOW ~ LAST_APPLICATION_WINDOW。對應一個Activity,token需設定成Activity的token。 如:Activity。
子視窗(1000 ~ 1999): FIRST_SUB_WINDOW ~ LAST_SUB_WINDOW。必須要有一個父視窗,token需設定成父視窗的token。 如:PopupWindow,依附於Activity。
系統視窗(2000 ~ 2999): FIRST_SYSTEM_WINDOW ~ LAST_SYSTEM_WINDOW。系統級的 不需要對應Activity 也不需要有父視窗,應用程式一般沒有許可權建立,只有系統程式可以建立。如:上面列出了部分常見的系統視窗,狀態列、來電、toast、輸入法等等。
幾個重要類
下面幾個類是後續經常看到的,這裡主要看下他們直接的繼承關係,後面看到比較容易理解。
public abstract class Window {}
public class PhoneWindow extends Window implements MenuBuilder.Callback {}
public interface WindowManagerPolicy extends WindowManagerPolicyConstants {}
public class PhoneWindowManager implements WindowManagerPolicy {}
public interface ViewManager {
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
public interface WindowManager extends ViewManager {}
public final class WindowManagerImpl implements WindowManager {}
/** A window in the window manager. */
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {}
- Window是一個抽象類,Activity、Toast、Dialog等都是靠Window來呈現。
PhoneWindow是Window的具體實現類(幾乎是唯一實現類)。 - WindowManager是個介面,繼承介面 ViewManager(ViewManager定義了3個操作:增加、更新、移除)。
WindowManagerImpl是WindowManager的實現類。
不過檢視WindowManagerImpl中 關於ViewManager的3個操作可以看出,這3個實現 最終是交由WindowManagerGlobal完成的。 - WindowState維護著視窗的所有資訊。WMS通過WindowState對視窗進行管理、儲存狀態等。
Activity建立視窗新增到WMS綜述
這是跟蹤的程式碼過程,這裡彙總下 方便後續檢視理解。 紅色是比較主要的幾個節點方法。
//attach()
-performLaunchActivity()
--activity.attach()//建立了PhoneWindow(mWindow)。mWindowManager儲存的是 從mWindow處獲取的 setWindowManager()建立的WindowManagerImpl
---mWindow.setWindowManager()//PhoneWindow內部 建立了WindowManagerImpl(mWindowManager),並儲存了appToken、appName。
//onCreate()
-setContentView()
--installDecor()
---generateDecor()//建立了DecorView(mDecor)
---generateLayout()//將activity的佈局作為子檢視(ViewGroup)新增到mDecor中
//onResume()
-r.activity.makeVisible()//
--wm.addView(mDecor, ...)//wm即mWindowManager(WindowManagerImpl物件)
---WindowManagerGlobal.addView()//建立了ViewRootImpl。addView的view是mDecor,ViewRootImpl中建立了mWindow(這裡是一個IBinder,而非attach()中建立的)
----ViewRootImpl.setView()//openSession()建立了Session(IWindowSession的代理類),view也是mDecor。mDecor傳入到ViewRootImpl的mView
-----Session.addToDisplay()//通過Session進入system_server程式
------mService.addWindow()//進入WMS,執行addWindow()新增視窗
attach階段:
一個Activity 建立了一個PhoneWindow物件 ,PhoneWindow通過setWindowManager() 建立了WindowManagerImpl 。
即Activity 對應一個PhoneWindow,並得到了一個WindowManager(WindowManagerImpl,Window建立的)。
onCreate階段:
建立了DecorView ,並將 activity的佈局新增到DecorView中 。
onResume階段:
建立了ViewRootImpl,通過setView()最終由Session進入system_server程式。最終執行addWindow新增視窗到WMS。
Activity中的一些結構示意圖
下面是我學習總結中 根據理解畫的,方便自己檢視時一眼可得。
(若有什麼不對,多謝指點)
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
}
public final class WindowManagerGlobal {
private static IWindowManager sWindowManagerService;//WMS客戶端,
private static IWindowSession sWindowSession;//Session
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
}
- 一個Activity對應了一個PhoneWindow物件。即 每個Activity對應一個Window (具體實現類是PhoneWindow)。
- 一個PhoneWindow 持有一個 DecorView 例項, DecorView實際是一個FrameLayout,它是Activity中所有View的根(最頂層的View)。
- 一個PhoneWindow有一個WindowManagerImpl。WindowManagerImpl持有一個單例WindowManagerGlobal。
Token傳遞到WMS
Activity啟動時 AMS會為其建立一個ActivityRecord。可以參考:AMS之應用的第一次啟動過程。
下面先看下ActivityRecord中關於token的幾處程式碼:
final class ActivityRecord extends ConfigurationContainer {
final IApplicationToken.Stub appToken; // window manager token
// TODO: Remove after unification
AppWindowToken mAppWindowToken;
ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,...) {
appToken = new Token(this, _intent);
}
void createAppWindowToken() {
mAppWindowToken = createAppWindow(mAtmService.mWindowManager, appToken,...);
}
static class Token extends IApplicationToken.Stub {
Token(ActivityRecord activity, Intent intent) {
weakActivity = new WeakReference<>(activity);
name = intent.getComponent().flattenToShortString();
}
}
}
ActivityRecord中的成員變數 appToken ,這個很重要,後續很多地方會一直涉及到。
ActivityRecord中有個 appToken ,其是一個IBinder(內部類Token繼承了IApplicationToken介面)。Token內部持有Activity的弱引用。
在ActivityRecord中會通過createAppWindow()建立並儲存 AppWindowToken物件 到mAppWindowToken。
mAppWindowToken:這個appToken會被封裝在其中。路徑:ActivityStack.startActivityLocked()->ActivityRecord.createAppWindowToken()。AppWindowToken是WindowToken子類。WindowToken可以標誌一個視窗。
這個appToken,會在Activity.attach()中作為引數傳遞到Activity。
Activity儲存到mToken。
然後通過 Activity.attach()->mWindow.setWindowManager() 傳入到Window(PhoneWindow)中。
Window儲存到mAppToken。
WindowManagerGlobal.addView()->Window.adjustLayoutParamsForSubWindow()儲存到WindowManager.LayoutParams中的token變數中。
最後WindowManager.LayoutParams(其中token即ActivityRecord中的appToken)作為引數傳入ViewRootImpl.setView()。
ViewRootImpl中mWindowAttributes拷貝了WindowManager.LayoutParams,作為引數通過Session.addToDisplay()傳入WMS中,進行後續操作。
這是整個新增視窗(到addWindow())過程 appToken參與的過程及傳遞過程。
appToken如何參與視窗的新增,這個在 “第三部分的2.8:mService.addWindow()” 註釋中能大致看到,比較詳細。
Activity視窗新增過程
這裡主要介紹 Activity對應Window的建立 以及 Window新增到WMS的過程。
Activity視窗建立
在 AMS之應用的第一次啟動過程 中,從點選應用圖示到activity建立並執行onCreate()。 下面部分是後面部分的擷取,不清楚可以參考下那篇文章。
1.1:handleLaunchActivity()
這裡從handleLaunchActivity()開始。
//ActivityThread.java
@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
...
WindowManagerGlobal.initialize();
final Activity a = performLaunchActivity(r, customIntent);
...
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
Window window = null;
...
//attach(),注意這個r.token。參考1.2
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
if (r.isPersistable()) {
//callActivityOnCreate() 最終執行到的activity的onCreate()方法。
//參考1.4
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
}
}
...
return activity;
}
WindowManagerGlobal.initialize();
是獲取WMS的IBinder代理類,用於與WMS通訊。這裡不列出程式碼了。
接著要看的是activity.attach()
。注意作為引數傳入attach()的 r.token 是個IBinder,來自ActivityClientRecord,簡單看標識了一個Activity。
1.2:activity.attach()
//Activity.java
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, ...) {
...
//建立PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);//建立PhoneWindow
//設定軟鍵盤
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
//token儲存到mToken。
mToken = token;
...
//mToken傳入到Window,參考1.3
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
//mWindowManager即setWindowManager()中建立的WindowManagerImpl。
mWindowManager = mWindow.getWindowManager();
...
}
首先建立了Activityd對應的Window,是PhoneWindow-Window的實現類。 接著看mWindow.setWindowManager()
。
1.3:mWindow.setWindowManager()
//Window.java
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
//ActivityClientRecord.token
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
//建立了WindowManagerImpl,注意WindowManagerImpl中mParentWindow是this,非空
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
這裡建立了WindowManagerImpl物件,即WindowManager的實現類。並儲存了appToken、appName、mWindowManager。
通過setWindowManager(),即為Window(或者PhoneWindow)設定建立了WindowManager(WindowManagerImpl)。
1.4:setContentView()
mInstrumentation.callActivityOnCreate()
最終有呼叫到Activity的onCreate()。
自定義Activity,設定佈局都執行了setContentView()
,下面直接來看下這個方法。
//Activity.java
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);//
initWindowDecorActionBar();
}
public Window getWindow() {
return mWindow;//即PhoneWindow物件
}
//PhoneWindow.java
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();//
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
...
}
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//生成DecorView,參考1.5
mDecor = generateDecor(-1);
...
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
//佈局新增到DecorView,參考1.5
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
if (decorContentParent != null) {
} else {
mTitleView = findViewById(R.id.title);
}
...
}
}
這裡主要關注下兩個方法:mDecor = generateDecor(-1);
和 mContentParent = generateLayout(mDecor);
。
先看下他們的相關程式碼:
1.5:generateDecor()和generateLayout()
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
Context context;
...
return new DecorView(context, featureId, this, getAttributes());//
}
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
}
protected ViewGroup generateLayout(DecorView decor) {
...
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...
mDecor.finishChanging();
return contentParent;
}
//DecorView.java
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
...
mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
通過generateDecor()
建立了一個DecorView。DecorView實際是一個FrameLayout。
然後通過generateLayout()
,最終將activity的佈局作為子檢視(ViewGroup)新增到DecorView中。
上面可以看到,activity生成到執行onCreate(),這個過程,activity生成了關聯的PhoneWindow,然後建立了WindowManagerImpl、DecorView。
下面看下Window新增到WMS的過程,看這些建立的物件之前如何聯絡 形成的一開始介紹的結構示意圖。
Window新增到WMS過程
在 AMS之應用的第一次啟動過程 中主要講到onCreate,其實onResume也在其中在,這裡不詳細解釋了,再次列出相關程式碼:
//ActivityStackSupervisor.java:
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
...
try {
...
try {
// Create activity launch transaction.
final ClientTransaction clientTransaction = ClientTransaction.obtain(
proc.getThread(), r.appToken);
final DisplayContent dc = r.getDisplay().mDisplayContent;
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
...
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
...
}
}
...
return true;
}
通過 LaunchActivityItem 關聯 最終執行結果是建立了應用的Activity 並 執行了attach()和onCreate()。 andResume為true(傳入的引數為true,可以參考那篇這段程式碼往前看), 通過 ResumeActivityItem 關聯 最終執行到的是 ActivityThread.handleResumeActivity()。
這裡從ActivityThread.handleResumeActivity()來看。
2.1:ActivityThread.handleResumeActivity()
//ActivityThread.java
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
...
// TODO Push resumeArgs into the activity for consideration
//執行onStart()->onResume()。參考2.2
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
final Activity a = r.activity;
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;
...
}
...
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
...
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
//參考2.3
r.activity.makeVisible();
}
}
...
}
注意 View decor = r.window.getDecorView();
獲取了DecorView物件,最後通過 a.mDecor = decor;
將DecorView賦到了Activity中。
這裡關注兩個:
performResumeActivity()
r.activity.makeVisible();
2.2:performResumeActivity()
//ActivityThread.java
@VisibleForTesting
public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
String reason) {
final ActivityClientRecord r = mActivities.get(token);
...
try {
r.activity.onStateNotSaved();
r.activity.mFragments.noteStateNotSaved();
...
r.activity.performResume(r.startsNotResumed, reason);
r.state = null;
r.persistentState = null;
r.setState(ON_RESUME);
reportTopResumedActivityChanged(r, r.isTopResumedActivity, "topWhenResuming");
}
return r;
}
//Activity.java
final void performResume(boolean followedByPause, String reason) {
performRestart(true /* start */, reason);
mInstrumentation.callActivityOnResume(this);
}
final void performRestart(boolean start, String reason) {
mInstrumentation.callActivityOnRestart(this);
}
//Instrumentation.java
public void callActivityOnRestart(Activity activity) {
activity.onRestart();
}
public void callActivityOnResume(Activity activity) {
activity.mResumed = true;
activity.onResume();
...
}
performResumeActivity()中 r.activity.performResume()
回撥Activity的performResume()方法。最終執行了Activity的onResume()方法。
performResume()在執行onResume前,呼叫了performRestart()
,最終呼叫的是activity的onStart()。這裡可以看出 onStart()執行在onResume()之前。
2.3:r.activity.makeVisible()
//Activity.java
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
//WindowManagerImpl.java
public final class WindowManagerImpl implements WindowManager {
@UnsupportedAppUsage
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
//mParentWindow即建立WindowManagerImpl是 傳入的。參考2.4
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
}
這裡的getWindowManager()獲取到的是前面講到的 attach()時通過setWindowManager()建立的WindowManagerImpl物件。
前面也講過,addView()等3個操作定義實現 最終在WindowManagerGlobal中,這裡就可以看到。
2.4:WindowManagerGlobal.addView()
//WindowManagerGlobal
@UnsupportedAppUsage
private final ArrayList<View> mViews = new ArrayList<View>();
@UnsupportedAppUsage
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
//調整Window引數,這個過程將token設定其中了
//參考2.4.1
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}
...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
//將view及相關引數設定到ViewRootImpl中。ViewRootImpl會向WMS新增新視窗、申請Surface及繪製工作等。
//參考2.6
root.setView(view, wparams, panelParentView);
}
...
}
}
//ViewRootImpl.java
@UnsupportedAppUsage
final IWindowSession mWindowSession;
public ViewRootImpl(Context context, Display display) {
mContext = context;
//建立了Session(),參考2.5
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
mThread = Thread.currentThread();
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
//這裡的mWindow不是前面Activity中的PhoneWindow,它是W extends IWindow.Stub
mWindow = new W(this);
mViewVisibility = View.GONE;
//建立AttachInfo
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
...
}
static class W extends IWindow.Stub {...}
//ViewRootImpl.java
public final class ViewRootImpl implements ViewParent,
//View.java
final static class AttachInfo {
AttachInfo(IWindowSession session, IWindow window, Display display,
ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer,
Context context) {
mSession = session;
mWindow = window;
mWindowToken = window.asBinder();
mDisplay = display;
mViewRootImpl = viewRootImpl;
mHandler = handler;
mRootCallbacks = effectPlayer;
mTreeObserver = new ViewTreeObserver(context);
}
}
這裡主要看到,建立了ViewRootImpl物件。這個類實現了View與WindowManager之間必要的協議。
注意建立中的mWindow = new W(this);
,這個W繼承IWindow.Stub。
建立ViewRootImpl物件時 建立了一個mAttachInfo = View.AttachInfo()
, AttachInfo是一系列繫結資訊。mWindowSession、mWindow作為引數傳入。AttachInfo建立時注意mWindowToken = window.asBinder();
。
mWindowSession在後續2.5/2.6/2.7中講到,它是Session物件,它是IWindowSession的代理類,通過他可以與WMS通訊的binder介面。
mWindow這裡是W物件,它是IWindow.Stub,通過new建立,後續能看到會傳入WMS,它是WMS回撥應用(與應用通訊)的binder介面。
mWindowToken,也就是W的IBinder物件,也是WMS與應用通訊的介面。
建立ViewRootImpl物件後,WindowManagerGlobal將View、ViewRootImpl、LayoutParams儲存到相應的ArrayList中。前面也講到過,WindowManagerGlobal是單例的,應用程式中只有一個。最後通過root.setView()將View(這裡是DecorView)傳入到ViewRootImpl中。
2.4.1:adjustLayoutParamsForSubWindow()
前面看到mAppToken是從Activity的傳入的。
這裡mAppToken被設定到WindowManager.LayoutParams裡,後面可以看到最終傳入到WMS參與處理。
//Window.java
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
CharSequence curTitle = wp.getTitle();
//子視窗,該篇中是應用視窗,所以不走這,也瞭解下。
if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
if (wp.token == null) {
View decor = peekDecorView();
if (decor != null) {
wp.token = decor.getWindowToken();
}
}
...
//系統視窗,也不走這
} else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
...
//應用視窗,該篇走這
} else {
if (wp.token == null) {
//設定到了WindowManager.LayoutParams中
wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
}
...
}
...
}
2.4.2:AttachInfo在其中瞭解下
ViewRootImpl與各個View。通過下面的過程,AttachInfo繫結資訊被設定到各個View中了,即各個View能夠獲取到各種相關資訊。
2.6執行到ViewRootImpl.setView()後,參考過程:setView()->requestLayout()->scheduleTraversals()->mTraversalRunnable->doTraversal()->performTraversals()->host.dispatchAttachedToWindow(mAttachInfo, 0)->View.dispatchAttachedToWindow()->ViewGroup.dispatchAttachedToWindow()。
屬同個ViewGroup的 AttachInfo是一樣的。
//ViewGroup.java
@Override
@UnsupportedAppUsage
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
super.dispatchAttachedToWindow(info, visibility);
mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
child.dispatchAttachedToWindow(info,
combineVisibility(visibility, child.getVisibility()));
}
final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
for (int i = 0; i < transientCount; ++i) {
View view = mTransientViews.get(i);
view.dispatchAttachedToWindow(info,
combineVisibility(visibility, view.getVisibility()));
}
}
}
上述過程 performTraversals() 大致瞭解下:從上而下遍歷檢視樹,每個View繪製自己,ViewGroup通知子View進行繪製。測量performMeasure() 執行佈局performLayout() 繪製performDraw()。
Android繪製 重要的部分就在這裡,需要了解的可以仔細研究下這個方法(performTraversals()),這裡不作關注。
2.5:WindowManagerGlobal.getWindowSession()
// WindowManagerGlobal.java
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
IWindowManager windowManager = getWindowManagerService();
//建立Session物件
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
});
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
//WindowManagerService.java
@Override
public IWindowSession openSession(IWindowSessionCallback callback) {
return new Session(this, callback);
}
獲取Sessiond物件,如果沒有則通過 windowManager.openSession()
建立。Session是IWindowSession的代理類,然後返回給ViewRootImpl中的mWindowSession。
2.6:ViewRootImpl.setView()
//ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
mWindowAttributes.copyFrom(attrs);
...
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();//TODO
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//參考2.7,進入system_server程式
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
setFrame(mTmpFrame);
}
...
}
}
}
res = mWindowSession.addToDisplay()
:mWindowSession是上面返回的建立的Session,mWindowSession.addToDisplay()
即通過binder進入system_server程式,執行的Session.addToDisplay()。
mView即DecorView。
這裡的mWindow是2.4中講到的,是 W 繼承IWindow.Stub。這是一個IBinder物件,在應用程式建立ViewRootImpl時被建立。
這裡mWindowSession.addToDisplay()
往後可以看到被傳入到WMS。
2.7:Session.addToDisplay()
//Session.java
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
public Session(WindowManagerService service, IWindowSessionCallback callback) {
mService = service;
...
}
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
//參考2.8
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
outInsetsState);
}
}
進入WMS,新增Window。
2.8:mService.addWindow()
終於到最後WMS.addWindow(),這裡完成視窗新增。可以仔細看下下面原始碼及註釋,這個方法即使縮減了很多還是比較長,需要耐心。
//WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
int[] appOp = new int[1];
//檢查許可權,無許可權不能新增視窗
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
boolean reportNewConfig = false;
WindowState parentWindow = null;
...
final int type = attrs.type;
synchronized (mGlobalLock) {
...
//獲取視窗要新增到的DisplayContent。即顯示在哪個螢幕上
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
if (displayContent == null) {
Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
+ displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
if (!displayContent.hasAccess(session.mUid)) {
Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
+ "does not have access: " + displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
if (mWindowMap.containsKey(client.asBinder())) {
Slog.w(TAG_WM, "Window " + client + " is already added");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
//新增子視窗,父視窗必須存在。
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
parentWindow = windowForClientLocked(null, attrs.token, false);
if (parentWindow == null) {
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
//這裡可以看出WMS要求 視窗的層級 最多為兩層
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display. Aborting.");
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
}
AppWindowToken atoken = null;
final boolean hasParent = parentWindow != null;
//獲取WindowToken,對於子視窗使用父視窗的token。
//通過attrs.token從mTokenMap取出:private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();
//關於Activity視窗:WindowToken,前面講過ActivityRecord 建立時會建立AppWindowToken,這個過程中appToken和AppWindowToken被儲存到mTokenMap中
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
// If this is a child window, we want to apply the same type checking rules as the
// parent window type.
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
//以下是WindowToken和視窗之間的關係
if (token == null) {
//以下視窗型別,WindowToken不能為空
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_DREAM) {
Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
parentWindow)) {
Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
final boolean isRoundedCornerOverlay =
(attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
//token為空,除上述視窗型別,其他是允許的。此時新建WindowToken
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
//token不為空,且是應用視窗型別,token需要時AppWindowToken型別
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
//判斷token是否是AppWindowToken型別
//前面知道,Activity建立的是AppWindowToken,即獲得的atoken非空
atoken = token.asAppWindowToken();
if (atoken == null) {
Slog.w(TAG_WM, "Attempted to add window with non-application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
} else if (atoken.removed) {
Slog.w(TAG_WM, "Attempted to add window with exiting application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
} else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null) {
Slog.w(TAG_WM, "Attempted to add starting window to token with already existing"
+ " starting window");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
//其他各種視窗型別處理
} else if (rootType == TYPE_INPUT_METHOD) {
...
}...
//WindowState維護了所有視窗的資訊,它是WMS實際管理的“視窗”
//它與Z-Order密切相關(多個Window層疊佈局),其屬性mLayer 越大,視窗越靠前。
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
// continue.
Slog.w(TAG_WM, "Adding window client " + client.asBinder()
+ " that is dead, aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
...
origId = Binder.clearCallingIdentity();
//建立SurfaceSession,實現與SurfaceFlinger通訊。參考2.9 簡單說明下
win.attach();
//將WindowState物件加入到mWindowMap中
mWindowMap.put(client.asBinder(), win);
win.initAppOpsState();
...
final AppWindowToken aToken = token.asAppWindowToken();
win.mToken.addWindow(win);
win.applyAdjustForImeIfNeeded();
...
}
...
return res;
}
WMS通過WindowState對視窗進行管理、儲存狀態等。
新增視窗都需要指明其WindowToken;同時視窗需指明其DisplayContent 以確定顯示到哪個螢幕裝置。
具體請看上面註釋,比較詳細了。
看完,大致能明白視窗型別、WindowToken在視窗新增中的作用。瞭解到token的作用。
比如:若新增的時子視窗,則必須有父視窗,且視窗的層級最多為兩層。WindowToken為null,可以明顯看出那些情況不允許新增。新增的視窗時應用視窗時,WindowToken要是AppWindowToken。等等。
addWindow()暫時就說這些。
這個新增後的結果,通過res 最終反回到了 2.6:ViewRootImpl.setView() 中,根據結果 繼續處理。
2.9:win.attach()
為什麼win.attach()
是建立與SurfaceFlinger通訊的?簡單瞭解下。
跟蹤下去是建立了SurfaceSession物件,這個建立進入native,最終建立了一個與SurfaceFlinger通訊的 SurfaceComposerClient。 因此,可以與SurfaceFlinger進行通訊。
ViewRootImpl建立時 就建立的mSurface,mSurface是ViewRootImpl的成員變數,此時建立的Surface什麼都沒有,最後通過relayoutWindow()進入WMS 一步步向SurfaceFlinger發出請求。
這時幾處相關程式碼。
//WindowState.java
void attach() {
if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
mSession.windowAddedLocked(mAttrs.packageName);
}
//Session.java
void windowAddedLocked(String packageName) {
mPackageName = packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
if (mSurfaceSession == null) {
mSurfaceSession = new SurfaceSession();
...
}
mNumWindow++;
}
//ViewRootImpl.java
// These can be accessed by any thread, must be protected with a lock.
// Surface can never be reassigned or cleared (use Surface.clear()).
@UnsupportedAppUsage
public final Surface mSurface = new Surface();
private final SurfaceControl mSurfaceControl = new SurfaceControl();
這裡只說到新增視窗到WMS,關於視窗新增後的處理、介面的計算顯示更新等等,以後再總結。