SensorActivity.java
在應用程序中使用傳感器需要用到 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 爲我們做了什麼,才把貌似複雜的傳感器處理簡化到了這樣幾行代碼就可以實現的程度。
SensorManager.java
對應用程序來說,最重要的就是把 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.java
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 代碼。
Sensors.h
在看 CPP 代碼前,先來看一下 Sensors.h 文件,這個文件將被 CPP 包含,從文件裏的信息來看,這個是 HAL (硬件抽象層)的接口文件,裏面提供了 device 的結構體封裝及一些設備驅動應用提供的操作函數,注意這些函數將被傳感器的驅動程序實現,在此不併心驅動的實現,只要知道驅動能提供這些函數供上層程序調用就可以了,這也就是 HAL 的作用,對不同的設備向上層提供統一的調用接口。
SensorManager.cpp
這個文件提供了對傳感器數據部分的操作,實現了前文提到的一組 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 規則傳遞到上層的數組中去。
SensorService.cpp
這裏的代碼與 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.c
最後,就是驅動程序了,利用硬件手冊,遵循驅動程序的框架,將 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 機制不衝突。
——歡迎轉載,轉載請註明出處,謝謝——