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

窗口的添加过程。

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 viewWindowManager

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>();

映射的是classString类型的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);
	}
}

//mGlobalWindowManagerGlobal.java类型的对象,是全局变量,也是单实例,所有的窗口都会在mGlobal中有记录。


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);
}

//ViewRootImplsetView开始,将会跟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到服务端窗口的映射,其中的IWindowViewRootImpl中的W实例,在addWindow过程中添加这个映射关系,其中IWindow.asBinder转成IBinder作为key。

HashMap<IBinder,WindowState> mWindowMap = new HashMap<>();


//token IBinderWindowToken的映射。其中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中备案;其中的tokenAppWindowToken,其父类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回调AMSAMS在判断有最顶部的可见Activity等待这个进程启动,就会调用ApplicationThread的函数scheduleLaunchActivity来启动指定的ActivityscheduleLaunchActivity通过LAUNCH_ACTIVITY消息,调用handleLaunchActivityhandleLaunchActivity在调用performLaunchActivity后,也会调用handleResumeActivity


Activity从启动到显示,会经过onCreateonStartonResume,其中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);
	}
}


WindowManagerImpladdView后面的处理过程跟系统窗口的添加是一致的,不在重复。

窗口添加的过程中有一步把窗口添加到全局的窗口列表中,具体看看这个过程。

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;
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
仓库管理系统WMS)是一种用于管理仓库操作和流程的软件系统。它通过自动化和优化仓库流程,提高了仓库的效率和准确性。 WMS系统可以帮助企业实现对仓库的全面掌控和监管。它提供了实时的库存信息、调度规划、收货、上架、拣货、打包、出货等功能。用户可以通过WMS系统对仓库作业进行统一调度和管理,减少了人为因素对仓库作业的影响。 WMS系统的核心功能包括库存管理和作业管理。库存管理功能包括库存查询、库存调整、库存盘点等,通过对库存信息的准确记录和实时更新,可以避免库存过多或不足的情况。作业管理功能包括入库作业、出库作业、移库作业等,通过对各项作业进行计划、分派和跟踪,提高了作业效率和准确性。 WMS系统还提供了一些高级功能,如货物跟踪、批次管理、质量管理等。货物跟踪功能可以帮助用户实时了解货物的流向和位置,提高了货物配送的准确性。批次管理功能可以对货物进行批次的管理和追溯,方便了企业对产品质量的控制和追踪。质量管理功能可以对货物的质量信息进行记录和追踪,保证了产品质量的可追溯性。 总之,WMS系统是一种能够提高仓库效率、准确性和可控性的管理工具。通过实时监控和跟踪仓库的各项作业和库存信息,可以帮助企业降低成本、提高服务水平,提高了企业的竞争力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值