WindowManagerService添加View流程
我们都知道在android中所有的界面显示相关的,都是通过WindowManager.addView方法来将当前需要显示的View添加到window中。
Window与WindowManager之间的关系
WindowManager的实现类就是WindowManagerImpl:
@Overridepublic void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { // 每一个window都又一个唯一标识的token,这里如果没有,则设置系统默认的 applyDefaultToken(params); // mGlobal是WindowManagerGlobal类型 mGlobal.addView(view, params, mDisplay, mParentWindow);}
然后通过,ViewRootImpl进一步实现当前需要显示的View的绘制,具体可以参考setContentView那些事
可以看到,在framework中Window和PhoneWindow构成了窗口的抽象部分,其中Window为抽象接口,PhoneWindow为具体实现,同样的WindowManager是实现部分的父类
WindowManagerImpl为具体实现逻辑,在WindowManagerImpl中使用WindowManagerGlobal通过IWindowManager接口与WindowManagerService进行交互,并由WMS完成具体的窗口管理工作
public final class WindowManagerGlobal { private static IWindowManager sWindowManagerService; public static IWindowManager getWindowManagerService() { synchronized (WindowManagerGlobal.class) { if (sWindowManagerService == null) { sWindowManagerService = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); try { sWindowManagerService = getWindowManagerService(); ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale()); } catch (RemoteException e) { Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e); } } return sWindowManagerService; } }}
Window与WindowManager建立连接
在Window中维护了一个mWindowManager属性,可以通过 方法设置一个mWindowManager,来和WindowManager建立连接
public abstract class Window { private WindowManager mWindowManager; public void setWindowManager(WindowManager wm, IBinder appToken, String appName) { setWindowManager(wm, appToken, appName, false); } public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { mAppToken = appToken; mAppName = appName; mHardwareAccelerated = hardwareAccelerated || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false); if (wm == null) { wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); } mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); } ....}
关于WindowManagerService
WindowManagerService(WMS),和其他系统服务一样也是在SystemServer中启动的。
public final class SystemServer { private void startOtherServices() { .... // 通过WindowManagerService的静态main方法获取一个WindowManagerService实例 wm = WindowManagerService.main(context, inputManager, mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore); // 将WMS添加到ServiceManager中 ServiceManager.addService(Context.WINDOW_SERVICE, wm); ServiceManager.addService(Context.INPUT_SERVICE, inputManager); mActivityManagerService.setWindowManager(wm); }}
在startOtherServices中,获取WindowManagerService实例,然后添加到ServiceManager中,之后我们就可以通过ServiceManager#getService获取WMS了.
WindowManagerService.main方法
通过WindowManagerService.main方法获取WMS实例,其实就是在main方法内部通过异步方法new了一个WindowManagerService实例。
public static WindowManagerService main(final Context context, final InputManagerService im, final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore) { final WindowManagerService[] holder = new WindowManagerService[1]; DisplayThread.getHandler().runWithScissors(new Runnable() { @Override public void run() { // 通过异步方法创建一个WindowManagerService实例 holder[0] = new WindowManagerService(context, im, haveInputMethods, showBootMsgs, onlyCore); } }, 0); return holder[0];}
WindowManagerService构造方法
private WindowManagerService(Context context, InputManagerService inputManager, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) { // 完成一些初始化工作 mContext = context; mHaveInputMethods = haveInputMethods; mAllowBootMessages = showBootMsgs; mOnlyCore = onlyCore; // 省略代码 // 获取显示服务 mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); // 为每一个display分配一个content mDisplays = mDisplayManager.getDisplays(); for (Display display : mDisplays) { createDisplayContentLocked(display); } mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy); // 获取PowerManager服务,并且注册LowPowerModeObserver mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); mPowerManagerInternal.registerLowPowerModeObserver( new PowerManagerInternal.LowPowerModeListener() { @Override public void onLowPowerModeChanged(boolean enabled) { synchronized (mWindowMap) { if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) { mAnimationsDisabled = enabled; dispatchNewAnimatorScaleLocked(null); } } } }); // 省略代码 // 获取IActivityManager mActivityManager = ActivityManagerNative.getDefault(); mBatteryStats = BatteryStatsService.getService(); mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); AppOpsManager.OnOpChangedInternalListener opListener = new AppOpsManager.OnOpChangedInternalListener() { @Override public void onOpChanged(int op, String packageName) { updateAppOpsState(); } }; ..... // 构建窗口动画 mAnimator = new WindowAnimator(this); // 初始化窗口管理策略 initPolicy(); // 开启绘制SurfaceView事务 SurfaceControl.openTransaction(); ....}
深入理解WindowManagerService
WMS主要用来管理当前窗口和对事件的管理和分发,在IWindowManager.aidl文件中定义了大部分WMS的功能方法,另外作为窗口的管理者,WMS里也定义了各种不同的窗口
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs { // 已经启动完成的应用 final ArrayList mFinishedStarting = new ArrayList<>(); // 尺寸正在改变的窗口 final ArrayList mResizingWindows = new ArrayList<>(); // 动画结束的窗口 final ArrayList mPendingRemove = new ArrayList<>(); // 即将释放Surface的窗口 final ArrayList mDestroySurface = new ArrayList<>(); // 失去焦点的窗口 ArrayList mLosingFocus = new ArrayList<>(); // 为了释放内存,需要强制关闭的窗口 final ArrayList mForceRemoves = new ArrayList<>(); // 等待绘制的窗口 ArrayList mWaitingForDrawn = new ArrayList<>(); // 正在打开的应用 final ArraySet mOpeningApps = new ArraySet<>(); // 正在关闭的应用 final ArraySet mClosingApps = new ArraySet<>(); // 当前获得焦点的窗口 WindowState mCurrentFocus = null; // 上一个获得焦点的窗口 WindowState mLastFocus = null; // 输入发窗口下方的窗口 WindowState mInputMethodTarget = null; // 输入法窗口 WindowState mInputMethodWindow = null; // 得到焦点的应用 AppWindowToken mFocusedApp = null;}
可以看到在WMS中维护的成员变量大都用到了线性表,不同窗口或者同一个窗口在不同阶段可能位于不同的线性表中,对于窗口,主要分为应用窗口和系统窗口
- 应用窗口
应用窗口中,我们常见的activity所处的窗口,应用对话窗口,应用弹出窗口都属于该类,与应用窗口相关的主要是Window和PhoneWindow类
PhoneWindow继承自Window,应用窗口的添加主要通过WindowManager.addView方法将一个DecorView添加到WindowManager中,具体可以参考setContentView那些事
- 系统窗口
我们平时常见的状态栏,导航栏等都是系统窗口,对于系统窗口,不像activity那样使用setContentView来设置布局,它没有专门的封装类,而是直接使用WindowManager.addView方法
将一个View添加到WindowManager中,下面看下PhoneStatusBar的显示过程。
PhoneStatusBar的显示
对于PhoneStatusBar,其主要的是在addStatusBarWindow中添加当前statusbar到WindowManager中的.
private void addStatusBarWindow() { // 加载并创建StatusBarWindowView,StatusBarWindowView继承自FrameLayout makeStatusBarView(); mStatusBarWindowManager = new StatusBarWindowManager(mContext); // 将StatusBarWindowView添加到WindowManager中 mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());}
加载并创建StatusBarWindowView
protected PhoneStatusBarView makeStatusBarView() { final Context context = mContext; Resources res = context.getResources(); updateDisplaySize(); // populates mDisplayMetrics updateResources(); // 加载布局文件,并初始化mStatusBarWindow对象 mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null); .... return mStatusBarView;}
将StatusBarWindowView添加到WindowManager
在StatusBarWindowManager中将StatusBarWindowView添加到WindowManager中的:
public void add(View statusBarView, int barHeight) { mLp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, barHeight, WindowManager.LayoutParams.TYPE_STATUS_BAR, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, PixelFormat.TRANSLUCENT); mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; mLp.gravity = Gravity.TOP; mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; mLp.setTitle("StatusBar"); mLp.packageName = mContext.getPackageName(); mStatusBarView = statusBarView; mBarHeight = barHeight; mWindowManager.addView(mStatusBarView, mLp); mLpChanged = new WindowManager.LayoutParams(); mLpChanged.copyFrom(mLp);}
WindowManager.addView流程分析
上述代码通过WindowManager.addView将当前View显示到屏幕,那么当前View具体是怎么被显示到屏幕的,下面就是我们要讨论的:
我们知道WindowManager是一个接口,其具体的实现类是WindowManagerImpl
public interface WindowManager extends ViewManager
看下WindowManagerImpl#addView方法:
public final class WindowManagerImpl implements WindowManager { private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); @Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { android.util.SeempLog.record_vg_layout(383,params); applyDefaultToken(params); // mGlobal是WindowManagerGlobal类的实例 mGlobal.addView(view, params, mDisplay, mParentWindow); }}
可以看到,上述最终实质上是通过WindowManagerGlobal#addView实现具体的逻辑
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { .... try { // root是ViewRootImpl的实例 root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { .... }}
继续分析ViewRootImpl#setView的逻辑实现:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; .... int res; /* = WindowManagerImpl.ADD_OKAY; */ // 实现具体的绘制操作 requestLayout(); try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); // 通过addToDisplay方法向WMS发起一个Session请求,这里最终会调用Session中对应的方法 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); } catch (RemoteException e) { .... throw new RuntimeException("Adding window failed", e); } finally { if (restore) { attrs.restore(); } } } }}
上面的方法主要做了下面的操作:
1. requestLayout(); // 进行具体的绘制操作
2. 调用了Session.addToDisplay方法:
@Overridepublic int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outStableInsets, outOutsets, outInputChannel);}
最终还是回到了WMS中与其建立连接,并且上述addToDisplay调用最终返回WMS中的addWindow的返回结果。
final WindowManagerPolicy mPolicy = new PhoneWindowManager();public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) { int[] appOp = new int[1]; // mPolicy实际上是一个PhoneWindowManager类型,在checkAddPermission方法中,首先判断窗口类型是否是系统级别的, // 如果不是系统级别的窗口,则返回一个ADD_OKAY,否则需要SYSTEM_ALERT_WINDOW或者INTERNAL_SYSTEM_WINDOW权限 int res = mPolicy.checkAddPermission(attrs, appOp); if (res != WindowManagerGlobal.ADD_OKAY) { return res; } .... synchronized(mWindowMap) { .... boolean addToken = false; WindowToken token = mTokenMap.get(attrs.token); if (token == null) { // 如果窗口是子窗口 if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } // 如果是输入法窗口 if (type == TYPE_INPUT_METHOD) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } // 如果是墙纸窗口 if (type == TYPE_WALLPAPER) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } // 如果是DayDream窗口,即互动屏保 if (type == TYPE_DREAM) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } // 构造WindowToken对象 token = new WindowToken(this, attrs.token, -1, false); addToken = true; } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { // 获取应用的AppWindowToken AppWindowToken atoken = token.appWindowToken; if (atoken == null) { return WindowManagerGlobal.ADD_NOT_APP_TOKEN; } else if (atoken.removed) { return WindowManagerGlobal.ADD_APP_EXITING; } if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { // No need for this guy! if (localLOGV) Slog.v( TAG, "**** NO NEED TO START: " + attrs.getTitle()); return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED; } } else if (type == TYPE_INPUT_METHOD) { if (token.windowType != TYPE_INPUT_METHOD) { // 如果是输入法窗口,token的windowType必须是ADD_BAD_APP_TOKEN类型 return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } .... // 在窗口的有效性检查完成之后,为当前窗口创建一个WindowState对象,来维护窗口的状态以及根据适当的机制来调整窗口的状态 WindowState win = new WindowState(this, session, client, token, attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent); // 如果客户端已经被销毁 if (win.mDeathRecipient == null) { return WindowManagerGlobal.ADD_APP_EXITING; } if (outInputChannel != null && (attrs.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { // 如果输出Channel的读通道为空,则创建通道 String name = win.makeInputChannelName(); InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); win.setInputChannel(inputChannels[0]); inputChannels[1].transferTo(outInputChannel); // 向InputManager中注册通道,以便当前窗口可以接收到事件 mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle); } ..... }}
到现在为止,使用WindowManager.addView方法显示对应的View解析就完成了,重点总结一下:
1. WindowManager#addView—>WindowManagerGlobal#addView—>ViewRootImpl#setView
2. 在ViewRootImpl#setView中的requestLayout();实现具体的绘制操作
3. 在ViewRootImpl#setView中调用Session#addToDisplay
4. 在Session#addToDisplay中最终还是回到了WMS中与其建立连接,并且最终调用WMS的addWindow
5. 在WMS的addWindow方法中,主要做了下面几件事:
检查当前窗口的权限,如果不是系统级别的窗口,则返回一个ADD_OKAY,否则需要SYSTEM_ALERT_WINDOW或者INTERNAL_SYSTEM_WINDOW权限
根据当前窗口类型,返回对应的token值
当前窗口创建一个WindowState对象,来维护窗口的状态以及根据适当的机制来调整窗口的状态,并且通过registerInputChannel,以便当前窗口可以接收输入事件
更多相关文章
- 36个Android开发常用经典代码大全
- Unity 调用 Android(安卓)Native 方法(一) 获得Android系统音量
- 「抄底 Android(安卓)内存优化 3」 —— JVM 内存管理
- Android中的Handler、Looper、Message简要分析
- Android(安卓)实现联网——在线程中联网
- Android(安卓)手动显示和隐藏软键盘 android 隐藏显示输入法键盘
- Android(安卓)Studio ADB响应失败解决方法
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用