您好,登錄后才能下訂單哦!
這篇文章主要介紹“Android10原理機制系列之Activity窗口添加到WMS過程是什么”,在日常操作中,相信很多人在Android10原理機制系列之Activity窗口添加到WMS過程是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Android10原理機制系列之Activity窗口添加到WMS過程是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
前言
首先看一個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窗口添加過程 ,跟蹤源碼詳細 講述了 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();
到此,關于“Android10原理機制系列之Activity窗口添加到WMS過程是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。