Android应用程序键盘(Keyboard)消息处理机制分析(27)

         Step 14. NativeInputQueue.unregisterInputChannel

        这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:

  1. status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChannelObj) {  
  2.     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,  
  3.         inputChannelObj);  
  4.     ......  
  5.   
  6.     { // acquire lock  
  7.         AutoMutex _l(mLock);  
  8.   
  9.         ssize_t connectionIndex = getConnectionIndex(inputChannel);  
  10.         ......  
  11.   
  12.         sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  
  13.         mConnectionsByReceiveFd.removeItemsAt(connectionIndex);  
  14.   
  15.         connection->status = Connection::STATUS_ZOMBIE;  
  16.   
  17.         connection->looper->removeFd(inputChannel->getReceivePipeFd());  
  18.   
  19.         env->DeleteGlobalRef(connection->inputHandlerObjGlobal);  
  20.         connection->inputHandlerObjGlobal = NULL;  
  21.         ......  
  22.     } // release lock  
  23.   
  24.     ......  
  25.     return OK;  
  26. }  

        真正的注销工作就是这里实现的了,读者可以对照前面介绍应用程序注册键盘消息接收通道过程中的Step 21(NativeInputQueue.registerInputChannel)来分析,它首先是将在之前创建的Connection对象从NativeInputQueue中的mConnectionByReceiveFd向量中删除:

  1. ssize_t connectionIndex = getConnectionIndex(inputChannel);  
  2. ......  
  3.   
  4. sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  
  5. mConnectionsByReceiveFd.removeItemsAt(connectionIndex);  

        然后再把这个Client端InputChannel的前向管道的读端文件描述符从应用程序主线程中的Looper对象中删除:

  1. connection->looper->removeFd(inputChannel->getReceivePipeFd());  

        这样,这个Activity窗口以后就不会接收到键盘事件了。

 

        最后将Connection对象中的回调对象inputHandlerOjbGlobal对象删除:

  1. env->DeleteGlobalRef(connection->inputHandlerObjGlobal);  
  2. connection->inputHandlerObjGlobal = NULL;  

        回忆一下前面我们在分析InputManager分发键盘消息给应用程序处理时,曾经说到,每当有键盘事件发生时,InputManager首先就会调用NativeInputQueue类的handleReceiveCallback函数。在这个handleReceiveCallback函数里面,NativeInputQueue会找到相应的Connection对象,然后把它里面的内部对象inputHandlerOjbGlobal作为参数来调用Java层的InputQueue类的dispatchKeyEvent函数来通知应用程序,有键盘事件发生了。在InputQueue类的dispatchKeyEvent函数里面,就是通过这个inputHandlerOjbGlobal对象来直正通知到当前激活的Activity窗口来处理这个键盘事件的。

 

        注册在应用程序这一侧的Client端InputChannel被注销以后,回到前面的Step 11中,我们继续分析注销注册在InputManager这一侧的Server端InputChannel。 Step 15. WindowManagerService.Session.remove

        这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     private final class Session extends IWindowSession.Stub  
  6.             implements IBinder.DeathRecipient {  
  7.         ......  
  8.   
  9.         public void remove(IWindow window) {  
  10.             removeWindow(this, window);  
  11.         }  
  12.   
  13.         ......  
  14.     }  
  15.   
  16.     ......  
  17. }  

 

        这个函数只是简单地调用其外部类WindowManagerService的removeWindow函数来进一步执行操作。

 

        Step 16. WindowManagerService.removeWindow
        这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     public void removeWindow(Session session, IWindow client) {  
  6.         synchronized(mWindowMap) {  
  7.             WindowState win = windowForClientLocked(session, client, false);  
  8.             if (win == null) {  
  9.                 return;  
  10.             }  
  11.             removeWindowLocked(session, win);  
  12.         }  
  13.     }  
  14.   
  15.     ......  
  16. }  

 

        回忆一下前面我们在分析应用程序注册键盘消息管道的过程时,在Step 11(WindowManagerService.addWindow)中,WindowManagerService为这个即将要激活的Activity窗口创建了一个WindowState对象win,创建的时候,使用了从ViewRoot中传过来的两个参数,分别是一个Session对象session和一个IWindow对象client。 

       在这个函数中,ViewRoot传过来的两个参数session和client和上面说的两个参数是一致的,因此,这个函数首先通过参数session和client得到一个WindowState对象win,然后调用removeWindowLocked来把它从WindowManagerService删除。

        Step 17. WindowManagerService.removeWindowLocked
        这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     public void removeWindowLocked(Session session, WindowState win) {  
  6.         ......  
  7.   
  8.         win.disposeInputChannel();  
  9.   
  10.         ......  
  11.     }  
  12.   
  13.     ......  
  14. }  

        我们忽略了这个函数的其它逻辑,只关注注销之前注册的Server端InputChannel的逻辑,这里,注销的操作就是调用win的disposeInputChannel进行的了。

       Step 18. WindowState.disposeInputChannel

        这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     private final class WindowState implements WindowManagerPolicy.WindowState {  
  6.         ......  
  7.   
  8.         void disposeInputChannel() {  
  9.             if (mInputChannel != null) {  
  10.                 mInputManager.unregisterInputChannel(mInputChannel);  
  11.   
  12.                 mInputChannel.dispose();  
  13.                 mInputChannel = null;  
  14.             }  
  15.         }  
  16.   
  17.         ......  
  18.     }  
  19.   
  20.     ......  
  21. }  
        上面说到,在前面分析应用程序注册键盘消息管道的过程时,在Step 11(WindowManagerService.addWindow)中,为当前这个Activity窗口创建了一个WindowState对象,接着创建了一个输入管道后,把Server端的InputChannel保存了在这个WindowState对象的成员变量mInputChannel中,因此,这里,就可以把它取回来,然后调用mInputManager对象的unregisterInputChannel函数来把它注销掉了。

你可能感兴趣的:(android,keyboard,消息处理机制分析)