您的位置:首页 > 产品设计 > UI/UE

Android 7.1 GUI系统-窗口管理WMS-窗口添加(三)

2017-12-11 22:44 477 查看
窗口的添加过程。Android中窗口通常分为两大类,一是系统窗口,一是应用窗口。添加的过程上,WMS不会特别区分这两类窗口,只是在权限和层级有差别。
1)系统窗口的添加,以状态栏为例。

private void addStatusBarWindow() @PhoneStatusBar.java{
//把R.layout.super_status_bar资源inflate为View对象mStatusBarWindow,
makeStatusBarView();
mStatusBarWindowManager = new StatusBarWindowManager(mContext);
mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
mHeadsUpManager);
//getStatusBarHeight()获取状态栏的高度。
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
//添加statusbar view到WindowManager。public void add(View statusBarView, int barHeight) @StatusBarWindowManager.java{
//设置窗口属性和类型。
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.setTitle("StatusBar");
mStatusBarView = statusBarView;
//调用WindowManagerImpl.java的addView方法。
mWindowManager.addView(mStatusBarView, mLp);
}
先看下mWindowManager对象的获取,在StatusBarWindowManager的构造函数中有:mWindowManager= (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);这里的Context.WINDOW_SERVICE字符串值:StringWINDOW_SERVICE = "window";//通过类名查找一个系统级别的service。
public final <T> T getSystemService(Class<T> serviceClass) @Context.java{
//子类会重写参数为string的 getSystemService方法,这里先把class映射到Service name,然后调用string参数的getSystemService。
String serviceName = getSystemServiceName(serviceClass);
return serviceName != null ? (T)getSystemService(serviceName) : null;
}
//上面两个函数的实现在ContextImpl.java中。
public String getSystemServiceName(Class<?> serviceClass) @ContextImpl.java{
return SystemServiceRegistry.getSystemServiceName(serviceClass);
}
public Object getSystemService(String name) @ContextImpl.java{
return SystemServiceRegistry.getSystemService(this, name);
}
为了能够快速的获取常用的系统服务,Android把这些service放在两个MAP中,一个是:HashMap<Class<?>,String> SYSTEM_SERVICE_NAMES = new HashMap<Class<?>,String>();,映射的是class到String类型的serviceName;一个是HashMap<String,ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =newHashMap<String, ServiceFetcher<?>>();映射是ServiceName到实际返回的Service对象。//这两个map的初始化在SystemServiceRegistry的静态代码块中。
Static @SystemServiceRegistry.java{
//可以看到 Context.WINDOW_SERVICE对应的Service是 WindowManagerImpl,其他系统service也是类似的实现方式。
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
}
然后接着看addView的实现。WindowManagerImpl继承自WindowManager,又间接继承自ViewManager。WindowManager.java这个接口是app跟系统的窗口管理员通信的接口,很多的窗口属性都是在这里定义的。

public final class WindowManagerImpl implements WindowManager {
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
}
//mGlobal是WindowManagerGlobal.java类型的对象,是全局变量,也是单实例,所有的窗口都会在mGlobal
1379f
中有记录。

public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) @WindowManagerGlobal.java{
//窗口属性
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//ViewTree的管理者 ViewRootImpl。
ViewRootImpl root;
//遍历mViews列表,检查这个view是否已经添加过,如果已经添加过,禁止重复添加。
int index = findViewLocked(view, false);
//如果是一个子窗口,要找到它的父窗口。
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++) {
//mWindow是ViewRootImpl中的变量,用于WMS访问app的接口。
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
//实例化 ViewRootImpl对象。
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);

