- 浏览: 359250 次
- 性别:
- 来自: 福州
文章分类
最新评论
-
loveskey:
找了好久,可算是找到了。感谢
dx.jar dx.bat -
zhaoyi168:
可以把工程的代码发给我吗?
ZJLN1982@yahoo.co ...
Athrun Demo -
ergodic09:
請問樓主 我目前在porting AR6003但是無法自己產生 ...
009-Android平台开发-WIFI function porting-WIFI功能移植 -
iedj99fei:
...
androi中xliff:g
下面是一篇关于Android传感器源码总结的文章,讲解得很详细,从Framework到HAL的接口,估计作者没有对sensors的HAL进行研究或者其他原因而没有对HAL更多说明。本人最近项目中有这个模块,先收藏下,以学习!
(原文)虽然这篇文章写得很差,因为赶时间,所以就匆匆忙忙地写出来自己作一个笔记。但是我想对大家应该有一点帮助。
1、有关sensor在Java应用程序的编程(以注册多个传感器为例,这程序是我临时弄出来的,可能有错)
package com.sensors.acc;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.hardware.SensorManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
public class acc extends Activity {
float x, y, z;
SensorManager sensormanager = null;
Sensor accSensor = null;
Sensor lightSensor = null;
Sensor proximitySensor = null;
TextView accTextView = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
sensormanager = (SensorManager)getSystemService(SENSOR_SERVICE);
accSensor = sensormanager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
lightSensor = sensormanager.getDefaultSensor(Sensor.TYPE_LIGHT);
proximitySensor = sensormanager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
accTextView = (TextView)findViewById(R.id.textview_name);
}
SensorEventListener lsn = new SensorEventListener() {
public void onSensorChanged(SensorEvent e) {
if (e.sensor == accSensor) {
Log.d("sensor", "found acc sensor");
x = e.values[SensorManager.DATA_X];
y = e.values[SensorManager.DATA_Y];
z = e.values[SensorManager.DATA_Z];
accTextView.setText("x = " + x + ", /ny = " + y + ", /nz = " + z);
}
else if (e.sensor == lightSensor) {
Log.d("sensor", "found light sensor");
accTextView.setText("data is " + e.values[0]);
}
else if (e.sensor == proximitySensor) {
Log.d("sensor", "found proximity sensor");
accTextView.setText("distance is " + e.values[0]);
}
// Log.d("sensor", "found acc sensor");
// Log.d("sensor", "x = " + x + ", y = " + y + ", z = " + z);
// accTextView.setText("x = " + x + ", /ny = " + y + ", /nz = " + z);
}
public void onAccuracyChanged(Sensor s, int accuracy) {
}
};
@Override
protected void onResume() {
super.onResume(); // register this class as a listener for the orientation and accelerometer sensors
sensormanager.registerListener(lsn, accSensor, SensorManager.SENSOR_DELAY_NORMAL);
sensormanager.registerListener(lsn, lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
sensormanager.registerListener(lsn, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
// sensormanager.unregisterListener(lsn);
}
@Override
protected void onStop() { // unregister listener
sensormanager.unregisterListener(lsn, accSensor);
sensormanager.unregisterListener(lsn, lightSensor);
sensormanager.unregisterListener(lsn, proximitySensor);
super.onStop();
}
}
在onCreate()函数中,调用getSystemService(SENSOR_SERVICE)初始化一个SensorManager实例,为什么要用getSystemService函数,而不直接用new SensorManager呢?我们看此函数的实现,在ApplicationContext.java中,
if (SENSOR_SERVICE.equals(name)) {
return getSensorManager();
然后getSensorManager()的实现
private SensorManager getSensorManager() {
synchronized (mSync) {
if (mSensorManager == null) {
mSensorManager = new SensorManager(mMainThread.getHandler().getLooper());
}
}
return mSensorManager;
}
看到没有?初始化SensorManager的时候需要mMainThread.getHandler().getLooper()这个参数,之个应该是用来传递消息用的,在SensorManager类的构造函数中会把此参数传给类成员mMainLooper。如果用new SensorManager()就需要另外获取mainLooper参数传递进去。
2、在android中跟sensor有关的一些文件有SensorManager.java,位于frameworks/base/core/java/android/hardware目录下,SensorService.java,位于frameworks/base/services/java/com/android/server目录下,android_hardware_SensorManager.cpp,位于frameworks/base/core/jni/目录下,与SensorManager.java相对应,com_android_server_SensorService.cpp,在frameworks/base/services/jni/目录下,与SensorService.java相对应。还有SystemServer.java文件,Hardware/Libhardware/Include/Hardware目录下的Sensor.h头文件。另外我们需要根据Sensor.h实现自己的一个源文件,一般取名为sensors_xxx.c或者sensors_xxx.cpp。
3、SensorManager类分析
有几个函数比较重要,必须清晰理解它们的实现,才能了解整个传感器系统的实现。从而更好地去实现硬件抽象层的实现。几个比较重要的函数有构造函数SensorManager(), registerListener()和unregisterListener(),其中registerListener()和unregisterListener()有多个,标志为 @Deprecated的是过时的,就不要看了。
(1)构造函数SensorManager(Looper mainLooper)
这个函数首先获取得传感器系统服务,并赋给类成员mSensorService,
mSensorService = ISensorService.Stub.asInterface(
ServiceManager.getService(Context.SENSOR_SERVICE));
这里我要说一句,就是关于这个传感器系统服务,很多书上都说用getSystemService()是获得传感器的系统服务,而它返回的是SensorManager类型,所以以为整个系统都是使用同一个SensorManager类的实例,以为我们在任何地方使用的SensorManager实例都是同一个,它们的公共成员是共享的。但是经过这两天的分析,这种说法是错误的。其实每次调用getSystemService()函数时都初始化一个新的SensorManager实例,而这个SensorManager实例会在构造函数里通过取得传感器系统服务SensorService来实现对下层传感器的一些控制。而这个SensorService才是系统的传感器服务,说服务,不如说它只是SensorService类的一个实例罢了。它只在系统初始化时初始化一次。Android中的系统服务机制应该跟传感器的都差不多一个样,都是由不同的Manager调用下层相同的Service。你可以列举其它的Manager。那它是什么时候初始化呢?它是系统初始化在SystemServer进程里创建的,SystemServer是一个管理很多系统服务的进程,我们转到SystemServer.的main函数里,可以看到一直到调用int2()函数,它会创建一个ServerThread,最终调用AdbSettingsObserver类的run()函数,在run()函数里有这么有一句
// Sensor Service is needed by Window Manager, so this goes first
Log.i(TAG, "Sensor Service");
ServiceManager.addService(Context.SENSOR_SERVICE, new SensorService(context));
这里就创建SensorService实例了。在创建这个实例时会在SensorService构造函数中调用jni函数 public SensorService(Context context) {
if (localLOGV) Log.d(TAG, "SensorService startup");
_sensors_control_init();
}
我们看_sensors_control_init();对应的为
static jint
android_init(JNIEnv *env, jclass clazz)
{
sensors_module_t* module;
if (hw_get_module(SENSORS_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) {
if (sensors_control_open(&module->common, &sSensorDevice) == 0) {
const struct sensor_t* list;
int count = module->get_sensors_list(module, &list);
return count;
}
}
return 0;
}
它主要调用了sensor.h中的sensors_control_open()
static inline int sensors_control_open(const struct hw_module_t* module,
struct sensors_control_device_t** device) {
return module->methods->open(module,
SENSORS_HARDWARE_CONTROL, (struct hw_device_t**)device);
}
之后在系统任何地方使用的都是这个SensorService实例。最后run()函数调用Looper.loop();就进行消息循环等待了,这就是SystemServer进程的消息服务了。这才真正叫做系统服务嘛。
我们继续看SensorManager类的构造函数,取得SensorService后,
nativeClassInit();
这是一个jni函数,SensorManager类调用的jni函数都在com_android_server_SensorService.cpp里,我们看这函数
static void
nativeClassInit (JNIEnv *_env, jclass _this)
{
jclass sensorClass = _env->FindClass("android/hardware/Sensor");
SensorOffsets& sensorOffsets = gSensorOffsets;
sensorOffsets.name = _env->GetFieldID(sensorClass, "mName", "Ljava/lang/String;");
sensorOffsets.vendor = _env->GetFieldID(sensorClass, "mVendor", "Ljava/lang/String;");
sensorOffsets.version = _env->GetFieldID(sensorClass, "mVersion", "I");
sensorOffsets.handle = _env->GetFieldID(sensorClass, "mHandle", "I");
sensorOffsets.type = _env->GetFieldID(sensorClass, "mType", "I");
sensorOffsets.range = _env->GetFieldID(sensorClass, "mMaxRange", "F");
sensorOffsets.resolution = _env->GetFieldID(sensorClass, "mResolution","F");
sensorOffsets.power = _env->GetFieldID(sensorClass, "mPower", "F");
}
这个函数只是获取和设置一些信息吧,我们不关心。接着
sensors_module_init();
我们看这函数
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;
}
它获取了sensor的模块信息,并把它赋给sSensorModule全局变量,之后传的modules参数都为这个。
接着在构造函数里
final ArrayList<Sensor> fullList = sFullSensorsList;
int i = 0;
do {
Sensor sensor = new Sensor();
i = sensors_module_get_next_sensor(sensor, i);
if (i>=0) {
Log.d(TAG, "found sensor: " + sensor.getName() +
", handle=" + sensor.getHandle());
sensor.setLegacyType(getLegacySensorType(sensor.getType()));
fullList.add(sensor);
sHandleToSensor.append(sensor.getHandle(), sensor);
}
} while (i>0);
这里主要是通过jni函数sensors_module_get_next_sensor(sensor, i);获取传感器列表,并把它加入自己的fullList列表中。我们看sensors_module_get_next_sensor()函数
static jint
sensors_module_get_next_sensor(JNIEnv *env, jobject clazz, jobject sensor, jint next)
{
if (sSensorModule == NULL)
return 0;
SensorOffsets& sensorOffsets = gSensorOffsets;
const struct sensor_t* list;
int count = sSensorModule->get_sensors_list(sSensorModule, &list);
if (next >= count)
return -1;
list += next;
jstring name = env->NewStringUTF(list->name);
jstring vendor = env->NewStringUTF(list->vendor);
env->SetObjectField(sensor, sensorOffsets.name, name);
env->SetObjectField(sensor, sensorOffsets.vendor, vendor);
env->SetIntField(sensor, sensorOffsets.version, list->version);
env->SetIntField(sensor, sensorOffsets.handle, list->handle);
env->SetIntField(sensor, sensorOffsets.type, list->type);
env->SetFloatField(sensor, sensorOffsets.range, list->maxRange);
env->SetFloatField(sensor, sensorOffsets.resolution, list->resolution);
env->SetFloatField(sensor, sensorOffsets.power, list->power);
next++;
return next<count ? next : 0;
}
它主要是调用HAL层的get_sensors_list()函数取得传感器列表信息。
接着在sensorManger构造函数最后
sSensorThread = new SensorThread();
创建一个SensorThread()线程。但并未运行,但在SensorThread类的构造函数里会执行jni函数 sensors_data_init();
我们看此函数static jint
sensors_data_init(JNIEnv *env, jclass clazz)
{
if (sSensorModule == NULL)
return -1;
int err = sensors_data_open(&sSensorModule->common, &sSensorDevice);
return err;
}
它调用了HAL层的sensors_data_open函数,而这个函数位于sensor.h中,它调用的是
static inline int sensors_data_open(const struct hw_module_t* module,
struct sensors_data_device_t** device) {
return module->methods->open(module,
SENSORS_HARDWARE_DATA, (struct hw_device_t**)device);
}
Modules->methods->open函数。而在SensorThread类的析构函数finalize()里会调用
sensors_data_uninit();
static jint
sensors_data_uninit(JNIEnv *env, jclass clazz)
{
int err = 0;
if (sSensorDevice) {
err = sensors_data_close(sSensorDevice);
if (err == 0)
sSensorDevice = 0;
}
return err;
}
在sensor.h里
static inline int sensors_data_close(struct sensors_data_device_t* device) {
return device->common.close(&device->common);
}
那什么时候sSensorThread线程会运行呢?我们在下面看registerListener()函数。
(2) public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) {
return registerListener(listener, sensor, rate, null);
}
它调用的是 public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate,
Handler handler)
在这函数中,先验证rate,然后检测注册的listener在不在本类的sListeners列表中。
for (ListenerDelegate i : sListeners) {
if (i.getListener() == listener) {
l = i;
break;
}
}
如果不在就申请一个listener,并把它加入全局列表sListener中,并调用mSensorService的enableSensor()函数使能传感器,这个enableSensor()函数最终会调用HAL层的active函数和set_delay()函数,使用后然后判断sListener列表是否为空,当然,第一次为空时加入一个新的listener就不为空了,此时就执行sSensorThread的startLocked运行sSensorThread线程了
l = new ListenerDelegate(listener, sensor, handler);
result = mSensorService.enableSensor(l, name, handle, delay);
if (result) {
sListeners.add(l);
sListeners.notify();
}
if (!sListeners.isEmpty()) {
sSensorThread.startLocked(mSensorService);
}
另一方面,如果注册的listener在sListeners列表中,则先调用mSensorService的enableSensor()函数使能传感器,然后把注册的传感器加入到已存在的listener中。
result = mSensorService.enableSensor(l, name, handle, delay);
if (result) {
l.addSensor(sensor);
}
接下来我们看看startLocked函数,它在SensorThread中,
void startLocked(ISensorService service) {
try {
if (mThread == null) {
Bundle dataChannel = service.getDataChannel();
mThread = new Thread(new SensorThreadRunnable(dataChannel),
SensorThread.class.getName());
mThread.start();
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in startLocked: ", e);
}
}
第一次mThread为null,然后它调用了service.getDataChannel()函数,此函数在SensorService类中,主要调用了jni函数_sensors_control_open(),
public Bundle getDataChannel() throws RemoteException {
// synchronize so we do not require sensor HAL to be thread-safe.
synchronized(mListeners) {
return _sensors_control_open();
}
}
SensorService类中调用的jni函数主要都在com_android_server_SensorService.cpp文件 中,我们看一下这个函数
static jobject
android_open(JNIEnv *env, jclass clazz)
{
native_handle_t* handle = sSensorDevice->open_data_source(sSensorDevice);
if (!handle) {
return NULL;
}
// new Bundle()
jobject bundle = env->NewObject(
gBundleOffsets.mClass,
gBundleOffsets.mConstructor);
if (handle->numFds > 0) {
jobjectArray fdArray = env->NewObjectArray(handle->numFds,
gParcelFileDescriptorOffsets.mClass, NULL);
for (int i = 0; i < handle->numFds; i++) {
// new FileDescriptor()
jobject fd = env->NewObject(gFileDescriptorOffsets.mClass,
gFileDescriptorOffsets.mConstructor);
env->SetIntField(fd, gFileDescriptorOffsets.mDescriptor, handle->data[i]);
// new ParcelFileDescriptor()
jobject pfd = env->NewObject(gParcelFileDescriptorOffsets.mClass,
gParcelFileDescriptorOffsets.mConstructor, fd);
env->SetObjectArrayElement(fdArray, i, pfd);
}
// bundle.putParcelableArray("fds", fdArray);
env->CallVoidMethod(bundle, gBundleOffsets.mPutParcelableArray,
env->NewStringUTF("fds"), fdArray);
}
if (handle->numInts > 0) {
jintArray intArray = env->NewIntArray(handle->numInts);
env->SetIntArrayRegion(intArray, 0, handle->numInts, &handle->data[handle->numInts]);
// bundle.putIntArray("ints", intArray);
env->CallVoidMethod(bundle, gBundleOffsets.mPutIntArray,
env->NewStringUTF("ints"), intArray);
}
// delete the file handle, but don't close any file descriptors
native_handle_delete(handle);
return bundle;
}
它主要调用了HAL层的open_data_source()函数。取得一些文件描述符等信息。
接下来SensorThread创建一个线程,调用start()就进入SensorThreadRunnable类的run()函数了,所以我们接着去看run()函数,它首先调用open()函数
if (!open()) {
return;
}
在open()函数中调用了 jni函数sensors_data_open(fds, ints);
static jint
sensors_data_open(JNIEnv *env, jclass clazz, jobjectArray fdArray, jintArray intArray)
{
jclass FileDescriptor = env->FindClass("java/io/FileDescriptor");
jfieldID fieldOffset = env->GetFieldID(FileDescriptor, "descriptor", "I");
int numFds = (fdArray ? env->GetArrayLength(fdArray) : 0);
int numInts = (intArray ? env->GetArrayLength(intArray) : 0);
native_handle_t* handle = native_handle_create(numFds, numInts);
int offset = 0;
for (int i = 0; i < numFds; i++) {
jobject fdo = env->GetObjectArrayElement(fdArray, i);
if (fdo) {
handle->data[offset++] = env->GetIntField(fdo, fieldOffset);
} else {
handle->data[offset++] = -1;
}
}
if (numInts > 0) {
jint* ints = env->GetIntArrayElements(intArray, 0);
for (int i = 0; i < numInts; i++) {
handle->data[offset++] = ints[i];
}
env->ReleaseIntArrayElements(intArray, ints, 0);
}
// doesn't take ownership of the native handle
return sSensorDevice->data_open(sSensorDevice, handle);
}
这函数最终调用了HAL层的data_open(),之后run()函数就进入一个while循环了。
while (true) {
// wait for an event
final int sensor = sensors_data_poll(values, status, timestamp);
int accuracy = status[0];
synchronized (sListeners) {
if (sensor == -1 || sListeners.isEmpty()) {
if (sensor == -1) {
// we lost the connection to the event stream. this happens
// when the last listener is removed.
Log.d(TAG, "_sensors_data_poll() failed, we bail out.");
}
// we have no more listeners or polling failed, terminate the thread
sensors_data_close();
mThread = null;
break;
}
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);
}
}
}
}
它调用了jni函数sensors_data_poll()一直读数据。
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;
}
把传感器得到的值都放在value数组中,根据返回的传感器标志sensor,把它分派给在sListener列表中所有的listener,如果listener中有监听这个sensor,就把它分派给这个listener,此时就会引起onSensorChange()了。
好了,获取传感器数据主要是这样一个途径。最后我们去分析一下unregisterListener()函数。
private void unregisterListener(Object listener) {
if (listener == null) {
return;
}
try {
synchronized (sListeners) {
final int size = sListeners.size();
for (int i=0 ; i<size ; i++) {
ListenerDelegate l = sListeners.get(i);
if (l.getListener() == listener) {
// disable all sensors for this listener
for (Sensor sensor : l.getSensors()) {
String name = sensor.getName();
int handle = sensor.getHandle();
mSensorService.enableSensor(l, name, handle, SENSOR_DISABLE);
}
sListeners.remove(i);
break;
}
}
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in unregisterListener: ", e);
}
}
不用想这个函数会做一些与registerListener相反的事情,至少差不多。它先在sListeners列表中找到这个listener,然后先调用enableSensor()函数禁止这个传感器。我们跟踪一下这函数,在SensorService类中。
synchronized(mListeners) {
if (enable!=SENSOR_DISABLE && !_sensors_control_activate(sensor, true)) {
Log.w(TAG, "could not enable sensor " + sensor);
return false;
}
Listener l = null;
int minDelay = enable;
for (Listener listener : mListeners) {
if (binder == listener.mToken) {
l = listener;
}
if (minDelay > listener.mDelay)
minDelay = listener.mDelay;
}
if (l == null && enable!=SENSOR_DISABLE) {
l = new Listener(binder);
binder.linkToDeath(l, 0);
mListeners.add(l);
mListeners.notify();
}
if (l == null) {
// by construction, this means we're disabling a listener we
// don't know about...
Log.w(TAG, "listener with binder " + binder +
", doesn't exist (sensor=" + name + ", id=" + sensor + ")");
return false;
}
if (minDelay >= 0) {
_sensors_control_set_delay(minDelay);
}
if (enable != SENSOR_DISABLE) {
l.addSensor(sensor, enable);
} else {
l.removeSensor(sensor);
deactivateIfUnusedLocked(sensor);
if (l.mSensors == 0) {
mListeners.remove(l);
binder.unlinkToDeath(l, 0);
mListeners.notify();
}
}
if (mListeners.size() == 0) {
_sensors_control_wake();
_sensors_control_close();
}
}
return true;
你们看到它的实现了吧。如果enable是true的话,就调用_sensors_control_activate(),如果是false的话,就调用deactivateIfUnusedLocked(),它们最终都会调用 HAL层的active()函数。最后,如果是禁止传感器的话,如果mListeners为空了,它就会调用
_sensors_control_wake();
_sensors_control_close();
这两个jni函数,最终会调用HAL层的wake()和close_data_source()函数。当调用wake()函数时,会使SensorManager类线程的run()函数中的sensor_data_poll()函数立即返回,此时在run()函数中调用sensors_data_close();最终会调用HAL层的data_close()函数。至此,一个传感器从初始到结束的流程就分析完了。
所以在java使用一个传感器在HAL层具体调用的函数流程为:
首先,sensors_control_open(),只在系统初始化时调用一次。用来初始化control_device结构体。
以下的是每次使用传感器一般经过的流程,注意,是一般而已,有些并不执行
(1)sensors_data_open
(2)get_sensors_list
(3)activate
(4)set_delay
(5)open_data_source
(6)data_open
(7)poll
一直读数据。。。。。。。。。。。
退出时
(8)activate
(9)sensors_control_close
(10)data_close
原文:http://blog.csdn.net/JerryMo06/archive/2010/04/15/5487476.aspx
(原文)虽然这篇文章写得很差,因为赶时间,所以就匆匆忙忙地写出来自己作一个笔记。但是我想对大家应该有一点帮助。
1、有关sensor在Java应用程序的编程(以注册多个传感器为例,这程序是我临时弄出来的,可能有错)
package com.sensors.acc;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.hardware.SensorManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
public class acc extends Activity {
float x, y, z;
SensorManager sensormanager = null;
Sensor accSensor = null;
Sensor lightSensor = null;
Sensor proximitySensor = null;
TextView accTextView = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
sensormanager = (SensorManager)getSystemService(SENSOR_SERVICE);
accSensor = sensormanager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
lightSensor = sensormanager.getDefaultSensor(Sensor.TYPE_LIGHT);
proximitySensor = sensormanager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
accTextView = (TextView)findViewById(R.id.textview_name);
}
SensorEventListener lsn = new SensorEventListener() {
public void onSensorChanged(SensorEvent e) {
if (e.sensor == accSensor) {
Log.d("sensor", "found acc sensor");
x = e.values[SensorManager.DATA_X];
y = e.values[SensorManager.DATA_Y];
z = e.values[SensorManager.DATA_Z];
accTextView.setText("x = " + x + ", /ny = " + y + ", /nz = " + z);
}
else if (e.sensor == lightSensor) {
Log.d("sensor", "found light sensor");
accTextView.setText("data is " + e.values[0]);
}
else if (e.sensor == proximitySensor) {
Log.d("sensor", "found proximity sensor");
accTextView.setText("distance is " + e.values[0]);
}
// Log.d("sensor", "found acc sensor");
// Log.d("sensor", "x = " + x + ", y = " + y + ", z = " + z);
// accTextView.setText("x = " + x + ", /ny = " + y + ", /nz = " + z);
}
public void onAccuracyChanged(Sensor s, int accuracy) {
}
};
@Override
protected void onResume() {
super.onResume(); // register this class as a listener for the orientation and accelerometer sensors
sensormanager.registerListener(lsn, accSensor, SensorManager.SENSOR_DELAY_NORMAL);
sensormanager.registerListener(lsn, lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
sensormanager.registerListener(lsn, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
// sensormanager.unregisterListener(lsn);
}
@Override
protected void onStop() { // unregister listener
sensormanager.unregisterListener(lsn, accSensor);
sensormanager.unregisterListener(lsn, lightSensor);
sensormanager.unregisterListener(lsn, proximitySensor);
super.onStop();
}
}
在onCreate()函数中,调用getSystemService(SENSOR_SERVICE)初始化一个SensorManager实例,为什么要用getSystemService函数,而不直接用new SensorManager呢?我们看此函数的实现,在ApplicationContext.java中,
if (SENSOR_SERVICE.equals(name)) {
return getSensorManager();
然后getSensorManager()的实现
private SensorManager getSensorManager() {
synchronized (mSync) {
if (mSensorManager == null) {
mSensorManager = new SensorManager(mMainThread.getHandler().getLooper());
}
}
return mSensorManager;
}
看到没有?初始化SensorManager的时候需要mMainThread.getHandler().getLooper()这个参数,之个应该是用来传递消息用的,在SensorManager类的构造函数中会把此参数传给类成员mMainLooper。如果用new SensorManager()就需要另外获取mainLooper参数传递进去。
2、在android中跟sensor有关的一些文件有SensorManager.java,位于frameworks/base/core/java/android/hardware目录下,SensorService.java,位于frameworks/base/services/java/com/android/server目录下,android_hardware_SensorManager.cpp,位于frameworks/base/core/jni/目录下,与SensorManager.java相对应,com_android_server_SensorService.cpp,在frameworks/base/services/jni/目录下,与SensorService.java相对应。还有SystemServer.java文件,Hardware/Libhardware/Include/Hardware目录下的Sensor.h头文件。另外我们需要根据Sensor.h实现自己的一个源文件,一般取名为sensors_xxx.c或者sensors_xxx.cpp。
3、SensorManager类分析
有几个函数比较重要,必须清晰理解它们的实现,才能了解整个传感器系统的实现。从而更好地去实现硬件抽象层的实现。几个比较重要的函数有构造函数SensorManager(), registerListener()和unregisterListener(),其中registerListener()和unregisterListener()有多个,标志为 @Deprecated的是过时的,就不要看了。
(1)构造函数SensorManager(Looper mainLooper)
这个函数首先获取得传感器系统服务,并赋给类成员mSensorService,
mSensorService = ISensorService.Stub.asInterface(
ServiceManager.getService(Context.SENSOR_SERVICE));
这里我要说一句,就是关于这个传感器系统服务,很多书上都说用getSystemService()是获得传感器的系统服务,而它返回的是SensorManager类型,所以以为整个系统都是使用同一个SensorManager类的实例,以为我们在任何地方使用的SensorManager实例都是同一个,它们的公共成员是共享的。但是经过这两天的分析,这种说法是错误的。其实每次调用getSystemService()函数时都初始化一个新的SensorManager实例,而这个SensorManager实例会在构造函数里通过取得传感器系统服务SensorService来实现对下层传感器的一些控制。而这个SensorService才是系统的传感器服务,说服务,不如说它只是SensorService类的一个实例罢了。它只在系统初始化时初始化一次。Android中的系统服务机制应该跟传感器的都差不多一个样,都是由不同的Manager调用下层相同的Service。你可以列举其它的Manager。那它是什么时候初始化呢?它是系统初始化在SystemServer进程里创建的,SystemServer是一个管理很多系统服务的进程,我们转到SystemServer.的main函数里,可以看到一直到调用int2()函数,它会创建一个ServerThread,最终调用AdbSettingsObserver类的run()函数,在run()函数里有这么有一句
// Sensor Service is needed by Window Manager, so this goes first
Log.i(TAG, "Sensor Service");
ServiceManager.addService(Context.SENSOR_SERVICE, new SensorService(context));
这里就创建SensorService实例了。在创建这个实例时会在SensorService构造函数中调用jni函数 public SensorService(Context context) {
if (localLOGV) Log.d(TAG, "SensorService startup");
_sensors_control_init();
}
我们看_sensors_control_init();对应的为
static jint
android_init(JNIEnv *env, jclass clazz)
{
sensors_module_t* module;
if (hw_get_module(SENSORS_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) {
if (sensors_control_open(&module->common, &sSensorDevice) == 0) {
const struct sensor_t* list;
int count = module->get_sensors_list(module, &list);
return count;
}
}
return 0;
}
它主要调用了sensor.h中的sensors_control_open()
static inline int sensors_control_open(const struct hw_module_t* module,
struct sensors_control_device_t** device) {
return module->methods->open(module,
SENSORS_HARDWARE_CONTROL, (struct hw_device_t**)device);
}
之后在系统任何地方使用的都是这个SensorService实例。最后run()函数调用Looper.loop();就进行消息循环等待了,这就是SystemServer进程的消息服务了。这才真正叫做系统服务嘛。
我们继续看SensorManager类的构造函数,取得SensorService后,
nativeClassInit();
这是一个jni函数,SensorManager类调用的jni函数都在com_android_server_SensorService.cpp里,我们看这函数
static void
nativeClassInit (JNIEnv *_env, jclass _this)
{
jclass sensorClass = _env->FindClass("android/hardware/Sensor");
SensorOffsets& sensorOffsets = gSensorOffsets;
sensorOffsets.name = _env->GetFieldID(sensorClass, "mName", "Ljava/lang/String;");
sensorOffsets.vendor = _env->GetFieldID(sensorClass, "mVendor", "Ljava/lang/String;");
sensorOffsets.version = _env->GetFieldID(sensorClass, "mVersion", "I");
sensorOffsets.handle = _env->GetFieldID(sensorClass, "mHandle", "I");
sensorOffsets.type = _env->GetFieldID(sensorClass, "mType", "I");
sensorOffsets.range = _env->GetFieldID(sensorClass, "mMaxRange", "F");
sensorOffsets.resolution = _env->GetFieldID(sensorClass, "mResolution","F");
sensorOffsets.power = _env->GetFieldID(sensorClass, "mPower", "F");
}
这个函数只是获取和设置一些信息吧,我们不关心。接着
sensors_module_init();
我们看这函数
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;
}
它获取了sensor的模块信息,并把它赋给sSensorModule全局变量,之后传的modules参数都为这个。
接着在构造函数里
final ArrayList<Sensor> fullList = sFullSensorsList;
int i = 0;
do {
Sensor sensor = new Sensor();
i = sensors_module_get_next_sensor(sensor, i);
if (i>=0) {
Log.d(TAG, "found sensor: " + sensor.getName() +
", handle=" + sensor.getHandle());
sensor.setLegacyType(getLegacySensorType(sensor.getType()));
fullList.add(sensor);
sHandleToSensor.append(sensor.getHandle(), sensor);
}
} while (i>0);
这里主要是通过jni函数sensors_module_get_next_sensor(sensor, i);获取传感器列表,并把它加入自己的fullList列表中。我们看sensors_module_get_next_sensor()函数
static jint
sensors_module_get_next_sensor(JNIEnv *env, jobject clazz, jobject sensor, jint next)
{
if (sSensorModule == NULL)
return 0;
SensorOffsets& sensorOffsets = gSensorOffsets;
const struct sensor_t* list;
int count = sSensorModule->get_sensors_list(sSensorModule, &list);
if (next >= count)
return -1;
list += next;
jstring name = env->NewStringUTF(list->name);
jstring vendor = env->NewStringUTF(list->vendor);
env->SetObjectField(sensor, sensorOffsets.name, name);
env->SetObjectField(sensor, sensorOffsets.vendor, vendor);
env->SetIntField(sensor, sensorOffsets.version, list->version);
env->SetIntField(sensor, sensorOffsets.handle, list->handle);
env->SetIntField(sensor, sensorOffsets.type, list->type);
env->SetFloatField(sensor, sensorOffsets.range, list->maxRange);
env->SetFloatField(sensor, sensorOffsets.resolution, list->resolution);
env->SetFloatField(sensor, sensorOffsets.power, list->power);
next++;
return next<count ? next : 0;
}
它主要是调用HAL层的get_sensors_list()函数取得传感器列表信息。
接着在sensorManger构造函数最后
sSensorThread = new SensorThread();
创建一个SensorThread()线程。但并未运行,但在SensorThread类的构造函数里会执行jni函数 sensors_data_init();
我们看此函数static jint
sensors_data_init(JNIEnv *env, jclass clazz)
{
if (sSensorModule == NULL)
return -1;
int err = sensors_data_open(&sSensorModule->common, &sSensorDevice);
return err;
}
它调用了HAL层的sensors_data_open函数,而这个函数位于sensor.h中,它调用的是
static inline int sensors_data_open(const struct hw_module_t* module,
struct sensors_data_device_t** device) {
return module->methods->open(module,
SENSORS_HARDWARE_DATA, (struct hw_device_t**)device);
}
Modules->methods->open函数。而在SensorThread类的析构函数finalize()里会调用
sensors_data_uninit();
static jint
sensors_data_uninit(JNIEnv *env, jclass clazz)
{
int err = 0;
if (sSensorDevice) {
err = sensors_data_close(sSensorDevice);
if (err == 0)
sSensorDevice = 0;
}
return err;
}
在sensor.h里
static inline int sensors_data_close(struct sensors_data_device_t* device) {
return device->common.close(&device->common);
}
那什么时候sSensorThread线程会运行呢?我们在下面看registerListener()函数。
(2) public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) {
return registerListener(listener, sensor, rate, null);
}
它调用的是 public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate,
Handler handler)
在这函数中,先验证rate,然后检测注册的listener在不在本类的sListeners列表中。
for (ListenerDelegate i : sListeners) {
if (i.getListener() == listener) {
l = i;
break;
}
}
如果不在就申请一个listener,并把它加入全局列表sListener中,并调用mSensorService的enableSensor()函数使能传感器,这个enableSensor()函数最终会调用HAL层的active函数和set_delay()函数,使用后然后判断sListener列表是否为空,当然,第一次为空时加入一个新的listener就不为空了,此时就执行sSensorThread的startLocked运行sSensorThread线程了
l = new ListenerDelegate(listener, sensor, handler);
result = mSensorService.enableSensor(l, name, handle, delay);
if (result) {
sListeners.add(l);
sListeners.notify();
}
if (!sListeners.isEmpty()) {
sSensorThread.startLocked(mSensorService);
}
另一方面,如果注册的listener在sListeners列表中,则先调用mSensorService的enableSensor()函数使能传感器,然后把注册的传感器加入到已存在的listener中。
result = mSensorService.enableSensor(l, name, handle, delay);
if (result) {
l.addSensor(sensor);
}
接下来我们看看startLocked函数,它在SensorThread中,
void startLocked(ISensorService service) {
try {
if (mThread == null) {
Bundle dataChannel = service.getDataChannel();
mThread = new Thread(new SensorThreadRunnable(dataChannel),
SensorThread.class.getName());
mThread.start();
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in startLocked: ", e);
}
}
第一次mThread为null,然后它调用了service.getDataChannel()函数,此函数在SensorService类中,主要调用了jni函数_sensors_control_open(),
public Bundle getDataChannel() throws RemoteException {
// synchronize so we do not require sensor HAL to be thread-safe.
synchronized(mListeners) {
return _sensors_control_open();
}
}
SensorService类中调用的jni函数主要都在com_android_server_SensorService.cpp文件 中,我们看一下这个函数
static jobject
android_open(JNIEnv *env, jclass clazz)
{
native_handle_t* handle = sSensorDevice->open_data_source(sSensorDevice);
if (!handle) {
return NULL;
}
// new Bundle()
jobject bundle = env->NewObject(
gBundleOffsets.mClass,
gBundleOffsets.mConstructor);
if (handle->numFds > 0) {
jobjectArray fdArray = env->NewObjectArray(handle->numFds,
gParcelFileDescriptorOffsets.mClass, NULL);
for (int i = 0; i < handle->numFds; i++) {
// new FileDescriptor()
jobject fd = env->NewObject(gFileDescriptorOffsets.mClass,
gFileDescriptorOffsets.mConstructor);
env->SetIntField(fd, gFileDescriptorOffsets.mDescriptor, handle->data[i]);
// new ParcelFileDescriptor()
jobject pfd = env->NewObject(gParcelFileDescriptorOffsets.mClass,
gParcelFileDescriptorOffsets.mConstructor, fd);
env->SetObjectArrayElement(fdArray, i, pfd);
}
// bundle.putParcelableArray("fds", fdArray);
env->CallVoidMethod(bundle, gBundleOffsets.mPutParcelableArray,
env->NewStringUTF("fds"), fdArray);
}
if (handle->numInts > 0) {
jintArray intArray = env->NewIntArray(handle->numInts);
env->SetIntArrayRegion(intArray, 0, handle->numInts, &handle->data[handle->numInts]);
// bundle.putIntArray("ints", intArray);
env->CallVoidMethod(bundle, gBundleOffsets.mPutIntArray,
env->NewStringUTF("ints"), intArray);
}
// delete the file handle, but don't close any file descriptors
native_handle_delete(handle);
return bundle;
}
它主要调用了HAL层的open_data_source()函数。取得一些文件描述符等信息。
接下来SensorThread创建一个线程,调用start()就进入SensorThreadRunnable类的run()函数了,所以我们接着去看run()函数,它首先调用open()函数
if (!open()) {
return;
}
在open()函数中调用了 jni函数sensors_data_open(fds, ints);
static jint
sensors_data_open(JNIEnv *env, jclass clazz, jobjectArray fdArray, jintArray intArray)
{
jclass FileDescriptor = env->FindClass("java/io/FileDescriptor");
jfieldID fieldOffset = env->GetFieldID(FileDescriptor, "descriptor", "I");
int numFds = (fdArray ? env->GetArrayLength(fdArray) : 0);
int numInts = (intArray ? env->GetArrayLength(intArray) : 0);
native_handle_t* handle = native_handle_create(numFds, numInts);
int offset = 0;
for (int i = 0; i < numFds; i++) {
jobject fdo = env->GetObjectArrayElement(fdArray, i);
if (fdo) {
handle->data[offset++] = env->GetIntField(fdo, fieldOffset);
} else {
handle->data[offset++] = -1;
}
}
if (numInts > 0) {
jint* ints = env->GetIntArrayElements(intArray, 0);
for (int i = 0; i < numInts; i++) {
handle->data[offset++] = ints[i];
}
env->ReleaseIntArrayElements(intArray, ints, 0);
}
// doesn't take ownership of the native handle
return sSensorDevice->data_open(sSensorDevice, handle);
}
这函数最终调用了HAL层的data_open(),之后run()函数就进入一个while循环了。
while (true) {
// wait for an event
final int sensor = sensors_data_poll(values, status, timestamp);
int accuracy = status[0];
synchronized (sListeners) {
if (sensor == -1 || sListeners.isEmpty()) {
if (sensor == -1) {
// we lost the connection to the event stream. this happens
// when the last listener is removed.
Log.d(TAG, "_sensors_data_poll() failed, we bail out.");
}
// we have no more listeners or polling failed, terminate the thread
sensors_data_close();
mThread = null;
break;
}
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);
}
}
}
}
它调用了jni函数sensors_data_poll()一直读数据。
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;
}
把传感器得到的值都放在value数组中,根据返回的传感器标志sensor,把它分派给在sListener列表中所有的listener,如果listener中有监听这个sensor,就把它分派给这个listener,此时就会引起onSensorChange()了。
好了,获取传感器数据主要是这样一个途径。最后我们去分析一下unregisterListener()函数。
private void unregisterListener(Object listener) {
if (listener == null) {
return;
}
try {
synchronized (sListeners) {
final int size = sListeners.size();
for (int i=0 ; i<size ; i++) {
ListenerDelegate l = sListeners.get(i);
if (l.getListener() == listener) {
// disable all sensors for this listener
for (Sensor sensor : l.getSensors()) {
String name = sensor.getName();
int handle = sensor.getHandle();
mSensorService.enableSensor(l, name, handle, SENSOR_DISABLE);
}
sListeners.remove(i);
break;
}
}
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in unregisterListener: ", e);
}
}
不用想这个函数会做一些与registerListener相反的事情,至少差不多。它先在sListeners列表中找到这个listener,然后先调用enableSensor()函数禁止这个传感器。我们跟踪一下这函数,在SensorService类中。
synchronized(mListeners) {
if (enable!=SENSOR_DISABLE && !_sensors_control_activate(sensor, true)) {
Log.w(TAG, "could not enable sensor " + sensor);
return false;
}
Listener l = null;
int minDelay = enable;
for (Listener listener : mListeners) {
if (binder == listener.mToken) {
l = listener;
}
if (minDelay > listener.mDelay)
minDelay = listener.mDelay;
}
if (l == null && enable!=SENSOR_DISABLE) {
l = new Listener(binder);
binder.linkToDeath(l, 0);
mListeners.add(l);
mListeners.notify();
}
if (l == null) {
// by construction, this means we're disabling a listener we
// don't know about...
Log.w(TAG, "listener with binder " + binder +
", doesn't exist (sensor=" + name + ", id=" + sensor + ")");
return false;
}
if (minDelay >= 0) {
_sensors_control_set_delay(minDelay);
}
if (enable != SENSOR_DISABLE) {
l.addSensor(sensor, enable);
} else {
l.removeSensor(sensor);
deactivateIfUnusedLocked(sensor);
if (l.mSensors == 0) {
mListeners.remove(l);
binder.unlinkToDeath(l, 0);
mListeners.notify();
}
}
if (mListeners.size() == 0) {
_sensors_control_wake();
_sensors_control_close();
}
}
return true;
你们看到它的实现了吧。如果enable是true的话,就调用_sensors_control_activate(),如果是false的话,就调用deactivateIfUnusedLocked(),它们最终都会调用 HAL层的active()函数。最后,如果是禁止传感器的话,如果mListeners为空了,它就会调用
_sensors_control_wake();
_sensors_control_close();
这两个jni函数,最终会调用HAL层的wake()和close_data_source()函数。当调用wake()函数时,会使SensorManager类线程的run()函数中的sensor_data_poll()函数立即返回,此时在run()函数中调用sensors_data_close();最终会调用HAL层的data_close()函数。至此,一个传感器从初始到结束的流程就分析完了。
所以在java使用一个传感器在HAL层具体调用的函数流程为:
首先,sensors_control_open(),只在系统初始化时调用一次。用来初始化control_device结构体。
以下的是每次使用传感器一般经过的流程,注意,是一般而已,有些并不执行
(1)sensors_data_open
(2)get_sensors_list
(3)activate
(4)set_delay
(5)open_data_source
(6)data_open
(7)poll
一直读数据。。。。。。。。。。。
退出时
(8)activate
(9)sensors_control_close
(10)data_close
原文:http://blog.csdn.net/JerryMo06/archive/2010/04/15/5487476.aspx
发表评论
-
eclipse中安装插件地址
2014-03-07 15:08 720http://subclipse.tigris.org/up ... -
ubuntu13下载android源码
2014-02-27 18:23 601一、注意repo的正确地址 repo:curl " ... -
用例无法运行,报如下错:Exception during suite construction
2014-02-26 15:47 1187一 前提 1、 测试工程中的所有参数已配置好,如ins ... -
dx.jar dx.bat
2013-06-17 19:33 2191Android SDK中dx.jar, dx.bat文件的备份 ... -
Please ensure that adb is correctly located..... 问题
2013-04-16 09:44 931解决方法: 方法一、查毒杀毒,也许了病毒占用了adb ... -
PC端通过adb与设备端通信
2013-03-28 09:36 1459adb 全称Android Debug Bri ... -
Unable to execute dex: Multiple dex files define Lorg/taptwo/android/widget/Circ
2013-02-21 15:07 2546问题:[2013-02-21 15:01:02 - Dex ... -
android viewTree and decorView
2013-01-30 13:55 1667Android ViewTree and DecorView ... -
No active compatible AVD's or devices found. Relaunch this configuration after c
2013-01-29 10:56 8725问题:No active compatible AVD's ... -
代码对比工具
2013-01-24 15:33 608windows: http://www.scooterso ... -
android最新源码下载
2013-01-23 09:57 1020源码下载:https://source.android.co ... -
Run MonkeyTalk Scripts via Ant in Windows
2012-12-18 20:24 1232Run MonkeyTalk Scripts via ... -
Android中cpu,memory,Battery的计算
2012-11-20 19:59 65691 Memory的分配(RAM而非ROM) 网址: •ht ... -
http://code.taobao.org/p/TMTS/src/
2012-10-12 17:38 996http://code.taobao.org/p/TMTS/s ... -
string.xml文件中的特殊符号转换符
2012-09-24 09:09 731strings.xml文件中需要对特殊符号(如%,'等)进行转 ... -
web server 中设置wifi代理
2012-09-24 09:07 867DefaultHttpClient httpClient = ... -
Android开发一些常见问题
2012-09-24 09:06 7161:当追踪问题时,代码中实在找不出问题所以,代码的逻辑完全正确 ... -
将log信息写入sdcard
2012-07-05 17:14 833File mFile; private void write ... -
string.xml文件中的特殊符号转换符
2012-06-19 19:39 997strings.xml文件中需要对特殊符号(如%,'等)进行转 ... -
关于popupWindow的dismiss
2012-06-17 16:13 8542View contentView = LayoutInflat ...
相关推荐
qt for android 版本5.4重力感应官方源码 希望有用
lm_sensors(linux 下cpu温度读取工具)
Android sensors from touchquode.com
Android application - Sensors Web Logger This application will send Environment Sensors data together with location to custom URL you specify. It is actually nice alternative to Arduino sensors if you...
Sensors2OSC Android app for sending sensor data, multitouch and NFC tags via Open Sound Control (OSC) over network to a recipient. Typical use case is controlling a music application from your phone ...
ROS Android传感器Ros到Android传感器的界面! 该项目与兼容。 在开发过程中受到启发。 当前支持Android 7.0(牛轧糖)API 24 旨在帮助机器人导航和定位支持的传感器列表: 全球定位系统 陀螺仪 加速度计 磁力计 ...
Android应用Android感应应用##关于最近的手机提供很多传感器。 由于大多数机器人团队将至少有一个成员拥有一部智能手机,因此它们可以成为一种廉价而有效的传感解决方案。 这是我们用于将传感器数据发送到Trekking /...
lm_sensors-2.10.0-3.1.i386 安装包
Sensors2Pd Android app, that loads Pd patches and sends data from sensors to receivers in these patches.
安装NativeScript 7+: ns plugin add nativescript-android-sensors低于7的NativeScript版本: tns plugin add nativescript-android-sensors@1.5.0 Android传感器: : 用法import { AndroidSensors , ...
Learn to build human–interactive Android apps, starting with device sensorsThis book shows Android developers how to exploit the rich set of device sensors—locational, physical (temperature, ...
cesium源码_github包
Posture-and-Fall-Detection-System-Using-3D-Motion-Sensors-源码.rar
android-sensors-experiment 玩弄安卓传感器。 我想做点击检测,可能是在屏幕关闭的情况下。
无线传感器网络 外国教程 Networking.Wireless.Sensors
yolo-octo-android-sensors 带有传感器数据的 android 客户端服务器应用程序客户端应用程序(客户端)使用简单的 TCP 连接从服务器(androidserver)请求位置和传感器数据。 服务器在收到连接请求后,根据请求消息...
IEEE Sensors Journal期刊模板(WORD版)
The book covers a collection of key topics in the fi eld of sensors and micro-electro-mechanical structures (MEMS). Despite the numerous efforts in the last few decades to put order in the terminology...
Snake-android-sensors WebGL Snake 使用来自 javascript 的“设备方向”传感器
模板Android Android项目的模板。 Dagger2,RxJava2,MVVM,木材