Android平台按键消息处理流程一(Android4.2.2)

来源:互联网 发布:linux工程师经验之谈 编辑:程序博客网 时间:2024/05/16 11:15

Android平台按键消息处理流程

       通过在网上看Andorid相关处理流程,发现Android不同的版本消息处理流程差别还是蛮大的,因此先说明代码流程是基于Android4.2.2。在Android系统中,按键消息主要是WindowManagerService服务来管理的,其初始化在System Server中,所以我先分析一下SystemServer中相关的初始化。 

1.1 加载JNI库

      在Android系统中,Zygote进程负责启动系统服务进程SystemServer,而系统服务进程SystemServer负责启动系统中的各种关键服务。SystemServer核心逻辑的入口是main,代码位于SystemServer.java文件中,对应main函数中我们先看如下代码

public static void main(String[] args){   

.......

System.loadLibrary("android_servers"); //加载JNI动态库

init1(args);//native函数

}

     我们首先看一下init(args)函数的原型申明native public static void init1(String[] args);函数前面的java关键字native表明该函数为native函数(Native一般指的是用C/C++编写的函数)。对于java要调用native函数,就必须通过一个位于JNI层的动态库。 动态库的调用是通过函数System.loadLibrary是实现的。System.loadLibrary函数的参数是动态库的名字,即android_servers。系统会自动根据不同的平台更新成真实的动态库文件名。Android的底层是linux系统,默认会更新为libandroid_servers.so(windows平台上默认会xxx.dll)。

      对于java层,调用JNI只需要完成2步就OK了,一是加载JNI对应的库,二是声明由关键字native修饰的函数并调用。

1.2 JNI函数注册

    

     通过阅读代码,我们会发现JAVA层调用的native函数名称,与实际的JNI层对应的函数是不一样的,我们已前面的init1()函数为说明,init1()函数实际对应的JNI函数为android_server_SystemServer_init1。但代码中Java层的NATIVE函数与JNI层怎么建立一一对应关系的,这其实就是JNI函数的注册问题,通过注册就是将JAVA层的NATIVE函数与JNI层函数关联对应。有了这层关联,调用JAVA层的native函数时,就能顺利找到JNI层对应的函数执行了。对于JNI的注册有2种方式,一种是静态注册,一种是动态注册。2种注册的区别,可以上网查一下。Android代码中才采用动态注册方法来建立java层与JNI层的连接。     

1.2.1 JNI的动态注册

在JNI中是通过JNINativeMethod的结构来保存JAVA层的NATIVE函数与JNI层函数一一对应关系,JNINativeMethod的定义如下:

typedef struct {

const char *name;//Java中native函数的名字,不用携带具体的包的路径

const char *signature;//JAVA函数签名信息(参数类型和返回值)

void *fnPtr;      //JNI层对应函数的函数指针

}JNINativeMethod;

 

     上述结构体中中三个变量,name为Java层中调用的native的函数名称;signature变量为name变量对应的native函数的签名信息(参数类型和函数返回值);fnPtr为JNI层对应的函数指针。上述结构体中的签名信息的出现,主要是因为java支持函数重载,也就是说,可以定义同名但不同参数的函数。然而仅仅根据函数名是没法找到具体函数的。为了解决这个问题,JNI技术中就将参数类型和返回值类型的组合作为一个函数的签名信息,有了签名信息,就能顺利的找到java中的函数了。

     因此通过定义上述结构,就可以完成JAVA层的NATIVE函数与JNI层函数对应关系,我们看一下(已前面提到的native函数init1()为例)

static JNINativeMethod gMethods[] = {

    /* name, signature, funcPtr */

    { "init1", "([Ljava/lang/String;)V", (void*) android_server_SystemServer_init1 },

};

 

定义了上述数组后,实际的注册是通过以下函数实现的:

int register_android_server_SystemServer(JNIEnv* env)