// mViews的元素对应了要添加的ViewTree, mRoots中元素对应了ViewTree的管理者, mParams中的元素对应了窗口的属性,同一个索引值在这三个列表中描述的是同一个对象。
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
//从ViewRootImpl的setView开始,将会跟WMS通信,主要分析窗口的添加,其他代码略。public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView)@ViewRootImpl.java {
if (mView == null) {
//一个ViewRootImpl对应一棵ViewTree,用mView保存这个ViewTree。
mView = view;
//执行第一次layout,在把window添加Window manager之前,因为WMS除了管理窗口,还负责分发事件,所以执行relayout是做好接收系统事件的准备。
RequestLayout();
//通过WindowSession向WMS申请添加窗口,IWindowSession是ViewRootImpl跟WMS通信的桥梁,由aidl接口描述,它是匿名的BinderServer,获取方式是通过实名的Binderserver类WindowManagerService的接口openSession实现。
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
}
IWindowSession对应Server端的实现是Session.java。public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
直接调用WindowManagerService的实现addWindow。public int addWindow(Session session, IWindow client, int seq,WindowManager.LayoutParams attrs, int viewVisibility, int displayId,Rect outContentInsets, Rect outStableInsets, Rect outOutsets,InputChannel outInputChannel)@WindowManagerService.java{
int[] appOp = new int[1];
//权限检查,mPolicy是PhoneWindowManager.java实例,如果是应用窗口,直接返回ok,如果系统窗口会细分,有些系统窗口的添加需要相应权限,比如TYPE_SYSTEM_ALERT,TYPE_SYSTEM_ERROR等。
int res = mPolicy.checkAddPermission(attrs, appOp);

WindowState attachedWindow = null;
final int callingUid = Binder.getCallingUid();
final int type = attrs.type;
//获取指定Displayid的显示内容。
final DisplayContent displayContent = getDisplayContentLocked(displayId);
//判断user是由有权限访问这个display。
if (!displayContent.hasAccess(session.mUid)) {
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
//判断是否已经添加过。
if (mWindowMap.containsKey(client.asBinder())) {
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
//如果是子窗口,找到其父窗口,但是父窗口不能再是别的窗口的子窗口。
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
attachedWindow = windowForClientLocked(null, attrs.token, false);
if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}

boolean addToken = false;
WindowToken token = mTokenMap.get(attrs.token);
AppWindowToken atoken = null;
//根据窗口的类型检查有效性,
if (token == null) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
atoken = token.appWindowToken;
}

//生成一个表达窗口相关信息的WindowState对象,其中的 session是IWindowSession;client是IWindow,是由ViewRootImpl中的W类对象; token是WindowToken对象。
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
//如果前面新增加了token(WindowToken),把它添加到全局变量mTokenMap中。
if (addToken) {
mTokenMap.put(attrs.token, token);
}
// client是IWindow对象,有ViewRootImpl传过来,作为WMS访问ViewRoot的通道,win是WindowState。
mWindowMap.put(client.asBinder(), win);
//把将窗口win按顺序添加到windowlist中,
addWindowToListInOrderLocked(win, true);
//通过getInsetHintLw计算窗口的内容区域。
if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, mRotation,
displayInfo.logicalWidth, displayInfo.logicalHeight, outContentInsets,
outStableInsets, outOutsets)) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
}
//分配层级值。
mLayersController.assignLayersLocked(displayContent.getWindowList());
}分析几个相近的变量,//从IWindow到服务端窗口的映射,其中的IWindow是ViewRootImpl中的W实例,在addWindow过程中添加这个映射关系,其中IWindow.asBinder转成IBinder作为key。HashMap<IBinder,WindowState> mWindowMap = new HashMap<>();
//从token IBinder到WindowToken的映射。其中IBinder代表了这个窗口的主人,比如上面函数中的attrs.token,其中的IBinder对应了ActivityRecord(AMS中的)中的appToken,是在添加窗口时通过WindowManager.layoutParams设置的。HashMap<IBinder,WindowToken> mTokenMap = new HashMap<>();在startActivity的过程中,其中一步startActivityLocked,会调用mWindowManager.addAppToken(...r.appToken…);通过mTokenMap.put(token.asBinder(),atoken);在WMS中备案;其中的token是AppWindowToken,其父类WindowToken。在申请窗口的过程中,如果attrs.token找不到匹配的WindowToken,也就是说它在AMS中没有记录,如果窗口类型是:FIRST_APPLICATION_WINDOW~LAST_APPLICATION_WINDOW,TYPE_INPUT_METHOD,TYPE_WALLPAPER,TYPE_DREAM,TYPE_QS_DIALOG.等还有几类,这些窗口类型在mTokenMap一定要有对应关系,就是在startActivity的过程中完成的,如果找不到对应关系,说明启动步骤出错了,窗口的申请也会报错返回。除去前面说的哪几类窗口类型外,允许在AMS中没有记录,程序会为这个窗口生成一个WindowToken,并把addToken置为true,随后把这个WindowToken添加到mTokenMap中(mTokenMap.put(attrs.token,token);)。
2)应用窗口的添加。以Activity为主体的应用为例,startactivity时,AMS会判断该Activity所属的应用进程是否已经在运行,如果是,在resumeTopActivityInnerLocked过程中,就调用ApplicationThread的函数scheduleResumeActivity,向这个进程发出启动指定Activity的指令。ApplicationThread是主线程ActivityThread的内部类,也是应用进程跟AMS通信的桥梁。scheduleResumeActivity会通过RESUME_ACTIVITY消息,调用handleResumeActivity函数。如果应用进程还没启动,就要先创建应用进程,运行ActivityThread主线程,然后attachApplication回调AMS,AMS在判断有最顶部的可见Activity等待这个进程启动,就会调用ApplicationThread的函数scheduleLaunchActivity来启动指定的Activity。scheduleLaunchActivity通过LAUNCH_ACTIVITY消息,调用handleLaunchActivity,handleLaunchActivity在调用performLaunchActivity后,也会调用handleResumeActivity。
Activity从启动到显示,会经过onCreate,onStart,onResume,其中onResume就是在窗口即将可见时被调用的,在onResume被调用后,主线程ActivityThread会通过该WindowManagerImpl把应用窗口添加到系统中。

