Android 4.0中按键的处理流程
按键在Android系统中,有着不同的代表意义。以前的全键盘的手机代码没有阅读过,所以也不是很了解。本人介绍的是在触摸屏的手机上的按键消息的处理流程。
在现在触摸屏成为主流的输入设备的情况下,很多厂商都在努力的做到取消物理按键的工作,但是目前就本人的学习情况来看,完全取消在目前看来还是不是那么现实。
有如下几点原因:
首先,本人说明的是目前原生的Android系统上。
其次,Android系统为了节省电量,在电源管理的过程中设置了休眠的方式。而休眠的时候触摸屏同样进入休眠状态。因此,不能够接收到用户的输入消息。
再次,目前的物理按键(主要指power,volume。home)是通过电源管理芯片进行控制的。触摸屏不是。
因此,如果没有现在的物理按键的情况下,如果想把设备从休眠的状态下唤醒基本上来讲是不可能的。
下面来正式的记录下本人在学习的过程中记录的点点滴滴。
首先,简要的介绍一下按键的处理流程。先简单的分为两大类:一类是虚拟按键。另一类是物理按键。
无论是虚拟按键还是物理按键都是要经过驱动层注册为输入设备,然后上报到kernel/drivers/input/input.c中。这里有相关函数的定义。然后通过、sys上报到frameworks/services/input/EventHub.cpp中,在这里会对设备进行扫描并且判断是哪种设备,然后在InputReader.cpp中对原始数据进行读取。在framewoks/services/input/InputDispatcher.cpp中实现数据的派发。在framework/base/core/jni/android_view_KeyEvent.cpp中实现通过JNI机制向上层的KeyEvent.java提供数据。并且在frameworks/base/core/java/android/view/KeyEvent.java中向上层的APP开发人员提供接口。
当然,虚拟键盘中有一个映射关系,键盘的按键值也会上报给上面的应用层,而对于物理按键往往是在frameworks层就被截取并且加以处理了。
普通的按键事件在Android系统中的调用流程(本人不太会处理visio绘图的保存问题)大致如下:
下图是人家goole的图 是比俺画的好啊……
但是对于物理按键的处理流程,目前主要查阅的代码的结果是在PhoneWindowManager.java中进行截获并处理的。
Android 4.0按键事件以及电源管理流程分析
Android是集成了linux内核以及frameworks层的东西而形成为os,其中主要包含了三种语言的编程,主要是c、c++以及java。因此他们之间的通信问题就显得尤为突出。
JAVA与c的通讯主要是通过JNI机制进行的。为了提高效率,在上层都使用java进行编程。因此在阅读源代码的过程中,就需要区分给用户使用的文件,系统内部使用的文件,以及与驱动打交道的文件。
Android获取系统消息概述
1、获取原始的用户消息,包括按键、触摸屏、鼠标、轨迹球等各种输入设备的消息。
2、对原始消息进行一定的加工,使之转化为程序可以理解的消息。比如所有的按键消息都包括“按下、弹起”等原始消息,而对程序来讲可能只关心该按键被“按了一次”或者“长按”,因此需要把原始消息转换为程序可以理解的消息。
3、把转换后的消息发送到相应的用户窗口所在的进程。如果获取线程和用户线程同在一个进程空间中,则传递消息比较简单,但对于多进程系统来讲,消息获取线程和用户线程往往在不同的进程空间中,因此需要使用IPC机制把消息传递到用户窗口所在的线程中。
在接下来的几篇博文中将陆续写如下内容:
WindowManagerService处理消息的时机
上报和分发的消息的处理流程
AIDL简介
WindowManagerService处理消息的时机
目前对于用户的输入消息分析的文章大都是划分为两种类型,一种是key消息,另一种是motion消息。
对于motion消息,Android原生系统中对其处理都是直接上报的。WindowManagerService没有对其做过多的处理。而对于key消息,则会首先回调WmS中的Key消息处理函数,在WindowManagerService中不处理该消息时才把消息发往客户窗口中。在一般情况下,WindowManagerService中仅处理一些系统Key消息,比如“HOME”键、照相按键、声音按键等。
在WindowManagerService中注册服务通道时,调用了Java环境中的InputManager对象mInputManager,而该类的构造函数中创建了一个callbacks变量,然后再nativeInit(mCallbacks)进行了初始化。变量mCallbacks作为初始化的参数传递到native环境中的InputManager对象中,从而使得在InputDispatcher进行回调时首先回调到Java环境中的InputManager.callbacks子类中。比如Callbacks中包含了interceptKeyBeforeDispatching()函数,当InputDispatcher()接收到Key消息时,首先回调该函数,而该函数的内部代码中,又调用了WindowManagerService对象中的InputMonitor的同名函数。
AIDL简介
通常每个应用程序都在他自己的进程内运行,但有时需要在进程之间传递对象(IPC通信)。此时可以通过应用程序UI的方式写一个运行在不同进程中的service。在Android平台中,一个进程通常不能访问其他进程中的内存区域。所以它们需要把对象拆分成操作系统能理解的简单形式,以便伪装成对象跨边界访问。而要完成这些需要AIDL机制。
AIDL(Android接口描述语言)是一个IDL语言,它可以生成一段代码,可以是一个在Android设备上运行的两个进程使用内部通信进程进行交互。如果你想在一个进程中(例如在一个Activity中)访问另一个进程中(例如service)某个对象的方法,你就可以使用AIDL来生成这样的代码来伪装传递各种参数。
要使用AIDL,service需要以AIDL文件的方式提供服务接口,AIDL工具将生成一个相应的java接口,并且在生成的服务器接口中包含一个功能调用的stub()服务桩类。service的onBind方法会返回实现类的对象,之后你就可以使用它了。
AIDL文件
framework中包含的aidl是在frameworks/base/Android.mk中定义的。该文件定义了两处aidl文件列表。
第一处是给LOCAL_SRC_FILES变量中使用 “+=” 进行赋值,该变量将包含在framework.jar目标中的所有源文件,包括aidl文件和java文件。
第二处是给aidl_files变量使用“:=”赋值符号进行赋值,该变量仅仅包含android.jar目标中所有的aidl文件。
因此,当给Frameworks中添加新的aidl文件时,需要考虑文件是否要公开到SDK中。如果需要,则需要把该文件路径同时添加到以上两个变量中;如果不需要公开到SDK中,则只需要把文件路径添加到LOCAL_SRC_FILES变量中。
在完成这些操作后编译仍会出现问题,此时需要运行,make update-api命令,此时改变的文件是current.txt改变的内容如下:
+ public abstract interface IEneaService implements android.os.IInterface {
+ }
+
+ public static abstract class IEneaService.Stub extends android.os.Binder implements android.os.IEneaService {
+ ctor public IEneaService.Stub();
+ method public android.os.IBinder asBinder();
+ method public static android.os.IEneaService asInterface(android.os.IBinder);
+ method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
+ }
+
这时,如果希望在写的Activity中使用的情况下,需要首先获取服务,这个步骤是通过"ServiceManager.getService()"这个API实现。然后使用service handle来调用service暴露出来的函数。ITestService om = ITestService.Stub.asInterface(ServiceManager.getService("Test"));
下面是个简单的例子:
/* * HelloServer.java */ package com.Test.helloserver; import android.app.Activity; import android.os.Bundle; import android.os.ServiceManager; import android.os.ITestService; import android.util.Log; public class HelloServer extends Activity { private static final String DTAG = "HelloServer"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ITestService om = ITestService.Stub.asInterface(ServiceManager.getService("Test")); try { Log.d(DTAG, "Going to call service"); om.setValue(20); Log.d(DTAG, "Service called succesfully"); } catch (Exception e) { Log.d(DTAG, "FAILED to call service"); e.printStackTrace(); } } }
上报和分发消息的流程