Android 应用程序建立与WMS服务之间的通信过程

转载地址:https://blog.csdn.net/yangwen123/article/details/18733631

我们知道WindowManagerService服务运行在SystemServer进程中,应用程序启动Activity时,需要请求WMS为启动的Activity创建对应的窗口,同时WMS也负责修改窗口属性,因此这里就涉及到应用程序进程与WMS服务之间的跨进程交互过程。在前面我们介绍了Android中的Binder通信机制,应用程序进程正是使用Binder通信方式和SystemServer进程交互的。


在应用程序进程中启动的每一个Activity都拥有一个ViewRootImpl对象:

frameworks\base\core\java\android\view\ViewRootImpl.java


 
 
  1. public ViewRootImpl(Context context, Display display) {
  2. //在WMS服务中创建Session Binder对象,同时返回Binder代理对象给当前应用程序进程,并保存在ViewRootImpl的成员变量mWindowSession中
  3. mWindowSession = WindowManagerGlobal.getWindowSession();
  4. ...
  5. //为每一个ViewRootImpl对象创建W Binder对象,用于WMS服务访问对应的Activity
  6. mWindow = new W( this);
  7. ...
  8. mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
  9. ...
  10. mChoreographer = Choreographer.getInstance();
  11. ...
  12. loadSystemProperties();
  13. }


1.建立应用程序到WMS服务之间的连接


从上面图中可以看到,要使应用程序进程可以请求WMS服务,必须在WMS服务这边创建一个类型为Session的Binder本地对象,同时应用程序这边获取Session的代理对象,通过该代理对象,应用程序进程就可以请求WMS服务了。
frameworks\base\core\java\android\view\WindowManagerGlobal.java

  
  
  1. public static IWindowSession getWindowSession() {
  2. synchronized (WindowManagerGlobal.class) {
  3. if (sWindowSession == null) {
  4. try {
  5. InputMethodManager imm = InputMethodManager.getInstance();
  6. //得到WindowManagerService服务的代理对象
  7. IWindowManager windowManager = getWindowManagerService();
  8. //通过WindowManagerService服务代理对象请求在WMS中创建Session本地对象
  9. sWindowSession = windowManager.openSession(imm.getClient(), imm.getInputContext());
  10. float animatorScale = windowManager.getAnimationScale( 2);
  11. ValueAnimator.setDurationScale(animatorScale);
  12. } catch (RemoteException e) {
  13. Log.e(TAG, "Failed to open window session", e);
  14. }
  15. }
  16. return sWindowSession;
  17. }
  18. }
函数首先通过getWindowManagerService来获取WMS服务的代理对象,由于WMS服务是一个有名Binder对象,即已经注册到了ServiceManager进程中,因此可以通过查询服务的方式来获取WMS的代理对象。

  
  
  1. public static IWindowManager getWindowManagerService() {
  2. synchronized (WindowManagerGlobal.class) {
  3. if (sWindowManagerService == null) {
  4. sWindowManagerService = IWindowManager.Stub.asInterface(
  5. ServiceManager.getService( "window"));
  6. }
  7. return sWindowManagerService;
  8. }
  9. }
得到了WMS服务的代理对象后,就可以通过该代理对象请求WMS服务创建一个Session Binder本地对象,该对象用于应用程序进程访问WMS服务。关于应用程序进程使用WMS代理对象如何请求WMS创建Session的过程这里不在介绍,这是Binder通信机制中函数远程调用流程。上面通过调用WMS代理对象的openSession函数后,WMS服务的openSession函数实现如下:
frameworks\base\services\java\com\android\server\wm\WindowManagerService.java

  
  
  1. public IWindowSession openSession(IInputMethodClient client,
  2. IInputContext inputContext) {
  3. if (client == null) throw new IllegalArgumentException( "null client");
  4. if (inputContext == null) throw new IllegalArgumentException( "null inputContext");
  5. Session session = new Session( this, client, inputContext);
  6. return session;
  7. }
这里直接构造一个Session对象,并将该对象返回给应用程序进程,这样在应用程序这边就可以得到Session的代理对象IWindowSession.Proxy,并保存在ViewRootImpl对象的成员变量mWindowSession中。

2.建立WMS服务到应用程序进程之间的连接