{

    return jniRegisterNativeMethods(env, "com/android/server/SystemServer",

            gMethods, NELEM(gMethods));

}

     在sourceInsight中代码可以通过搜索register_android_server_SystemServer函数,发现该函数在Onload.cpp文件中JNI_OnLoad被调用。对于JNI_OnLoad函数,前面提到了加载JNI动态库后,JNI紧接会查找该库中是否存在JNI_OnLoad函数,如果有的话,就直接调用,调用成功后也就意味动态注册的工作完成了。

    前面我们也提到了systemserver负责启动系统中的各种关键服务,对于系统各种server不可避免的会调用底层接口,底层接口一般都是C或者C++函数写的,对于JAVA层而言就是通过调用native函数通过JNI技术与底层进行交互。因此我们会在Onload.cpp文件中JNI_OnLoad中看到一系列JNI动态注册的接口。本文的目的是介绍消息相关的处理,因此我们关注一下register_android_server_InputManager(env);对于其他注册函数的分析其实想通的。

下面看一下register_android_server_InputManager具体的函数声明。

int res = jniRegisterNativeMethods(env, "com/android/server/input/InputManagerService",

            gInputManagerMethods, NELEM(gInputManagerMethods));

jniRegisterNativeMethods函数完成java标准的native函数的映射工作。上面函数参数中env:指向JNIEnv结构体;第二个函数classname:说明java层对应的类名,对应类名“.”在C++中有特殊的定义,因此用“/”代替(可以换作说法该文件中定义的native函数就是在《上述类名.java》中调用,这样直接在工程中搜索该类名就比较方便找到对应的申明);第三个参数:为前面提到JNINativeMethod类型的指针,主要指向需要动态注册相关参数的数组名;第四个参数表明参数三锁对应的数组中元素的个数。

FIND_CLASS(clazz, "com/android/server/input/InputManagerService");

........

 gMotionEventClassInfo.clazz = jclass(env->NewGlobalRef(gMotionEventClassInfo.clazz));

上述代码主要是初始化变量gServiceClassInfo,gInputDeviceClassInfo,gKeyEventClassInfo和gMotionEventClassInfo。个人认为定义上述变量初始化,主要是从JNI层中调用JAVA层的函数速度优化考虑的,这个可以看一下具体的逻辑代码。在JNI中调用java层的函数,一般是通过env(env可以通过JVAAVM对象获取)获取对应类,获取对应类后创建对应的类对象,然后通过类对象找到对应的方法或者成员变量(对应类中的如果是static方法或者成员变量,是不需要创建对应的类对象的;并且对应是否为静态方法或者静态变量,对应JNI调用的方法也不相同)。

对应JNI相关的初始化到此就基本OK了,对于底层如何通过JNI调用JAVA层的函数会根据具体的代码在做分析。

 

1.3 java层Server相关初始化

 

     前面提到systemserver中的main方法核心逻辑入口,main里面通过调用init1方法,该方法对应的JNI的函数声明中会调用init2()方法,在init2方法中会启动一个线程ServerThread,在ServerThread的run函数中我们看一下如下代码:

Slog.i(TAG, "Input Manager");

inputManager = new InputManagerService(context, wmHandler);

Slog.i(TAG, "Window Manager");

wm = WindowManagerService.main(context, power, display, inputManager,

                    uiHandler, wmHandler,

                    factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,

                    !firstBoot, onlyCore);

ServiceManager.addService(Context.WINDOW_SERVICE, wm);

 ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

ActivityManagerService.self().setWindowManager(wm);

inputManager.setWindowManagerCallbacks(wm.getInputMonitor());

inputManager.start();

 

1.3.1 InputManagerService的初始化

 

首先看一下InputManagerService对应的构造函数如下:

 public InputManagerService(Context context, Handler handler) {

        this.mContext = context;

        this.mHandler = new InputManagerHandler(handler.getLooper());

 

        mUseDevInputEventForAudioJack =

                context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);

        Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="

                + mUseDevInputEventForAudioJack);

        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());

}

