在应用程序中使用传感器需要用到hardware包中的SensorManager、SensorListener等相关的类,具体的实现代码如下:
public class SensorActivity extends Activity {
private static final String TAG="SensorActivity";
SensorManager mySM;
SensorListener mySL=new SensorListener(){
public void onAccuracyChanged(int sensor, int accuracy) {
}
public void onSensorChanged(int sensor, float[] values) {
switch(sensor) {
case SensorManager.SENSOR_ACCELEROMETER:
Log.i(TAG,"Accelerometer: "
+ values[0] + ", "
+ values[1] + ", "
+ values[2]);
break;
}
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mySM=(SensorManager)getSystemService(SENSOR_SERVICE);
}
protected void onPause() {
mySM.unregisterListener(mySL);
super.onPause();
}
protected void onResume() {
mySM.registerListener(mySL,SensorManager.SENSOR_ACCELEROMETER,
SensorManager.SENSOR_DELAY_UI);
super.onResume();
}
}
这段代码特别简单,利用了大家熟悉的观察者模式对传感器数据进行监听处理,接下来,我们就分析这段代码背后的机制,看看Android为我们做了什么,才把貌似复杂的传感器处理简化到了这样几行代码就可以实现的程度。
对应用程序来说,最重要的就是把SensorListener注册到SensorManager上,从而才能以观察者身份接收到数据的变化,因此,我们把目光落在SensorManager的构造函数、RegisterListener函数和通知机制相关的代码上。
先来看构造函数的代码:
public SensorManager(Looper mainLooper) {
mSensorService = ISensorService.Stub.asInterface(
ServiceManager.getService(Context.SENSOR_SERVICE));
……
// initialize the sensor list
sensors_module_init();
final ArrayList<Sensor> fullList = sFullSensorsList;
int i = 0;
do {
Sensor sensor = new Sensor();
i = sensors_module_get_next_sensor(sensor, i);
if (i>=0) {
sensor.setLegacyType(getLegacySensorType(sensor.getType()));
fullList.add(sensor);
sHandleToSensor.append(sensor.getHandle(), sensor);
}
} while (i>0);
……
sSensorThread = new SensorThread();
}
在这段代码中我们关注三件事,一是利用ISensorService接口获取了对SensorService的引用;二是利用sensors_module_init()等函数完成了传感器列表的初始化;三是创建了一个SensorThread线程对象。
在此处,并没有启动SensorThread线程,也没有看到明显的操作传感器的动作,接下来,再分析一下registerListener()函数的代码:
public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate,
Handler handler) {
……
l = new ListenerDelegate(listener, sensor, handler);
sListeners.add(l);
if (!sListeners.isEmpty()) {
result = sSensorThread.startLocked(mSensorService);
if (result) {
result = mSensorService.enableSensor(l, name, handle, delay);
}
}
……
}
在这段代码中值得注意的是SensorThread线程被启动,通过SensorService的EnableSensor方法传感器将被打开进入工作状态。
再来看一下SensorThread的代码,在它的构造函数中调用了sensors_data_init()函数,在startLocked()函数中启动了线程,在线程run()函数中调用了sensors_data_open()函数,然后
while (true) {
// wait for an event
final int sensor = sensors_data_poll(values, status, timestamp);
final Sensor sensorObject = sHandleToSensor.get(sensor);
if (sensorObject != null) {
// report the sensor event to all listeners that
// care about it.
final int size = sListeners.size();
for (int i=0 ; i<size ; i++) {
ListenerDelegate listener = sListeners.get(i);
if (listener.hasSensor(sensorObject)) {
// this is asynchronous (okay to call
// with sListeners lock held).
listener.onSensorChangedLocked(sensorObject,
values, timestamp, accuracy);
}
}
}
}
即通过sensors_data_poll()函数等待硬件传感器的事件,调用listener.onSensorChangedLocked()函数,在该函数中调用了SensorListener中的onSensorChanged(),即调到了应用程序的观察者接口。
至此,SensorManager中的主要逻辑已经理清了,还有两个部分,一是SensorService的逻辑还没有涉及;二是sensors_data_init()相关的一组函数做了什么还没涉及。先来看一下SensorService的相关部分。
SensorService代码中可以看到,主要做的就是对传感器的控制,包括初始化、开、关、激活、设置延迟参数、睡眠几个动作,用的函数是一组类似_sensors_control_init()的函数,比如前文提到的enableSensor()的代码是:
if (_sensors_control_activate(sensor, true) == false) {
Slog.w(TAG, "could not enable sensor " + sensor);
return false;
}
在JAVA的代码里面,对于前文提到的两组函数sensor_data_XXX()和_sensor_control_XXX()都是采用native的方式进行的JNI调用,接下来就透过JNI,来看相关的CPP代码。
在看CPP代码前,先来看一下Sensors.h文件,这个文件将被CPP包含,从文件里的信息来看,这个是HAL(硬件抽象层)的接口文件,里面提供了device的结构体封装及一些设备驱动应用提供的操作函数,注意这些函数将被传感器的驱动程序实现,在此不并心驱动的实现,只要知道驱动能提供这些函数供上层程序调用就可以了,这也就是HAL的作用,对不同的设备向上层提供统一的调用接口。
这个文件提供了对传感器数据部分的操作,实现了前文提到的一组sensor_data_XXX()函数,如果对驱动有点熟悉的话,可以看到
static jint
sensors_module_init(JNIEnv *env, jclass clazz)
{
int err = 0;
sensors_module_t const* module;
err = hw_get_module(SENSORS_HARDWARE_MODULE_ID, (const hw_module_t **)&module);
if (err == 0)
sSensorModule = (sensors_module_t*)module;
return err;
}
这个函数通过hw_get_module加载了驱动程序。
static jint
sensors_data_poll(JNIEnv *env, jclass clazz,
jfloatArray values, jintArray status, jlongArray timestamp)
{
sensors_data_t data;
int res = sSensorDevice->poll(sSensorDevice, &data);
if (res >= 0) {
jint accuracy = data.vector.status;
env->SetFloatArrayRegion(values, 0, 3, data.vector.v);
env->SetIntArrayRegion(status, 0, 1, &accuracy);
env->SetLongArrayRegion(timestamp, 0, 1, &data.time);
}
return res;
}
这个函数通过sSensorDevice->poll()函数等待设备的数据到来,并将数据通过JNI规则传递到上层的数组中去。
这里的代码与SensorManager.cpp类似,比如:
static jboolean
android_activate(JNIEnv *env, jclass clazz, jint sensor, jboolean activate)
{
int active = sSensorDevice->activate(sSensorDevice, sensor, activate);
return (active<0) ? false : true;
}
调用了sSensorDevice->activate()函数使能特定的传感器。其他函数就不一一列举了。
最后,就是驱动程序了,利用硬件手册,遵循驱动程序的框架,将sensors.h中的结构体的函数指针赋值即可。不是本文的关注点,就不再详细解析了。
1. 通过上述对源码的分析过程,可以对应到Android架构图的各个层次,是一次上下贯通的过程,参照架构图可以看到:
SensorActivity.java对应的是Application层,是应用程序。
SensorManager.java,SensorListener.java对应的是Frameworks层,是Android提供的应用程序开发接口,应用程序框架。与应用程序的调用是通过类实例化或类继承进行的。
SensorManager.cpp,SensorService.cpp对应的是Libraries层,是Android提供的底层库,与Frameworks的调用是通过JNI实现的跨语言调用。
Sensors.h是HAL层,即硬件抽象层,这里提供了Android独立于具体硬件的抽象接口。
Sensors.c是Linux Kernel层,是具体的硬件设备驱动程序。
2. 通过上述分析,可以进一步了解Android系统的Binder接口机制。这里使用了简化的IBinder接口,可能是原设计人员考虑到了这部分相对独立且简单,所以没有使用此前关于IPC机制学习中用到的Proxy模式,而只是用ISensorService.Stub接口直接使用了内核服务,但与前文了解到的IPC机制不冲突。
——欢迎转载,转载请注明出处,谢谢——