前面介绍了应用程序进程到WMS服务之间的连接过程,虽然应用程序进程中的ViewRootImpl对象已经获取WMS服务中的Session的代理对象,也就是说应用程序进程可以请求WMS服务了,但是WMS服务还是不能请求应用程序进程,怎么在应用程序进程和WMS服务之间建立双向连接呢?在前面ViewRootImpl的构造函数中,创建了一个类型为W的对象,W实现了IWindow接口,WMS服务正是通过W的代理对象来请求应用程序进程的。那WMS服务是如何获取W的代理对象的呢?
frameworks\base\core\java\android\view\ViewRootImpl.java

  
  
  1. public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
  2. synchronized ( this) {
  3. if (mView == null) {
  4. mView = view;
  5. ...
  6. //mWindow为W本地对象
  7. res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
  8. getHostVisibility(), mDisplay.getDisplayId(),
  9. mAttachInfo.mContentInsets, mInputChannel);
  10. ...
  11. if (mInputChannel != null) {
  12. if (mInputQueueCallback != null) {
  13. mInputQueue = new InputQueue();
  14. mInputQueueCallback.onInputQueueCreated(mInputQueue);
  15. }
  16. mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
  17. Looper.myLooper());
  18. }
  19. ...
  20. }
  21. }
  22. }
在上一节中已经介绍了,ViewRootImpl对象的成员变量mWindowSession保存了Session的代理对象,向addToDisplay函数传递的第一个参数为成员变量mWindow,而mWindow中保存了W类型的Binder本地对象,因此这里通过函数addToDisplay就可以将W的代理对象传递给WMS服务。
frameworks\base\services\java\com\android\server\wm\Session.java

  
  
  1. public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
  2. int viewVisibility, int displayId, Rect outContentInsets,
  3. InputChannel outInputChannel) {
  4. return mService.addWindow( this, window, seq, attrs, viewVisibility, displayId,
  5. outContentInsets, outInputChannel);
  6. }
Session接收某一个应用程序进程的函数调用请求,并将请求转交给WMS服务来完成。Session和WMS服务之间的关系如下:
frameworks\base\services\java\com\android\server\wm\WindowManagerService.java

  
  
  1. public int addWindow(Session session, IWindow client, int seq,
  2. WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
  3. Rect outContentInsets, InputChannel outInputChannel) {
  4. ...
  5. WindowToken token = mTokenMap.get(attrs.token);
  6. if (token == null) {
  7. ...
  8. token = new WindowToken( this, attrs.token, -1, false);
  9. addToken = true;
  10. //应用程序窗口
  11. } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
  12. AppWindowToken atoken = token.appWindowToken;
  13. ...
  14. //输入法窗口
  15. } else if (type == TYPE_INPUT_METHOD) {
  16. ...
  17. //墙纸窗口
  18. } else if (type == TYPE_WALLPAPER) {
  19. ...
  20. } else if (type == TYPE_DREAM) {
  21. ...
  22. }
  23. //在WMS服务中为窗口创建WindowState对象
  24. win = new WindowState( this, session, client, token,
  25. attachedWindow, appOp[ 0], seq, attrs, viewVisibility, displayContent);
  26. ...
  27. win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
  28. res = mPolicy.prepareAddWindowLw(win, attrs);
  29. ...
  30. if (outInputChannel != null && (attrs.inputFeatures
  31. & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
  32. String name = win.makeInputChannelName();
  33. InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
  34. win.setInputChannel(inputChannels[ 0]);
  35. inputChannels[ 1].transferTo(outInputChannel);
  36. mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
  37. }
  38. ...
  39. if (addToken) {
  40. //mTokenMap哈希表保存了所有的WindowToken对象
  41. mTokenMap.put(attrs.token, token);
  42. }
  43. win.attach();
  44. //mWindowMap哈希表以键值对的方式保存了所有的WindowState对象及W代理对象:<IWindow.Proxy,WindowState>
  45. mWindowMap.put(client.asBinder(), win);
  46. if (win.mAppOp != AppOpsManager.OP_NONE) {
  47. if (mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(), win.getOwningPackage())
  48. != AppOpsManager.MODE_ALLOWED) {
  49. win.setAppOpVisibilityLw( false);
  50. }
  51. }
  52. ...
  53. }
  54. ...
  55. }
该函数首先为应用程序进程新增的窗口在WMS服务中创建对应的WindowState对象,并且将WMS,接收应用程序进程的Session,应用程序进程中的W代理对象保存到WindowState中。

  
  
  1. WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
  2. WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
  3. int viewVisibility, final DisplayContent displayContent) {
  4. mService = service; //标识WMS服务
  5. mSession = s; //标识接收应用程序进程的Session本地对象
  6. mClient = c; //标识应用程序进程中窗口的W代理对象
  7. mAppOp = appOp;
  8. mToken = token; //标识应用程序进程中的窗口布局参数的Token
  9. mOwnerUid = s.mUid;
  10. mWindowId = new IWindowId.Stub() {
  11. @Override
  12. public void registerFocusObserver(IWindowFocusObserver observer) {
  13. WindowState. this.registerFocusObserver(observer);
  14. }
  15. @Override
  16. public void unregisterFocusObserver(IWindowFocusObserver observer) {
  17. WindowState. this.unregisterFocusObserver(observer);
  18. }
  19. @Override
  20. public boolean isFocused() {
  21. return WindowState. this.isFocused();
  22. }
  23. };
  24. ...
  25. }
