Android14 WMS-窗口添加流程(一)-Client端
窗口布局在onCreate方法中通过setContentView(R.layout.xxx)加载,但窗口的显示并不是在wm_on_create_called中, 而是在wm_on_resume_called后,也就是说应用onResume时此窗口是不可见的,真正可见是当此window窗口的mDrawState变化状态从NO_SURFACE -> DRAW_PENDING -> COMMIT_DRAW_PENDING -> HAS_DRAWN-> READY_TO_SHOW,然后才会将图层置为可见状态,这个在后面会讲解到。设置为可见的log如下:
05-25 10:56:31.956 1915 1973 V WindowManager: performShow on Window{f4647f5 u0 com.android.settings/com.android.settings.homepage.SettingsHomepageActivity}: mDrawState=READY_TO_SHOW readyForDisplay=true starting=false during animation: policyVis=true parentHidden=false tok.visibleRequested=true tok.visible=true animating=false tok animating=false Callers=com.android.server.wm.WindowState.performShowLocked:4372 com.android.server.wm.WindowStateAnimator.commitFinishDrawingLocked:256 com.android.server.wm.DisplayContent.lambda$new$8:1082 com.android.server.wm.DisplayContent.$r8$lambda$NJwM1ysKPNyOazqyI2QXlp2I4yA:0
05-25 10:56:31.962 1915 1973 V WindowManager: Showing Window{f4647f5 u0 com.android.settings/com.android.settings.homepage.SettingsHomepageActivity}: mDrawState=READY_TO_SHOW readyForDisplay=true starting=false during animation: policyVis=true parentHidden=false tok.visibleRequested=true tok.visible=true animating=true tok animating=false Callers=com.android.server.wm.WindowState.performShowLocked:4387 com.android.server.wm.WindowStateAnimator.commitFinishDrawingLocked:256 com.android.server.wm.DisplayContent.lambda$new$8:1082 com.android.server.wm.DisplayContent.$r8$lambda$NJwM1ysKPNyOazqyI2QXlp2I4yA:0
1. ActivityThread#handleResumeActivity
Activity启动时一开始都是置为不可见INVISIBLE的,然后才置为可见VISIBLE。
ActivityThread.java - OpenGrok cross reference for /frameworks/base/core/java/android/app/ActivityThread.java
@Overridepublic void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,boolean isForward, boolean shouldSendCompatFakeFocus, String reason) {// If we are getting ready to gc after going to the background, well// we are back active so skip it.unscheduleGcIdler();mSomeActivitiesChanged = true;// TODO Push resumeArgs into the activity for consideration// skip below steps for double-resume and r.mFinish = true case.
//这个是activity resume很重要的一步,会调用到activity本身的onResume方法,后面会做详细解释if (!performResumeActivity(r, finalStateRequest, reason)) {return;}
//如果mActivitiesToBeDestroyed集合包含此acitivity,则不往下执行resume操作。
//mActivitiesToBeDestroyed是即将 要销毁的activity集合if (mActivitiesToBeDestroyed.containsKey(r.token)) {// Although the activity is resumed, it is going to be destroyed. So the following// UI operations are unnecessary and also prevents exception because its token may// be gone that window manager cannot recognize it. All necessary cleanup actions// performed below will be done while handling destruction.return;}final Activity a = r.activity;if (localLOGV) {Slog.v(TAG, "Resume " + r + " started activity: " + a.mStartedActivity+ ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished);}
...
//如果这个window没有被add进window manager,并且这个activity没有执行finish操作,或者启动其他activity,则首先要将此window添加进window manager中。boolean willBeVisible = !a.mStartedActivity;if (!willBeVisible) {willBeVisible = ActivityClient.getInstance().willActivityBeVisible(a.getActivityToken());}
//这里代表这是首次启动这个activity
//如果window为空,并且不要执行finish,并且申请可见,则进入如下逻辑if (r.window == null && !a.mFinished && willBeVisible) {r.window = r.activity.getWindow();View decor = r.window.getDecorView();
//先将根布局设置为不可见decor.setVisibility(View.INVISIBLE);
//获取当前window manager对象ViewManager wm = a.getWindowManager();
//获取当前window属性WindowManager.LayoutParams l = r.window.getAttributes();a.mDecor = decor;
//将window type类型设置为普通APP类型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.
//获取ViewRootImplViewRootImpl impl = decor.getViewRootImpl();if (impl != null) {impl.notifyChildRebuilt();}}if (a.mVisibleFromClient) {if (!a.mWindowAdded) {a.mWindowAdded = true;
//如果此window没有被add进window manager过,则将此根view(decor)add进window manager中wm.addView(decor, l);} else {// The activity will get a callback for this {@link LayoutParams} change// earlier. However, at that time the decor will not be set (this is set// in this method), so no action will be taken. This call ensures the// callback occurs with the decor set.a.onWindowAttributesChanged(l);}}// 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) {//如果这个activity已经被add进window manager过了,并且在resume期间又起了其他activity,那么我们就不会将这个window设置为可见if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
//设置立即hide的flag为truer.hideForNow = true;}// Get rid of anything left hanging around.cleanUpPendingRemoveWindows(r, false /* force */);// The window is now visible if it has been added, we are not// simply finishing, and we are not starting another activity.
//这里代表这个activity已经被添加进window manager了,并非首次启动if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward);
//获取ViewRootImplViewRootImpl impl = r.window.getDecorView().getViewRootImpl();
//获取窗口属性WindowManager.LayoutParams l = impl != null? impl.mWindowAttributes : r.window.getAttributes();if ((l.softInputMode& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)!= forwardBit) {l.softInputMode = (l.softInputMode& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))| forwardBit;if (r.activity.mVisibleFromClient) {ViewManager wm = a.getWindowManager();View decor = r.window.getDecorView();wm.updateViewLayout(decor, l);}}r.activity.mVisibleFromServer = true;mNumVisibleActivities++;if (r.activity.mVisibleFromClient) {r.activity.makeVisible();}
...}
这块大概的流程图如上所示,在执行handleResumeActivity的时候,会先去执行activity的onResume方法,然后再将当前的window add进window manager。
这块有个地方拓展下,就是窗口类型,普通apk启动的窗口类型都是TYPE_BASE_APPLICATION
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
Android系统三大窗口类型
Android系统中共有三大窗口类型,分别是Application类型, 子窗口类型,以及系统窗口类型:
窗口类型 | Vlaue | 含义 |
---|---|---|
以下窗口类型为普通APPLICAT窗口类型,范围1-99 | ||
FIRST_APPLICATION_WINDOW | 1 | App的初始值 |
TYPE_BASE_APPLICATION | 1 | 所有App的基础值 |
TYPE_APPLICATION | 2 | 普通应用程序窗口 |
TYPE_APPLICATION_STARTING | 3 | starting窗口 |
TYPE_DRAWN_APPLICATION | 4 | 等待绘制完成的窗口 |
LAST_APPLICATION_WINDOW | 99 | App的最大值 |
以下窗口类型为子窗口类型,范围1000-1999 | ||
FIRST_SUB_WINDOW | 1000 | 子窗口的初始值 |
TYPE_APPLICATION_PANEL | FIRST_SUB_WINDOW | 应用程序窗口顶部的面板。这些窗口出现在它们的附属窗口的顶部。 |
TYPE_APPLICATION_MEDIA | FIRST_SUB_WINDOW +1 | media子窗口 |
TYPE_APPLICATION_SUB_PANEL | FIRST_SUB_WINDOW +2 | 子窗口之上的子窗口 |
TYPE_APPLICATION_ATTACHED _DIALOG | FIRST_SUB_WINDOW +3 | Dialog子窗口 |
TYPE_APPLICATION_MEDIA _OVERLAY | FIRST_SUB_WINDOW +4 | media子窗口之上的子窗口 |
TYPE_APPLICATION_ABOVE_ SUB_PANEL | FIRST_SUB_WINDOW +5 | 在SUB_PANEL之上的子窗口 |
LAST_SUB_WINDOW | 1999 | 子窗口的最大值 |
以下为系统窗口类型,范围2000-2999 | ||
FIRST_SYSTEM_WINDOW | 2000 | 系统窗口的初始值 |
TYPE_STATUS_BAR | FIRST_SYSTEM_WINDOW | 系统状态栏窗口 |
TYPE_SEARCH_BAR | FIRST_SYSTEM_WINDOW+1 | 搜索条窗口 |
TYPE_PHONE | FIRST_SYSTEM_WINDOW+2 | 通话窗口,位于状态栏之上 |
TYPE_SYSTEM_ALERT | FIRST_SYSTEM_WINDOW+3 | Alert窗口,如电量不足提示,显示在APP之上窗口 |
TYPE_KEYGUARD | FIRST_SYSTEM_WINDOW+4 | 锁屏窗口 |
TYPE_TOAST | FIRST_SYSTEM_WINDOW+5 | 短暂的提示框窗口 |
TYPE_SYSTEM_OVERLAY | FIRST_SYSTEM_WINDOW+6 | 系统覆盖窗口,不能接受input焦点,否则会与屏保发生冲突 |
TYPE_PRIORITY_PHONE | FIRST_SYSTEM_WINDOW+7 | 电话优先窗口,如屏保状态下显示来电窗口 |
TYPE_SYSTEM_DIALOG | FIRST_SYSTEM_WINDOW+8 | 系统Dialog窗口 |
TYPE_KEYGUARD_DIALOG | FIRST_SYSTEM_WINDOW+9 | keyguard Dialog窗口 |
TYPE_SYSTEM_ERROR | FIRST_SYSTEM_WINDOW+10 | 系统报错窗口 |
TYPE_INPUT_METHOD | FIRST_SYSTEM_WINDOW+11 | 输入法窗口 |
TYPE_INPUT_METHOD_DIALOG | FIRST_SYSTEM_WINDOW+12 | 输入法Dialog窗口 |
TYPE_WALLPAPER | FIRST_SYSTEM_WINDOW+13 | 壁纸窗口 |
TYPE_STATUS_BAR_PANEL | FIRST_SYSTEM_WINDOW+14 | 从状态栏滑出的面板在多用户系统中显示在所有用户的窗口上。 |
TYPE_SECURE_SYSTEM_OVERLAY | FIRST_SYSTEM_WINDOW+15 | @hide |
TYPE_DRAG | FIRST_SYSTEM_WINDOW+16 | @hide拖拽窗口 |
TYPE_STATUS_BAR_SUB_PANEL | FIRST_SYSTEM_WINDOW+17 | @hide,status bar之上的子窗口 |
TYPE_POINTER | FIRST_SYSTEM_WINDOW+18 | @hide |
TYPE_NAVIGATION_BAR | FIRST_SYSTEM_WINDOW+19 | @hide导航栏窗口 |
TYPE_VOLUME_OVERLAY | FIRST_SYSTEM_WINDOW+20 | @hide系统音量条 |
TYPE_BOOT_PROGRESS | FIRST_SYSTEM_WINDOW+21 | @hide启动时的进度条窗口 |
TYPE_INPUT_CONSUMER | FIRST_SYSTEM_WINDOW+22 | @hide消耗input事件的窗口 |
TYPE_NAVIGATION_BAR_PANEL | FIRST_SYSTEM_WINDOW+24 | @hide |
TYPE_DISPLAY_OVERLAY | FIRST_SYSTEM_WINDOW+26 | @hide用于模拟第二个Display显示屏 |
TYPE_MAGNIFICATION_OVERLAY | FIRST_SYSTEM_WINDOW+27 | @hide |
TYPE_PRIVATE_PRESENTATION | FIRST_SYSTEM_WINDOW+30 | |
TYPE_VOICE_INTERACTION | FIRST_SYSTEM_WINDOW+31 | @hide |
TYPE_ACCESSIBILITY_OVERLAY | FIRST_SYSTEM_WINDOW+32 | |
TYPE_VOICE_INTERACTION_STARTING | FIRST_SYSTEM_WINDOW+33 | @hide |
TYPE_DOCK_DIVIDER | FIRST_SYSTEM_WINDOW+34 | @hide |
TYPE_QS_DIALOG | FIRST_SYSTEM_WINDOW+35 | @hide |
TYPE_SCREENSHOT | FIRST_SYSTEM_WINDOW+36 | @hide在锁屏之上,该层保留截图动画,区域选择和UI。 |
TYPE_PRESENTATION | FIRST_SYSTEM_WINDOW+37 | @hide用于在外部显示器上显示的窗口(多个屏幕情况下) |
TYPE_APPLICATION_OVERLAY | FIRST_SYSTEM_WINDOW+38 | |
TYPE_ACCESSIBILITY_MAGNIFICATION _OVERLAY | FIRST_SYSTEM_WINDOW+39 | @hide |
TYPE_NOTIFICATION_SHADE | FIRST_SYSTEM_WINDOW+40 | @hide |
TYPE_STATUS_BAR_ADDITIONAL | FIRST_SYSTEM_WINDOW+41 | @hide |
LAST_SYSTEM_WINDOW | 2999 | 系统窗口的最大值 |
1.1. ActivityThread#performResumeActivity
@VisibleForTestingpublic boolean performResumeActivity(ActivityClientRecord r, boolean finalStateRequest,String reason) {if (localLOGV) {Slog.v(TAG, "Performing resume of " + r + " finished=" + r.activity.mFinished);}
//如果activity将要finish,则直接return掉,不执行resume操作了if (r.activity.mFinished) {return false;}
//如果activity已经on_resume了,则不继续往下执行,returnif (r.getLifecycleState() == ON_RESUME) {if (!finalStateRequest) {final RuntimeException e = new IllegalStateException("Trying to resume activity which is already resumed");
...}return false;}
...
//执行activity的onResume操作r.activity.performResume(r.startsNotResumed, reason);r.state = null;r.persistentState = null;
//设置activity状态为on_resumer.setState(ON_RESUME);
//将会打印出event log---wm_on_top_resumed_gained_called相关信息reportTopResumedActivityChanged(r, r.isTopResumedActivity, "topWhenResuming");} catch (Exception e) {if (!mInstrumentation.onException(r.activity, e)) {throw new RuntimeException("Unable to resume activity "+ r.intent.getComponent().toShortString() + ": " + e.toString(), e);}}return true;}
本小节介绍了activity启动过程中其他的知识,下文再继续接着讲怎么添加窗口流程的,下面接着1.1中的addView方法。
2. WindowManager#addView
wm的获取方法如下,可以看到最后是获得到了一个WindowManager对象,
private WindowManager mWindowManager;public WindowManager getWindowManager() {return mWindowManager;}
那就来到了WindowManager中的addView方法
查看WindowManager中没有addView方法,但是ViewManager中有,那我们再看下谁实现了WindowManager呢
public interface WindowManager extends 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); }
可找到是WindowManagerImpl中实现此addView方法,因为WindowManagerImpl继承了WindowManager:
/** @see WindowManager* @see WindowManagerGlobal* @hide*/
public final class WindowManagerImpl implements WindowManager {
...@Overridepublic void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {applyTokens(params);mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,mContext.getUserId());}
此方法又调用了 WindowManagerGlobal中的addView方法,并将view,窗口属性,要显示的屏幕,父window,以及user id传入
WindowManagerGlobal.java - OpenGrok cross reference for /frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow, int userId) {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);}}}IWindowSession windowlessSession = null;// If there is a parent set, but we can't find it, it may be coming// from a SurfaceControlViewHost hierarchy.
//如果token不为空并且不是子窗口if (wparams.token != null && panelParentView == null) {for (int i = 0; i < mWindowlessRoots.size(); i++) {ViewRootImpl maybeParent = mWindowlessRoots.get(i);if (maybeParent.getWindowToken() == wparams.token) {
//获取WindowSessionwindowlessSession = maybeParent.getWindowSession();break;}}}if (windowlessSession == null) {
//如果windowlessSession为空root = new ViewRootImpl(view.getContext(), display);} else {
//如果windowlessSession不为空,则在此处传入windowlessSessionroot = new ViewRootImpl(view.getContext(), display,windowlessSession, new WindowlessWindowLayout());}
//将窗口布局属性设置进viewview.setLayoutParams(wparams);
//将此view添加进mViewsmViews.add(view);
//将此root添加进MRootsmRoots.add(root);
//将此窗口布局属性添加进mParamsmParams.add(wparams);// do this last because it fires off messages to start doing thingstry {
//将view,窗口属性和子窗口,user id 设置进ViewRootImplroot.setView(view, wparams, panelParentView, userId);} catch (RuntimeException e) {final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);// BadTokenException or InvalidDisplayException, clean up.if (viewIndex >= 0) {removeViewLocked(viewIndex, true);}throw e;}}}
3. ViewRootImpl#setView
然后就从WindowManagerGlobal走到了ViewRootImpl中
http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/view/ViewRootImpl.java#1214
final IWindowSession mWindowSession;public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {setView(view, attrs, panelParentView, UserHandle.myUserId());}public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {synchronized (this) {
//这个mView和上一个方法中的可不一样,当窗口detach后,这个mView会被置为空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.
//首先需要layoutrequestLayout();
...
//这个是很重要的一步res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,mInsetsController.getRequestedVisibleTypes(), inputChannel, mTempInsets, mTempControls, attachedFrame, compatScale);
...mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars;mInsetsController.onStateChanged(mTempInsets);mInsetsController.onControlsChanged(mTempControls.get());final InsetsState state = mInsetsController.getState();final Rect displayCutoutSafe = mTempRect;state.getDisplayCutoutSafe(displayCutoutSafe);final WindowConfiguration winConfig = getCompatWindowConfiguration();mWindowLayout.computeFrames(mWindowAttributes, state,displayCutoutSafe, winConfig.getBounds(), winConfig.getWindowingMode(),UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH,mInsetsController.getRequestedVisibleTypes(), 1f /* compactScale */, mTmpFrames);setFrame(mTmpFrames.frame, true /* withinRelayout */);
...}
接着往下走,就会讲到WindowSession.addToDisplayAsUser方法
4. WindowSession.addToDisplayAsUser
这里是涉及到AIDL通信,因为mWindowSession是final IWindowSession mWindowSession;
用到的AIDL文件如下:
frameworks/base/core/java/android/view/IWindowSession.aidl
http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/view/IWindowSession.aidl
Session.java - OpenGrok cross reference for /frameworks/base/services/core/java/com/android/server/wm/Session.java
@Overridepublic int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,int viewVisibility, int displayId, int userId, @InsetsType int requestedVisibleTypes,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,float[] outSizeCompatScale) {
//调用WMS的addWindow流程return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,requestedVisibleTypes, outInputChannel, outInsetsState, outActiveControls,outAttachedFrame, outSizeCompatScale);}
下面是一个关于以上流程的setting主页面启动流程
05-25 12:58:21.791 1909 2175 I wm_task_created: 441
05-25 12:58:21.795 1909 2175 I wm_task_moved: [441,441,0,1,2]
05-25 12:58:21.795 1909 2175 I wm_task_to_front: [0,441,0]
05-25 12:58:21.795 1909 2175 I wm_create_task: [0,441,441,0]
05-25 12:58:21.795 1909 2175 I wm_create_activity: [0,232997572,441,com.android.settings/.homepage.SettingsHomepageActivity,android.settings.SETTINGS,NULL,NULL,335544320]
05-25 12:58:21.795 1909 2175 I wm_task_moved: [441,441,0,1,2]
05-25 12:58:22.265 1909 3494 I wm_restart_activity: [0,232997572,441,com.android.settings/.homepage.SettingsHomepageActivity]
05-25 12:58:22.267 1909 3494 I wm_set_resumed_activity: [0,com.android.settings/.homepage.SettingsHomepageActivity,minimalResumeActivityLocked - onActivityStateChanged]
05-25 12:58:22.363 2518 2518 I wm_on_create_called: [232997572,com.android.settings.homepage.SettingsHomepageActivity,performCreate,66]
05-25 12:58:22.457 2518 2518 I wm_on_start_called: [232997572,com.android.settings.homepage.SettingsHomepageActivity,handleStartActivity,92]
05-25 12:58:22.460 2518 2518 V ActivityThread: Performing resume of ActivityRecord{a4a98fc token=android.os.BinderProxy@c7155ef {com.android.settings/com.android.settings.homepage.SettingsHomepageActivity}} finished=false
05-25 12:58:22.460 2518 2518 I wm_on_resume_called: [232997572,com.android.settings.homepage.SettingsHomepageActivity,RESUME_ACTIVITY,0]
05-25 12:58:22.525 2518 2518 V ActivityThread: Resume ActivityRecord{a4a98fc token=android.os.BinderProxy@c7155ef {com.android.settings/com.android.settings.homepage.SettingsHomepageActivity}} started activity: false, hideForNow: false, finished: false
05-25 12:58:22.545 1909 2175 V WindowManager: Attaching Window{a90e6ab u0 com.android.settings/com.android.settings.homepage.SettingsHomepageActivity} token=ActivityRecord{de342c4 u0 com.android.settings/.homepage.SettingsHomepageActivity t441}
05-25 12:58:22.552 2518 2518 V ActivityThread: Resuming ActivityRecord{a4a98fc token=android.os.BinderProxy@c7155ef {com.android.settings/com.android.settings.homepage.SettingsHomepageActivity}} with isForward=true
05-25 12:58:22.552 2518 2518 V ActivityThread: Scheduling idle handler for ActivityRecord{a4a98fc token=android.os.BinderProxy@c7155ef {com.android.settings/com.android.settings.homepage.SettingsHomepageActivity}}
05-25 12:58:22.553 2518 2518 I wm_on_top_resumed_gained_called: [232997572,com.android.settings.homepage.SettingsHomepageActivity,topStateChangedWhenResumed]
05-25 12:58:22.769 1909 1971 I wm_activity_launch_time: [0,232997572,com.android.settings/.homepage.SettingsHomepageActivity,970]
6. WindowManagerService#addWindow
这里就到了Server端,以上都是Client端。
WMS的流程较复杂,后面再出关于Server端的文章。
相关文章:

Android14 WMS-窗口添加流程(一)-Client端
窗口布局在onCreate方法中通过setContentView(R.layout.xxx)加载,但窗口的显示并不是在wm_on_create_called中, 而是在wm_on_resume_called后,也就是说应用onResume时此窗口是不可见的,真正可见是当此window窗口的mDrawState变化状态从NO_SUR…...

【人工智能】第二部分:ChatGPT的架构设计和训练过程
人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…...

Informer
I n f o r m e r Informer Informer 摘要: 长序列时间序列的预测 i n f o r m e r informer informer优点: P r o b s p a r e Probspare Probspare自关注机制,在时间复杂度和内存使用方面达到 O ( N l o g N ) O(NlogN) O(NlogN),在序列依…...

12岁学什么编程机构好:深入剖析与全面指导
12岁学什么编程机构好:深入剖析与全面指导 在数字化时代,编程已成为一项必备技能。对于12岁的孩子来说,选择一个合适的编程机构至关重要。然而,市场上的编程机构众多,如何选择成为了一个难题。本文将从四个方面、五个…...

Day60 柱状图中最大的矩形
84 柱状图中最大的矩形 题目链接:84. 柱状图中最大的矩形 - 力扣(LeetCode) 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的…...

typescript --object对象类型
ts中的object const obj new Object()Object 这里的Object是Object类型,而不是JavaScript内置的Object构造函数。 这里的Object是一种类型,而Object()构造函数表示一个值。 Object()构造函数的ts代码 interface ObjectConstructor{readonly prototyp…...

如何使用python将多个EXCEL表进行合并
在Python中,你可以使用pandas库来轻松地将多个Excel表格合并。以下是一个基本的步骤指南和示例代码,说明如何合并多个Excel文件到一个单独的DataFrame中: 步骤 安装pandas和openpyxl(如果你正在处理.xlsx文件)。导入…...

【前端每日基础】day35——HTML5离线存储
HTML5引入了一些新的特性和API来增强Web应用的功能,其中之一就是离线存储。离线存储允许Web应用在没有网络连接的情况下仍能正常运行。以下是HTML5离线存储的主要技术和详细介绍: Web Storage (LocalStorage 和 SessionStorage) LocalStorage 概述&…...

动态规划算法:背包问题
背包问题概述 背包问题 (Knapsack problem) 是⼀种组合优化的 NP完全问题 。 问题可以描述为:给定⼀组物品,每种物品都有⾃⼰的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最⾼。 根据物品的个…...

新版idea配置git步骤及项目导入
目录 git安装 下载 打开git Bash 配置全局用户名及邮箱 查看已经配置的用户名和邮箱 在IDEA中设置Git 问题解决 项目导入 前言-与正文无关 生活远不止眼前的苦劳与奔波,它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中࿰…...

赶紧收藏!2024 年最常见 20道 Kafka面试题(一)
一、Kafka都有哪些特点? Kafka是一个分布式流处理平台,它被设计用于高吞吐量的数据管道和流处理。以下是Kafka的一些主要特点: 高吞吐量、低延迟:Kafka每秒可以处理数十万条消息,延迟可以低至几毫秒。这是通过优化数据…...

unsigned char*和const char*的一些问题
1.可以返回字符串常量,但是不能返回char buf[BUF_SIZE] char* get_str(){char* str "hello world";return str; }char* get_str(){char str[32] {0};strcpy(str, "hello world");return str; }//err 2.联合体不能用指针,也…...

前端知识1-4:性能优化进阶
性能优化进阶 Navigation Timing API navigationStart / end 表示从上一个文档卸载结束时 > 如果没有上一个文档,这个值和fetchStart相等 unloadEventStart / end 标识前一个网页unload的时间点 redirectStart / end 第一个http重定向发生和结束的时间 fetch…...

ios 新安装app收不到fcm推送
🏆本文收录于「Bug调优」专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&…...

汽美汽修店管理系统会员小程序的作用是什么
汽车后市场汽美汽修赛道同样存在着大量商家,连锁品牌店或个人小店等,门店扎堆且区域覆盖面积广,当然每天车来车往也有不少生意。 随着线上化程度加深和商家不断拓展市场的需要,传统运营模式可能难以满足现状,尤其是年…...

远程自动锁定平面
目录 Ubuntu 系统上 方法一:使用 SSH 重新连接 方法二:解锁当前会话 方法三:通过 SSH 解锁会话 方法四:禁用自动锁屏(如果合适) windows系统 方法三:修改组策略设置 Ubuntu 系统上 远程…...

鸿蒙Ability Kit(程序框架服务)【UIAbility组件与UI的数据同步】
UIAbility组件与UI的数据同步 基于当前的应用模型,可以通过以下几种方式来实现UIAbility组件与UI之间的数据同步。 [使用EventHub进行数据通信]:在基类Context中提供了EventHub对象,可以通过发布订阅方式来实现事件的传递。在事件传递前&am…...

一个完整的springboot项目,我们还需要做什么
文章目录 一 从0创建Srpingboot项目1.1 启动springboot项目1.2 导入必要的依赖 二、还缺什么2.1 统一异常捕获2.2 统一MVC返回2.3 数据分层2.4 连接数据库模块2.5 放置常量和工具类2.6 基础controller、基础entity、query查询类2.7 为了方便处理异常,一般还可以入参…...

QT-界面居中管理
问题:为什么不能对checkbox直接居中,LineEdit可以 复选框是一个固定大小的控件,不适合填满整个单元格,而相比之下QLineEdit是一个可变大小的控件 关于居中: lineEdit:lineEdit -> setAlignment(QT::Al…...

Python | MATLAB | R 心理认知数学图形模型推断
🎯要点 🎯图形模型推断二元过程概率:🖊模型1:确定成功率 θ 的后验分布 | 🖊模型2:确定两个概率差 δ \delta δ 的后验分布 | 🖊模型3:确定底层概率,后验预…...

Linux系统tab键无法补齐命令-已解决
在CentOS中,按下tab键就可以自动补全,但是在最小化安装时,没有安装自动补全的包,需要安装一个包才能解决 bash-completion 1.检查是否安装tab补齐软件包(如果是最小化安装,默认没有) rpm -q ba…...

数据库之函数、存储过程
函数、存储过程 1.函数 函数,常用于对一个或多个输入参数进行操作,主要目的是返回一个结果值,就是一种方法,在postgre里存放的位置叫function,比如创建一个计算长方面积的函数。 举例:建立一个计算长方形…...

安卓启动 性能提升 20-30% ,基准配置 入门教程
1.先从官方下载demohttps://github.com/android/codelab-android-performance/archive/refs/heads/main.zip 2.先用Android studio打开里面的baseline-profiles项目 3.运行一遍app,这里建议用模拟器,(Pixel 6 API 34)设备运行&a…...

Linux C/C++目录和文件的更多操作
1.access()库函数 用于判断当前用户对目录或文件的存取权限 #include<unistd.h>int accsee(const char *pathname,int mode);参数说明: pathname //目录或文件名 mode //需要判断的存取权限,在<unistd.h>预定义如下#define R_OK 4 //读权…...

如何高效地向Redis 6插入亿级别的数据
如何高效地向Redis插入亿级别的数据 背景不可用的方案可用方案:利用管道插入其他命令:参考: 背景 上一条记录;80G的存储;10几个文件,如何快速导入是一个大问题,也是一个很棘手的问题;如下将给出…...

中国历年肥料进口数量统计报告
数据来源于国家统计局,为1991年到2021年我国每年肥料进口数量统计。 2021年,我国进口肥料909万吨,比上年减少151万吨。 数据统计单位为:万吨 数据说明: 数据来源于国家统计局,为海关进出口统计数 我国肥料…...

即时通讯视频会议平台,WorkPlus本地化部署解决方案
随着现代科技的快速发展,传统的会议方式已经不再满足企业和组织的需求。即时通讯视频会议以其便利性和高效性,成为了现代企业沟通和协作的重要工具。通过即时通讯视频会议,企业可以实现无时差的交流和远程协作,增强团队合作和提高…...

Java的数据库编程-----JDBC
目录 一.JDBC概念&使用条件: 二.mysql-connector驱动包的下载与导入: 三.JDBC编程: 使用JDBC编程的主要五个步骤: 完整流程1(更新update): 完整流程2(查询query): 一.JDB…...

如何获取SSL证书,消除网站不安全警告
获取SSL证书通常涉及以下几个步骤: 选择证书颁发机构(CA): 你需要从受信任的SSL证书颁发机构中选择一个,比如DigiCert、GlobalSign、JoySSL等。部分云服务商如阿里云、腾讯云也提供免费或付费的SSL证书服务。 生成证…...

Unity动画系统介绍
Unity动画系统介绍 Animator组件: 这是Unity中用于控制动画状态的组件,它与Animator Controller一起工作,可以基于游戏逻辑来切换不同的动画状态。 Animator Controller: 这是一个用于管理动画状态机的组件,它允许…...