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来分发键盘消息的。
InputManager(C++)的初始化基本完成, InputManager是系统事件处理的核心,通过上述代码分析可以知道其有2个线程如下
1)InputReaderThread叫做"InputReader"线程,它负责读取并预处理RawEvent,applies policy并且把消息送入DispatcherThead管理的队列中。
2)InputDispatcherThread叫做"InputDispatcher"线程,它在队列上等待新的输入事件,并且异步地把这些事件分发给应用程序。
InputReaderThread类与InputDispatcherThread类不共享内部状态,所有的通信都是单向的,从InputReaderThread到InputDispatcherThread。两个类可以通过InputDispatchPolicy进行交互。///???
InputManager类从不与Java交互,而InputDispatchPolicy负责执行所有与系统的外部交互,包括调用DVM业务。
上面InputManager中2个线程只是创建了,实际还没有正式(thread只有调用Start)
- Android平台按键消息处理流程一(Android4.2.2)
- android4.3 按键消息处理分析
- android4.3 按键消息处理分析
- Android按键消息处理
- Android按键消息处理
- Android 按键消息处理
- Android 按键消息处理
- Android 按键消息处理
- Android 按键消息处理
- Android 按键消息处理
- Android输入法框架中按键消息的处理流程
- android 按键处理流程
- Android按键消息传播流程
- Android按键消息传播流程
- Android按键消息传播流程
- Android按键事件处理流程
- Android Framework层Power键关机流程(一,Power长按键操作处理)
- Android6.0 按键流程(六)应用进程处理按键消息
- hmap理解
- zend studio 配thinkphp框架学习笔记
- toad及pl/sql使用Oracle instant client访问数据库
- 当执行[object selector:para]的时候,发生了什么?
- bind1nd,not1,compose1等用法
- Android平台按键消息处理流程一(Android4.2.2)
- .NET Micro Framework-百度百科上的介绍
- 数据结构上机测试1:顺序表的应用
- mysql模块学习:python操作MySQL数据库
- 创新人才的价值
- Python网络爬虫3 ---- ubuntu下安装爬虫框架scrapy
- 【LeetCode】Linked List Cycle
- oracle dblink使用
- netduino plus2简介