在构造WindowState对象过程中,又创建了一个用于标识指定窗口的IWindowId本地对象,因此应用程序进程必定会获取该对象的代理对象。关于IWindowId代理对象的获取过程在接下来分析。在WMS服务中为应用程序进程中的指定窗口创建了对应的WindowState对象后,接着调用该对象的attach()函数:
frameworks\base\services\java\com\android\server\wm\WindowState.java

  
  
  1. void attach() {
  2. if (WindowManagerService.localLOGV) Slog.v(
  3. TAG, "Attaching " + this + " token=" + mToken
  4. + ", list=" + mToken.windows);
  5. mSession.windowAddedLocked();
  6. }
在构造WindowState对象时,将用于接收应用程序进程请求的本地Session对象保存在WindowState的成员变量mSession中,这里调用Session的windowAddedLocked()函数来创建请求SurfaceFlinger的SurfaceSession对象,同时将接收应用程序进程请求的Session保存到WMS服务的mSessions数组中。
frameworks\base\services\java\com\android\server\wm\Session.java

  
  
  1. void windowAddedLocked() {
  2. if (mSurfaceSession == null) {
  3. mSurfaceSession = new SurfaceSession();
  4. mService.mSessions.add( this);
  5. }
  6. mNumWindow++;
  7. }

3. IWindowId代理对象获取过程


在前面构造WindowState对象过程中,创建了一个IWindowId本地对象,并保存在WindowState的成员变量mWindowId中,因此,应用程序进程也会获取用于标识每一个窗口对象的IWindowId的代理对象。
frameworks\base\core\java\android\view\View.java

  
  
  1. public WindowId getWindowId() {
  2. if (mAttachInfo == null) {
  3. return null;
  4. }
  5. if (mAttachInfo.mWindowId == null) {
  6. try {
  7. /**获取WMS服务端的IWindowId代理对象,mAttachInfo在前面创建ViewRootImpl对象时创建
  8. * mWindow = new W(this);
  9. * mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
  10. * 在AttachInfo的构造函数中:
  11. * mWindow = window;
  12. * mWindowToken = window.asBinder();
  13. * 因此AttachInfo的成员变量mWindow和mWindowToken引用了同一对象,该对象就是类型为W的本地binder对象
  14. * 下面通过W本地binder对象,来获取AMS服务端的IWindowId的代理对象
  15. */
  16. mAttachInfo.mIWindowId = mAttachInfo.mSession.getWindowId(mAttachInfo.mWindowToken);
  17. WindowId类的定义主要是为了方便在进程间传输IWindowId Binder对象
  18. mAttachInfo.mWindowId = new WindowId(mAttachInfo.mIWindowId);
  19. } catch (RemoteException e) {
  20. }
  21. }
  22. return mAttachInfo.mWindowId;
  23. }
应用程序进程还是通过Session的代理对象来获取IWindowId的代理对象,参数window在应用程序进程端为W本地binder对象,经过Binder传输到达WMS服务进程后,就转换为W的binder代理对象了。
frameworks\base\services\java\com\android\server\wm\Session.java

  
  
  1. public IWindowId getWindowId(IBinder window) {
  2. return mService.getWindowId(window);
  3. }
在WMS服务中的Session本地对象又将IWindowId对象的查找过程交给WMS服务。
frameworks\base\services\java\com\android\server\wm\WindowManagerService.java

  
  
  1. public IWindowId getWindowId(IBinder token) {
  2. synchronized (mWindowMap) {
  3. //通过W的binder代理对象从mWindowMap哈希表中查找对应的WindowState对象。
  4. WindowState window = mWindowMap.get(token);
  5. return window != null ? window.mWindowId : null;
  6. }
  7. }
根据W的binder代理对象token在WMS中查找窗口对应的WindowState对象,再将该窗口在WindowState对象中创建的IWindowId Binder本地对象返回,这样,客户端进程就可以得到该Binder的代理对象。

查找过程如下:
通过以上介绍,可以知道应用程序进程和WMS服务之间的交互主要是通过IWindowSession,Iwindow,IWindowId三个接口来完成,IWindowSession实现应用程序进程到WMS服务间的通信,而Iwindow和IWindowId则实现WMS服务到应用程序进程间的通信。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值