老李推荐:第5章5节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 获取系统服务引用 2

MonkeyKeyEvent这个类的injectEvent方法,该方法支持的输入参数就有上面提到的WindowManagerService和ActivityManagerService,但实际上这两个服务并没有用到的。请看代码如下:

代码5-5-2 MonkeyKeyEvent - injectEvent示例

      @Override     public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {         if (verbose > 1) {             String note;              if (mAction == KeyEvent.ACTION_UP) {                  note = "ACTION_UP";              } else {                  note = "ACTION_DOWN";              }              try {                  System.out.println(":Sending Key (" + note + "): "                          + mKeyCode + "    // "                         + MonkeySourceRandom.getKeyName(mKeyCode));             } catch (ArrayIndexOutOfBoundsException e) {                 System.out.println(":Sending Key (" + note + "): "                         + mKeyCode + "    // Unknown key event");             }         }         KeyEvent keyEvent = mKeyEvent;         if (keyEvent == null) {             long eventTime = mEventTime;             if (eventTime <= 0) {                 eventTime = SystemClock.uptimeMillis();             }             long downTime = mDownTime;             if (downTime <= 0) {                 downTime = eventTime;             }             keyEvent = new KeyEvent(downTime, eventTime, mAction, mKeyCode,                     mRepeatCount, mMetaState, mDeviceId, mScanCode,                     KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_KEYBOARD);         }         if (!InputManager.getInstance().injectInputEvent(keyEvent,                 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT)) {             return MonkeyEvent.INJECT_FAIL;         }         return MonkeyEvent.INJECT_SUCCESS;     } }
 

参数中虽然是传进来了WindowManagerService和ActivityManagerService的服务的引用,但是最终整个注入按键事件的方法体中并没有用到。最终注入事件也是通过InputManager来实现的,并没有通过上面的这些服务。其实如果我们返回老一点的版本,会看到这里注入按键事件时使用到的会是WindowManagerService。比如我查到的MonkeyKeyEvent最后一次使用WindowManagerService来进行按键事件注入的版本是android-4.0.4_r2.1,请看下图:

图5-5-2 MonkeyKeyEvent老版本事件注入方式 

MonkeyKeyEvent相关的详细分析我们留给下一章来描述,这里我们只是想通过MonkeyKeyEvent事件注入方式的变化来告诉大家其实新版本的MonkeyRunner不会再直接使用WindowManagerService来进行按键事件注入而已。

当然,InputManagerService服务管理的主要是用户输入的操作,其他一些窗口相关的操作还是需要用到WindowManagerService来进行处理的。比如模拟屏幕旋转的MonkeyRotationEvent就不属于用户输入的范畴,就需要使用到WindowManagerService服务来往系统注入相应的事件。请看下面代码:

代码5-5-3 MonkeyRotationEvent - injectEvent

 @Override 40 public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) { ...

 // inject rotation event

  try { 47 iwm.freezeRotation(mRotationDegree); ...

 }

...

 }

 

从以上代码我们可以看到模拟屏幕旋转的操作是通过调用WindowManagerService服务的freezeRotation方法来实现的。

那么我们往下还是看下getSystemInterfaces这个方法是如何获得这些服务的引用的:

代码5-5-4 Monkey - getSystemInterfaces

      /**       * Attach to the required system interfaces.       *       * @return Returns true if all system interfaces were available.       */      private boolean getSystemInterfaces() {          mAm = ActivityManagerNative.getDefault();        ...          mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));        ...          mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));        ...      }

从以上代码可以看到这些系统服务接口的获取方式,除了ActivityManagerService外都是使用了AIDL的机制,以下简要解析下这三个服务获取的一些基本知识:

  • ActivityManagerNative.getDefault返回来的其实并不是ActivityManagerService的实例,而是代理类ActivityManagerProxy的实例,而该代理类实际上代理的就是ActivityManagerService。这里ActivityManager,ActivityManagerService和ActivityManagerProxy使用了设计模式中的代理模式。至于它们是怎么实现的就超出了本书的边界了,读者如果感兴趣的本人推荐你去看罗升阳写的《Android系统源代码情景分析》。

  • 下面的WindowManager和PackageManager都是通过Android的AIDL机制来获得的。大家应该都清楚,Android系统中的每个进程都是独立运行的,进程之间是不能直接互相调用的,它们是各自活在自己的虚拟世界里的。因此,当两个进程需要互动时就需要提供一些机制在不同进程之间进行数据通信。比如我们要使用到的这些服务都是独立的,为了使其他的应用程序也可以访问这些服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现,在安卓中该机制叫做Binder机制,类似于Windows上的COM和Linux上的Corba。而与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。841-842行调用的IWindowManagerService和IPackageManagerService的Stub.asInterface方法就是AIDL机制中获取对应远程服务的代理引用的方法。有了这些服务的代理引用后,用户就可以像本地调用一样来调用这些远程服务了。

看完这几个服务的初始化,大家可能会有点疑问,不是说现在主要是用InputManagerService服务来进行事件注入吗?怎么没有看到对这个服务的引用进行初始化啊? 其实从代码5-5-2中我们可以看到,对该服务的引用是通过InputManager的getInstance方法来实现的。其实看到这个getInstance方法,我们应该立刻能判断出它其实是一个单例模式实现的类。

代码5-5-5 InputManager - getInstance方法

        public static InputManager getInstance()     {       synchronized (InputManager.class) {        if (sInstance == null) {          IBinder b = ServiceManager.getService("input");           sInstance = new InputManager(IInputManager.Stub.asInterface(b));         }         return sInstance;       }     }

从以上代码我们看到它确实是以单例模式进行实现的,186行判断如果已经存在一个InputManager这个引用InputManagerService服务的实例的话就会跳到190行直接把改引用对象返回给调用者。

当然,如果之前就没有建立好该引用的话,那么就需要在189-188行先对InputManager这个引用对象进行初始化了。之所以这了把InputManager称呼为应用对象主要是因为它接受到用户的命令请求后,主要就是直接抛给InputManagerService服务来进行处理的。这里187行先通过ServiceManager的getService方法获得代表InputManagerService这个远程服务对象的IBinder接口(Binder是安卓进程间通信的核心机制,往往配合AIDL这个接口定义机制来使用)。所以在188行我们可以看到该代码先是通过InputManager的接口IIputManager.Stub.asInterface方法获得InputManagerService的代理引用,然后再把该引用对象作为参数传给InputManager的构造函数来保存起来。这样的话当我们调用InputManager实例对象来进行事件注入的话就可以直接通过刚保存起来的对InputManagerService的引用来请求InputManagerService服务来注入事件了。

这一节我们在分析获取系统服务引用的过程中顺带简单描述了下这些服务的一些背景知识,但这里需要再次强调的是安卓的服务和进程间通信IPC机制的知识是远不止此的,但由于它们的机制以及实现的细节并不是本书的重点,所以这里只是简单的描述,如果大家对它们的机制和实现感兴趣的,可以自行进行安卓操作系统源码的分析,或者查阅其他相关书籍,比如上面提到的罗升阳著的《安卓系统源码情景分析》。

 
 


你可能感兴趣的:(软件测试开发)