重点关注nativeInit函数,该函数为native函数,对应的函数在JNI的定义如下:

static jint nativeInit(JNIEnv* env, jclass clazz,

        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {

    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);

    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,

            messageQueue->getLooper());

    im->incStrong(serviceObj);

    return reinterpret_cast<jint>(im);//C++中指针强制类型转化

}

   在Java中如果想查找对应的JNI声明函数,比较快的方法如下:在SourceInsight中直接搜索java文件对应的文件名,然后在收到的结果中直接高亮jniRegisterNativeMethods函数,该函数对应的*.cpp为对应的JNI中的申明。

 

nativeInit函数中会调用NativeInputManager的构造函数如下

NativeInputManager::NativeInputManager(jobject contextObj,

        jobject serviceObj, const sp<Looper>& looper) :

        mLooper(looper) {

   .......

    sp<EventHub> eventHub = new EventHub();

    mInputManager = new InputManager(eventHub, this, this);

}

函数中会创建EventHub的对象如下:

 sp<EventHub> eventHub = new EventHub();

EventHub:是系统中所有事件的中央处理站。它管理所有系统中可以识别的输入设备的输入事件,此外,当设备增加或删除时,EventHub将产生相应的输入事件给系统。EventHub通过getEvents函数,给系统提供一个输入事件流。它也支持查询输入设备当前的状态(如哪些键当前被按下)。而且EventHub还跟踪每个输入调入的能力,比如输入设备的类别,输入设备支持哪些按键。 

 mInputManager = new InputManager(eventHub, this, this);

    首先我们先关注一下InputManager(C++)所在的文件夹与NativeInputManager(JNI中)是不相同的,逻辑上个人认为InputManager中相关的是与底层驱动相关了,上层JAVA应该是看不到InputManager相关的接口的。

 

InputManager::InputManager(

        const sp<EventHubInterface>& eventHub,

        const sp<InputReaderPolicyInterface>& readerPolicy,

        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {

        //readerPolicy &&dispatcherPolicy 实质为NativeInputManager对象

    mDispatcher = new InputDispatcher(dispatcherPolicy);

    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);

    initialize();

}

      这里主要是创建了一个InputDispatcher对象和一个InputReader对象,并且分别保存在成员变量mDispatcher和mReader中。InputDispatcher类是负责把键盘消息分发给当前激活的Activity窗口的,而InputReader类则是通过EventHub类来实现读取键盘事件的创建了这两个对象后,还要调用initialize函数来执行其它的初始化操作

 

void InputManager::initialize() {

    mReaderThread = new InputReaderThread(mReader);

    mDispatcherThread = new InputDispatcherThread(mDispatcher);

}

 

这个函数创建了一个InputReaderThread线程实例和一个InputDispatcherThread线程实例,并且分别保存在成员变量mReaderThread和mDispatcherThread中。这里的InputReader实列mReader就是通过这里的InputReaderThread线程实列mReaderThread来读取键盘事件的,而InputDispatcher实例mDispatcher则是通过这里的InputDispatcherThread线程实例mDisptacherThread来分发键盘消息的。

 

    InputManagerC++)的初始化基本完成, InputManager是系统事件处理的核心,通过上述代码分析可以知道其有2个线程如下

    1)InputReaderThread叫做"InputReader"线程,它负责读取并预处理RawEvent,applies policy并且把消息送入DispatcherThead管理的队列中。

     2)InputDispatcherThread叫做"InputDispatcher"线程,它在队列上等待新的输入事件,并且异步地把这些事件分发给应用程序。

     InputReaderThread类与InputDispatcherThread类不共享内部状态,所有的通信都是单向的,从InputReaderThread到InputDispatcherThread。两个类可以通过InputDispatchPolicy进行交互。///???

     InputManager类从不与Java交互,而InputDispatchPolicy负责执行所有与系统的外部交互,包括调用DVM业务。

上面InputManager2个线程只是创建了,实际还没有正式(thread只有调用Start)

0 0
原创粉丝点击