final void handleResumeActivity(IBinder token,boolean clearHide, boolean isForward,
boolean reallyResume, int seq, String reason) @ActivityThread.java{
//performResumeActivity 会通过Instrumentation调用Activity的onResume()。
ActivityClientRecord r = mActivities.get(token);
r = performResumeActivity(token, clearHide, reason);
//如果窗口还没添加到WindowManager,并且Activity的状态不是finished,又是即将变为visible,那就把这棵ViewTree添加到WindowManager。
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
//一个Activity对应的View树的根是DecorView,在onCreate中通过setContentView设置的是DecorView的内容,不包括标题栏等装饰部分。
View decor = r.window.getDecorView();
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
//指定窗口类型TYPE_BASE_APPLICATION;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
//wm实际是WindowManagerImpl实例,通过它把窗口添加到WMS。
wm.addView(decor, l);
}
}


WindowManagerImpl的addView后面的处理过程跟系统窗口的添加是一致的,不在重复。窗口添加的过程中有一步把窗口添加到全局的窗口列表中,具体看看这个过程。private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) @WindowManagerService.java{
//没有父窗口的情况。
if (win.mAttachedWindow == null) {
final WindowToken token = win.mToken;
int tokenWindowsPos = 0;
//.appWindowToken不为null,说明Activity相关的窗口,应用程序窗口的添加就是走这里。
if (token.appWindowToken != null) {
tokenWindowsPos = addAppWindowToListLocked(win);
}else{
addFreeWindowToListLocked(win);
}
if (addToToken) {
token.windows.add(tokenWindowsPos, win);
}
}else{
addAttachedWindowToListLocked(win, addToToken);
}
//把与Activity有关的窗口添加到allAppWindows,这个列表包含了属于这个token的所有窗口,包括子窗口。
final AppWindowToken appToken = win.mAppToken;
if (appToken != null) {
if (addToToken) {
appToken.addWindow(win);
}
}
}
private int addAppWindowToListLocked(final WindowState win) @WindowManagerService.java{
final DisplayContent displayContent = win.getDisplayContent();
final WindowToken token = win.mToken;
//当前显示设备下的所有窗口。
final WindowList windows = displayContent.getWindowList();
//这个token下的所有窗口,一个应用程序下的所有窗口,可以通过token.windows获取。
WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);

//应用下已经有至少一个窗口了,通过placeWindowBefore 简单的把新窗口放到顶部,WindowState lowestWindow = tokenWindowList.get(0);placeWindowBefore(lowestWindow, win);。
if (!tokenWindowList.isEmpty()) {
return addAppWindowToTokenListLocked(win, token, windows, tokenWindowList);
}

//下面的代码就是应用程序下目前还没有窗口的情况,这种情况新窗口添加的位置,要基于这个应用的位置。
WindowState pos = null;
//获取所有的task,循环遍历,找到匹配的token,
final ArrayList<Task> tasks = displayContent.getTasks();
for (taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
for (tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
final AppWindowToken t = tokens.get(tokenNdx);
if (t == token) {
--tokenNdx;
break;
}
}
//获取这个token的所有窗口的列表,获得一个插入位置pos。
tokenWindowList = getTokenWindowsOnDisplay(t, displayContent);
pos = tokenWindowList.get(0);
}
//pos不为null,说明找到了窗口插入的位置,接着在mTokenMap 中找到pos窗口对应的WindowToken,最后把需要添加的 窗口的添加到参考窗口的下面 placeWindowBefore。
if (pos != null) {
WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
if (atoken != null) {
tokenWindowList =getTokenWindowsOnDisplay(atoken, displayContent);
}
final int NC = tokenWindowList.size();
if (NC > 0) {
WindowState bottom = tokenWindowList.get(0);
pos = bottom;
}
placeWindowBefore(pos, win);
}

如果前一步没有找到合适的位置,还没继续查找剩下AppTokenList tokens中第一个带有窗口的token,然后在这个token的窗口列表中查找参考位置。直到循环结束,如果依然找不到合适的位置,就以这个窗口的BaseLayer为参考位置。
final int myLayer = win.mBaseLayer;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: