在本文中,我们将给您介绍关于AndroidSensorEvent时间戳常量的详细内容,并且为您解答android获取时间戳的相关问题,此外,我们还将为您提供关于(OK)android-5.0senso
在本文中,我们将给您介绍关于Android SensorEvent时间戳常量的详细内容,并且为您解答android 获取时间戳的相关问题,此外,我们还将为您提供关于(OK) android-5.0 sensor 工作原理 —sensorservice 的启动 (一)、(OK) android-5.0 sensor工作原理—sensorservice的启动(二)、Android Firebase addListenerForSingleValueEvent不起作用、Android NDK and OpenCV Development With Android Studio的知识。
本文目录一览:- Android SensorEvent时间戳常量(android 获取时间戳)
- (OK) android-5.0 sensor 工作原理 —sensorservice 的启动 (一)
- (OK) android-5.0 sensor工作原理—sensorservice的启动(二)
- Android Firebase addListenerForSingleValueEvent不起作用
- Android NDK and OpenCV Development With Android Studio
Android SensorEvent时间戳常量(android 获取时间戳)
使用代码:
float dT = (event.timestamp-accel_timestamp)*NS2S;
从Android Reference Guide的例子中计算四元数的旋转矩阵.
当我使用galaxy nexus-s运行代码时,我在测量之间得到0.06~0.07秒的dT但是当我在LG Nexus 4或Nexus 7上运行相同的代码时,dT总是为0.我知道这个问题,Android SensorEvent timestamp issue,Nexus 7时间戳是一个unix时间戳,但连续测量之间的差异不应总是为零. Nexus 4和Nexus 7都有相同的IMU,这可能是IMU如何创建时间戳的错误?
解决方法
正在覆盖每个SensorEvent的时间戳,就像它是一个静态变量一样……
当我在事件发生时记录时间戳的字符串时,所有值都不同.
事件以不变的方式存储在数组中.
数组中的每个SensorEvent现在都具有相同的时间戳,但值数组仍然不同(即,它们不是同一个对象并包含不同的信息…时间戳除外).
Google / HTC,请返回我生命中的3个小时!
除非有人能解释这种行为,否则我将提交错误报告.它肯定没有记录在API中.
在此期间,尝试这个解决方案:
import android.hardware.Sensor; import android.hardware.SensorEvent; public class UnbrokenSensorEvent { public long timestamp; public float[] values; public Sensor sensor; public UnbrokenSensorEvent(SensorEvent event){ this.timestamp = event.timestamp; this.values = event.values; this.sensor = event.sensor; } }
然后在你的听众中做这样的事情:
ArrayList<UnbrokenSensorEvent> results = new ArrayList<UnbrokenSensorEvent>(); public void onSensorChanged(SensorEvent event) { results.add(new UnbrokenSensorEvent(event)); }
重构应该非常简单,因为SensorEvent和UnbrokenSensorEvent具有相同的公共字段.如果您需要使用其他SensorEvent功能,请继续将其放入Unbroken版本中.
这是hacky,但恕我直言快速黑客总是比等待API更新更好!
(OK) android-5.0 sensor 工作原理 —sensorservice 的启动 (一)
http://blog.csdn.net/zsj100213/article/details/48179655
sensorservice 的启动:
1. systemserver.Java 的 run 函数:
- private void run() {
- ……
- // Initialize native services.
- System.loadLibrary("android_servers");
- nativeInit();
- ……
- }
Android 在启动之后通过 Zygote 来管理和启动 android server,他首先会运行 SystemServer.java 里面的 run 函数,这里主要是 load android_server.so 库,然后调用 nativeInit () 来启动 sensorservice。
2. nativeInit () 是本地方法,上层 Java 调用本地 C++ 方法,需要通过 JNI 层的转化,在 com_android_server_SystemServer.cpp 中:
- static void android_server_SystemServer_nativeInit(JNIEnv* env, jobject clazz) {
- char propBuf[PROPERTY_VALUE_MAX];
- property_get("system_init.startsensorservice", propBuf, "1");
- if (strcmp(propBuf, "1") == 0) {
- // Start the sensor service
- SensorService::instantiate();
- }
- }
- /*
- * JNI registration.
- */
- static JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "nativeInit", "()V", (void*) android_server_SystemServer_nativeInit },
- };
- int register_android_server_SystemServer(JNIEnv* env)
- {
- return jniRegisterNativeMethods(env, "com/android/server/SystemServer",
- gMethods, NELEM(gMethods));
- }
首先建立 nativeInit () 方法对本地方法 android_server_SystemServer_nativeInit () 的映射,然后 jniRegisterNativeMethods 注册该方法,在上层调用 nativeInit () 方法就可以调用本地方法 android_server_SystemServer_nativeInit () 通过 SensorService::instantiate (); 来实例化 sensorservice,主要是向 servicemanager 注册 sensorservice。
- static status_t publish(bool allowIsolated = false) {
- sp<IServiceManager> sm(defaultServiceManager());
- return sm->addService(
- String16(SERVICE::getServiceName()),
- new SERVICE(), allowIsolated);
- }
- static void instantiate() { publish(); }
addservice () 方法主要是完成向 servicemanager 注册 sensorservice 服务,原型如下:
- virtual status_t addService( const String16& name,
- const sp<IBinder>& service,
- bool allowIsolated = false) = 0;
其第二个参数也就是 new SERVICE () 是强引用 sp sensorservice,所以会调用到 SensorService::onFirstRef (),这样就开始了 sensorservice 的一系列初始化工作。
3. SensorService::onFirstRef()
- void SensorService::onFirstRef()
- {
- ALOGD("nuSensorService starting...");
- SensorDevice& dev(SensorDevice::getInstance());
- if (dev.initCheck() == NO_ERROR) {
- sensor_t const* list;
- ssize_t count = dev.getSensorList(&list);
- if (count > 0) {
- ssize_t orientationIndex = -1;
- bool hasGyro = false;
- uint32_t virtualSensorsNeeds =
- (1<<SENSOR_TYPE_GRAVITY) |
- (1<<SENSOR_TYPE_LINEAR_ACCELERATION) |
- (1<<SENSOR_TYPE_ROTATION_VECTOR);
- mLastEventSeen.setCapacity(count);
- for (ssize_t i=0 ; i<count ; i++) {
- registerSensor( new HardwareSensor(list[i]) );
- switch (list[i].type) {
- case SENSOR_TYPE_ORIENTATION:
- orientationIndex = i;
- break;
- case SENSOR_TYPE_GYROSCOPE:
- case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
- hasGyro = true;
- break;
- case SENSOR_TYPE_GRAVITY:
- case SENSOR_TYPE_LINEAR_ACCELERATION:
- case SENSOR_TYPE_ROTATION_VECTOR:
- virtualSensorsNeeds &= ~(1<<list[i].type);
- break;
- }
- }
- // it''s safe to instantiate the SensorFusion object here
- // (it wants to be instantiated after h/w sensors have been
- // registered)
- const SensorFusion& fusion(SensorFusion::getInstance());
- // build the sensor list returned to users
- mUserSensorList = mSensorList;
- if (hasGyro) {
- Sensor aSensor;
- // Add Android virtual sensors if they''re not already
- // available in the HAL
- aSensor = registerVirtualSensor( new RotationVectorSensor() );
- if (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) {
- mUserSensorList.add(aSensor);
- }
- aSensor = registerVirtualSensor( new GravitySensor(list, count) );
- if (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) {
- mUserSensorList.add(aSensor);
- }
- aSensor = registerVirtualSensor( new LinearAccelerationSensor(list, count) );
- if (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) {
- mUserSensorList.add(aSensor);
- }
- aSensor = registerVirtualSensor( new OrientationSensor() );
- if (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) {
- // if we are doing our own rotation-vector, also add
- // the orientation sensor and remove the HAL provided one.
- mUserSensorList.replaceAt(aSensor, orientationIndex);
- }
- // virtual debugging sensors are not added to mUserSensorList
- registerVirtualSensor( new CorrectedGyroSensor(list, count) );
- registerVirtualSensor( new GyroDriftSensor() );
- }
- // debugging sensor list
- mUserSensorListDebug = mSensorList;
- // Check if the device really supports batching by looking at the FIFO event
- // counts for each sensor.
- bool batchingSupported = false;
- for (int i = 0; i < mSensorList.size(); ++i) {
- if (mSensorList[i].getFifoMaxEventCount() > 0) {
- batchingSupported = true;
- break;
- }
- }
- if (batchingSupported) {
- // Increase socket buffer size to a max of 100 KB for batching capabilities.
- mSocketBufferSize = MAX_SOCKET_BUFFER_SIZE_BATCHED;
- } else {
- mSocketBufferSize = SOCKET_BUFFER_SIZE_NON_BATCHED;
- }
- // Compare the socketBufferSize value against the system limits and limit
- // it to maxSystemSocketBufferSize if necessary.
- FILE *fp = fopen("/proc/sys/net/core/wmem_max", "r");
- char line[128];
- if (fp != NULL && fgets(line, sizeof(line), fp) != NULL) {
- line[sizeof(line) - 1] = ''\0'';
- size_t maxSystemSocketBufferSize;
- sscanf(line, "%zu", &maxSystemSocketBufferSize);
- if (mSocketBufferSize > maxSystemSocketBufferSize) {
- mSocketBufferSize = maxSystemSocketBufferSize;
- }
- }
- if (fp) {
- fclose(fp);
- }
- mWakeLockAcquired = false;
- mLooper = new Looper(false);
- const size_t minBufferSize = SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT;
- mSensorEventBuffer = new sensors_event_t[minBufferSize];
- mSensorEventScratch = new sensors_event_t[minBufferSize];
- mMapFlushEventsToConnections = new SensorEventConnection const * [minBufferSize];
- mInitCheck = NO_ERROR;
- run("SensorService", PRIORITY_URGENT_DISPLAY);
- }
- }
- }
- SensorDevice::SensorDevice()
- : mSensorDevice(0),
- mSensorModule(0)
- {
- status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
- (hw_module_t const**)&mSensorModule);
- ALOGE_IF(err, "couldn''t load %s module (%s)",
- SENSORS_HARDWARE_MODULE_ID, strerror(-err));
- if (mSensorModule) {
- err = sensors_open_1(&mSensorModule->common, &mSensorDevice);
- ALOGE_IF(err, "couldn''t open device for module %s (%s)",
- SENSORS_HARDWARE_MODULE_ID, strerror(-err));
- if (mSensorDevice) {
- if (mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_1 ||
- mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_2) {
- ALOGE(">>>> WARNING <<< Upgrade sensor HAL to version 1_3");
- }
- sensor_t const* list;
- ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list);
- mActivationCount.setCapacity(count);
- Info model;
- for (size_t i=0 ; i<size_t(count) ; i++) {
- mActivationCount.add(list[i].handle, model);
- mSensorDevice->activate(
- reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
- list[i].handle, 0);
- }
- }
- }
- }
sensorDevice 的实例化主要完成以下三项工作:
3.1.1. 主要是为了从 /system/lib/hw 或者 /vendor/lib/hw 路径 load 一个 sensors.(platform).so 的库文件,如 sensors.mt6753.so,第一个参数是我们所获取的 hardware 模块的名字,第二个参数是我们所要获得的 hw_module_t。
- hw_get_module(SENSORS_HARDWARE_MODULE_ID,
- (hw_module_t const**)&mSensorModule);
load () 首先是通过 dlopen () 函数载入前面获取的 path 的动态链接库,如 sensors.mt6753.so,然后使用 dlsym () 函数来获取.so 文件中的符号 HMI 的地址,最后 dlsym () 函数返回 hw_module_t 的地址 hmi,最后将 hmi 的值赋给参数 * pHmi。load () 函数的代码如下:
- /**
- * Name of the hal_module_info
- */
- #define HAL_MODULE_INFO_SYM HMI
- /**
- * Name of the hal_module_info as a string
- */
- #define HAL_MODULE_INFO_SYM_AS_STR "HMI" // 获取符号 HMI 的地址,然后 dlsym () 函数返回的是 hw_module_t () 的
- static int load(const char *id,
- const char *path,
- const struct hw_module_t **pHmi)
- {
- int status;
- void *handle;
- struct hw_module_t *hmi;
- /*
- * load the symbols resolving undefined symbols before
- * dlopen returns. Since RTLD_GLOBAL is not or''d in with
- * RTLD_NOW the external symbols will not be global
- */
- handle = dlopen(path, RTLD_NOW);
- if (handle == NULL) {
- char const *err_str = dlerror();
- ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
- status = -EINVAL;
- goto done;
- }
- /* Get the address of the struct hal_module_info. */
- const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
- hmi = (struct hw_module_t *)dlsym(handle, sym);
- if (hmi == NULL) {
- ALOGE("load: couldn''t find symbol %s", sym);
- status = -EINVAL;
- goto done;
- }
- /* Check that the id matches */
- if (strcmp(id, hmi->id) != 0) {
- ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
- status = -EINVAL;
- goto done;
- }
- hmi->dso = handle;
- /* success */
- status = 0;
- done:
- if (status != 0) {
- hmi = NULL;
- if (handle != NULL) {
- dlclose(handle);
- handle = NULL;
- }
- } else {
- ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
- id, path, *pHmi, handle);
- }
- *pHmi = hmi;
- return status;
- }
此处的 hw_get_module () 最后返回的 sensors_module_t 是经过封装的 hw_module_t,他除了包含一个 hw_module_t 还包含一个获取 sensorlist 的 API 这个结构体主要是几个 API 接口的定义,hw_module_t 结构体的定义如下:
- struct sensors_module_t HAL_MODULE_INFO_SYM = {
- common:{
- tag: HARDWARE_MODULE_TAG,
- version_major: 1,
- version_minor: 0,
- id: SENSORS_HARDWARE_MODULE_ID,
- name: "SPRD Sensor module",
- author: "Spreadtrum",
- methods: &sensors_module_methods,
- dso: 0,
- reserved:{},
- },
- get_sensors_list:sensors__get_sensors_list,
- };
- static struct hw_module_methods_t sensors_module_methods = {
- open: open_sensors
- };
- static int sensors__get_sensors_list(struct sensors_module_t *module,
- struct sensor_t const **list)
- {
- *list = sSensorList;
- return numSensors;
- }
3.1.2. 通过 sensors_open_1 (&mSensorModule->common, &mSensorDevice) 函数新建并初始化系统所有的 sensor。
- static inline int sensors_open_1(const struct hw_module_t* module,
- sensors_poll_device_1_t** device) {
- return module->methods->open(module,
- SENSORS_HARDWARE_POLL, (struct hw_device_t**)device);
- }
这里的 open 函数就是上一步 hw_get_module () 获取的 sensors_module_t 所定义的 sensors_module_methods 的 open_sensors 接口,open_sensors 接口的代码如下:
- static int open_sensors(const struct hw_module_t* module, const char* id,
- struct hw_device_t** device)
- {
- int status = -EINVAL;
- sensors_poll_context_t *dev = new sensors_poll_context_t();
- memset(&dev->device, 0, sizeof(sensors_poll_device_t));
- dev->device.common.tag = HARDWARE_DEVICE_TAG;
- dev->device.common.version = 0;
- dev->device.common.module = const_cast<hw_module_t*>(module);
- dev->device.common.close = poll__close;
- dev->device.activate = poll__activate;
- dev->device.setDelay = poll__setDelay;
- dev->device.poll = poll__poll;
- *device = &dev->device.common;
- status = 0;
- return status;
- }
函数首先是通过 sensors_poll_context_t () 方法新建并初始化系统所有的 sensor:
- sensors_poll_context_t::sensors_poll_context_t()
- {
- #ifndef ACC_NULL
- mSensors[acc] = new AccSensor();
- numSensors +=
- mSensors[acc]->populateSensorList(sSensorList + numSensors);
- mPollFds[acc].fd = mSensors[acc]->getFd();
- mPollFds[acc].events = POLLIN;
- mPollFds[acc].revents = 0;
- ALOGD("AnumSensors=%d; %d", numSensors, AccSensor::numSensors);
- #endif
- #ifndef ORI_NULL
- mSensors[ori] = new OriSensor();
- numSensors +=
- mSensors[ori]->populateSensorList(sSensorList + numSensors);
- mPollFds[ori].fd = mSensors[ori]->getFd();
- mPollFds[ori].events = POLLIN;
- mPollFds[ori].revents = 0;
- ALOGD("OnumSensors=%d; %d", numSensors, OriSensor::numSensors);
- #endif
- #ifdef SENSORHAL_PROXIMITY_STK
- mSensors[stk_als] = <span style="color:#FF0000;">new STKLightSensor();</span>
- numSensors +=
- mSensors[stk_als]->populateSensorList(sSensorList + numSensors);
- mPollFds[stk_als].fd = mSensors[stk_als]->getFd();
- mPollFds[stk_als].events = POLLIN;
- mPollFds[stk_als].revents = 0;
- mSensors[stk_ps] = new STKProximitySensor();
- numSensors +=
- mSensors[stk_ps]->populateSensorList(sSensorList + numSensors);
- mPollFds[stk_ps].fd = mSensors[stk_ps]->getFd();
- mPollFds[stk_ps].events = POLLIN;
- mPollFds[stk_ps].revents = 0;
- #else
- #ifndef PLS_NULL
- mSensors[pls] = new PlsSensor();
- numSensors +=
- mSensors[pls]->populateSensorList(sSensorList + numSensors);
- mPollFds[pls].fd = mSensors[pls]->getFd();
- mPollFds[pls].events = POLLIN;
- mPollFds[pls].revents = 0;
- ALOGD("PnumSensors=%d; %d", numSensors, PlsSensor::numSensors);
- #endif
- #endif
- int wakeFds[2];
- int result = pipe(wakeFds);
- ALOGE_IF(result < 0, "error creating wake pipe (%s)", strerror(errno));
- fcntl(wakeFds[0], F_SETFL, O_NONBLOCK);
- fcntl(wakeFds[1], F_SETFL, O_NONBLOCK);
- mWritePipeFd = wakeFds[1];
- mPollFds[wake].fd = wakeFds[0];
- mPollFds[wake].events = POLLIN;
- mPollFds[wake].revents = 0;
- }
例如在 new STKLightSensor () 中主要定义了数据读取环形缓冲区的大小,sensor_event_t 的初始化,以及 input 子系统上报事件所保存的路径。STKLightSensor () 代码如下:
- STKLightSensor::STKLightSensor()
- : SensorBase(NULL, "lightsensor-level"),
- mEnabled(0),
- mInputReader(4),
- mHasPendingEvent(false) {
- mPendingEvent.version = sizeof(sensors_event_t);
- mPendingEvent.sensor = ID_L;
- mPendingEvent.type = SENSOR_TYPE_LIGHT;
- memset(mPendingEvent.data, 0, sizeof(mPendingEvent.data));
- if (data_fd) {
- #if 1
- strcpy(input_sysfs_path, "/sys/class/input/");
- strcat(input_sysfs_path, input_name);
- strcat(input_sysfs_path, "/device/driver/"); // 此项目初始化文件节点所在文件夹目录为:/sys/class/input/input*/device/driver/
- input_sysfs_path_len = strlen(input_sysfs_path);
- #else
- strcpy(input_sysfs_path, INPUT_SYSFS_PATH_ALS);
- #endif
- input_sysfs_path_len = strlen(input_sysfs_path);
- LOGD("%s: input=%s", __func__, input_name);
- LOGD("%s: input_sysfs_path = %s\n", __func__, input_sysfs_path);
- }
- }
open_sensors 第二步是对 sensors_poll_device_t 结构体的初始化,主要是对 sensor 打开,关闭,以及数据获取的 API 借口的定义,这几个 API 接口将在后面详细描述。
3.1.3. mSensorModule->get_sensors_list (mSensorModule, &list) 这个是前面获取的 sensors_module_t 中定义的 get_sensor_list 方法。他最终获取的是 HAL 层初始化好的 sensor 的列表,并返回 sensor 的数量,active 方法打开 sensor,
- mSensorDevice->activate(
- reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
- list[i].handle, 0);
- static int poll__activate(struct sensors_poll_device_t *dev,
- int handle, int enabled)
- {
- sensors_poll_context_t *ctx = (sensors_poll_context_t *) dev;
- return ctx->activate(handle, enabled);
- }
- int sensors_poll_context_t::activate(int handle, int enabled)
- {
- int drv = handleToDriver(handle); // 获取 sensor 的类型
- int err;
- switch (handle) {
- case ID_A:
- case ID_M:
- case ID_L:
- case ID_P:
- /* No dependencies */
- break;
- case ID_O:
- /* These sensors depend on ID_A and ID_M */
- mSensors[handleToDriver(ID_A)]->setEnable(ID_A, enabled);
- mSensors[handleToDriver(ID_M)]->setEnable(ID_M, enabled);
- break;
- default:
- return -EINVAL;
- }
- ALOGD("activate handle=%d; drv=%d", handle, drv);
- err = mSensors[drv]->setEnable(handle, enabled); // 使用对应的 sensor 的 setEnable 方法
- if (enabled && !err) {
- const char wakeMessage(WAKE_MESSAGE);
- int result = write(mWritePipeFd, &wakeMessage, 1);
- ALOGE_IF(result < 0, "error sending wake message (%s)",
- strerror(errno));
- }
- return err;
- }
- int STKLightSensor::setEnable(int32_t, int en) {
- int flags = en ? 1 : 0;
- if (flags != mEnabled) {
- int fd;
- strcpy(&input_sysfs_path[input_sysfs_path_len], "enable"); // 初始化的 input_sysfs_path 是 /sys/class/input/input*/device/driver/,此项目的文件节点是
- /sys/class/input/input*/device/driver/enable,所以使用 strcpy 在 input_sysfs_path 后面添加了 enable 节点。
- fd = open(input_sysfs_path, O_RDWR); // 打开对应的 sysfs 目录下的文件节点
- LOGE("STKLightSensor enable path %s fd %d", input_sysfs_path, fd);
- if (fd >= 0) {
- char buf[2];
- buf[1] = 0;
- if (flags) {
- buf[0] = ''1'';
- } else {
- buf[0] = ''0'';
- }
- write(fd, buf, sizeof(buf)); // 将 buf 的值写到 sysfs 的目录下面 sensor 对应的文件节点
- close(fd);
- mEnabled = flags;
- if(flags)
- setInitialState(); // 处理 sensor 开启后的第一笔数据
- return 0;
- }
- return -1;
- }
- return 0;
- }
如果打开了 sensor,则会调用 setInitialState () 通过 ioctl 来从 data_fd 中获取数据,然后将是否有待处理数据设置为 “true”,在 poll 后面将要讲到的 poll 数据时会直接先处理这个数据。这里获取的数据个人理解是在 sensor 开启之后的第一笔数据。
- int STKLightSensor::setInitialState() {
- struct input_absinfo absinfo;
- if (!ioctl(data_fd, EVIOCGABS(ABS_MISC), &absinfo)) {
- mPendingEvent.light = (float)absinfo.value;
- mHasPendingEvent = true;
- }
- else
- ALOGE("%s:ioctl failed!", __func__);
- return 0;
- }
至此,sensorDevice 的实例化就完成了。下一篇将继续讲
(OK) android-5.0 sensor工作原理—sensorservice的启动(二)
http://blog.csdn.net/zsj100213/article/details/48287023
3.2 dev.getSensorList(&list);获取厂商在HAL层初始化的sensor_t类型结构体的sSensorList,并返回sensor device的数目。
3.3 registerSensor( new HardwareSensor(list[i]) );在for循环中注册这些sensor。根据硬件sensor_t创建HardwareSensor,然后加入mSensorList(Sensor) 和mSensorMap(HardwareSensor)中,后面的switch是判断是否存在电子罗盘,GYRO以及GYRO对应所需要的对应的一些所对应的一些虚拟sensor
- for (ssize_t i=0 ; i<count ; i++) {
- registerSensor( new HardwareSensor(list[i]) );
- switch (list[i].type) {
- case SENSOR_TYPE_ORIENTATION:
- orientationIndex = i;
- break;
- case SENSOR_TYPE_GYROSCOPE:
- case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
- hasGyro = true;
- break;
- case SENSOR_TYPE_GRAVITY:
- case SENSOR_TYPE_LINEAR_ACCELERATION:
- case SENSOR_TYPE_ROTATION_VECTOR:
- virtualSensorsNeeds &= ~(1<<list[i].type);
- break;
- }
- Sensor SensorService::registerSensor(SensorInterface* s)
- {
- sensors_event_t event;
- memset(&event, 0, sizeof(event));
- const Sensor sensor(s->getSensor());
- // add to the sensor list (returned to clients)
- mSensorList.add(sensor);
- // add to our handle->SensorInterface mapping
- mSensorMap.add(sensor.getHandle(), s);
- // create an entry in the mLastEventSeen array
- mLastEventSeen.add(sensor.getHandle(), event);
- return sensor;
- }
3.4 mUserSensorList = mSensorList;将mSensorList传感器列表赋值给mUserSensorList,mSensorList是由registerSensor初始化的,mUserSensorList是要提交给Java框架层的传感器列表。
3.5 如果有GYRO就注册这样一些上面提到的三种虚拟sensor来实现GYRO的功能,此处我是这么理解的。
- if (hasGyro) {
- Sensor aSensor;
- // Add Android virtual sensors if they''re not already
- // available in the HAL
- aSensor = registerVirtualSensor( new RotationVectorSensor() );
- if (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) {
- mUserSensorList.add(aSensor);
- }
- aSensor = registerVirtualSensor( new GravitySensor(list, count) );
- if (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) {
- mUserSensorList.add(aSensor);
- }
- aSensor = registerVirtualSensor( new LinearAccelerationSensor(list, count) );
- if (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) {
- mUserSensorList.add(aSensor);
- }
- aSensor = registerVirtualSensor( new OrientationSensor() );
- if (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) {
- // if we are doing our own rotation-vector, also add
- // the orientation sensor and remove the HAL provided one.
- mUserSensorList.replaceAt(aSensor, orientationIndex);
- }
- // virtual debugging sensors are not added to mUserSensorList
- registerVirtualSensor( new CorrectedGyroSensor(list, count) );
- registerVirtualSensor( new GyroDriftSensor() );
- }
3.6 run("SensorService", PRIORITY_URGENT_DISPLAY);启动sensorService线程,sensorService父类有一个Thread线程,调用run方法会创建线程并调用threadLoop方法。
- bool SensorService::threadLoop()
- {
- ALOGD("nuSensorService thread starting...");
- // each virtual sensor could generate an event per "real" event, that''s why we need
- // to size numEventMax much smaller than MAX_RECEIVE_BUFFER_EVENT_COUNT.
- // in practice, this is too aggressive, but guaranteed to be enough.
- const size_t minBufferSize = SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT;
- const size_t numEventMax = minBufferSize / (1 + mVirtualSensorList.size());
- SensorDevice& device(SensorDevice::getInstance());
- const size_t vcount = mVirtualSensorList.size();
- SensorEventAckReceiver sender(this);
- sender.run("SensorEventAckReceiver", PRIORITY_URGENT_DISPLAY);
- const int halVersion = device.getHalDeviceVersion();
- do {
- ssize_t count = device.poll(mSensorEventBuffer, numEventMax);
- if (count < 0) {
- ALOGE("sensor poll failed (%s)", strerror(-count));
- break;
- }
- // Reset sensors_event_t.flags to zero for all events in the buffer.
- for (int i = 0; i < count; i++) {
- mSensorEventBuffer[i].flags = 0;
- }
- // Make a copy of the connection vector as some connections may be removed during the
- // course of this loop (especially when one-shot sensor events are present in the
- // sensor_event buffer). Promote all connections to StrongPointers before the lock is
- // acquired. If the destructor of the sp gets called when the lock is acquired, it may
- // result in a deadlock as ~SensorEventConnection() needs to acquire mLock again for
- // cleanup. So copy all the strongPointers to a vector before the lock is acquired.
- SortedVector< sp<SensorEventConnection> > activeConnections;
- {
- Mutex::Autolock _l(mLock);
- for (size_t i=0 ; i < mActiveConnections.size(); ++i) {
- sp<SensorEventConnection> connection(mActiveConnections[i].promote());
- if (connection != 0) {
- activeConnections.add(connection);
- }
- }
- }
- Mutex::Autolock _l(mLock);
- // Poll has returned. Hold a wakelock if one of the events is from a wake up sensor. The
- // rest of this loop is under a critical section protected by mLock. Acquiring a wakeLock,
- // sending events to clients (incrementing SensorEventConnection::mWakeLockRefCount) should
- // not be interleaved with decrementing SensorEventConnection::mWakeLockRefCount and
- // releasing the wakelock.
- bool bufferHasWakeUpEvent = false;
- for (int i = 0; i < count; i++) {
- if (isWakeUpSensorEvent(mSensorEventBuffer[i])) {
- bufferHasWakeUpEvent = true;
- break;
- }
- }
- if (bufferHasWakeUpEvent && !mWakeLockAcquired) {
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
- mWakeLockAcquired = true;
- }
- recordLastValueLocked(mSensorEventBuffer, count);
- // handle virtual sensors
- if (count && vcount) {
- sensors_event_t const * const event = mSensorEventBuffer;
- const size_t activeVirtualSensorCount = mActiveVirtualSensors.size();
- if (activeVirtualSensorCount) {
- size_t k = 0;
- SensorFusion& fusion(SensorFusion::getInstance());
- if (fusion.isEnabled()) {
- for (size_t i=0 ; i<size_t(count) ; i++) {
- fusion.process(event[i]);
- }
- }
- for (size_t i=0 ; i<size_t(count) && k<minBufferSize ; i++) {
- for (size_t j=0 ; j<activeVirtualSensorCount ; j++) {
- if (count + k >= minBufferSize) {
- ALOGE("buffer too small to hold all events: "
- "count=%zd, k=%zu, size=%zu",
- count, k, minBufferSize);
- break;
- }
- sensors_event_t out;
- SensorInterface* si = mActiveVirtualSensors.valueAt(j);
- if (si->process(&out, event[i])) {
- mSensorEventBuffer[count + k] = out;
- k++;
- }
- }
- }
- if (k) {
- // record the last synthesized values
- recordLastValueLocked(&mSensorEventBuffer[count], k);
- count += k;
- // sort the buffer by time-stamps
- sortEventBuffer(mSensorEventBuffer, count);
- }
- }
- }
- // handle backward compatibility for RotationVector sensor
- if (halVersion < SENSORS_DEVICE_API_VERSION_1_0) {
- for (int i = 0; i < count; i++) {
- if (mSensorEventBuffer[i].type == SENSOR_TYPE_ROTATION_VECTOR) {
- // All the 4 components of the quaternion should be available
- // No heading accuracy. Set it to -1
- mSensorEventBuffer[i].data[4] = -1;
- }
- }
- }
- // Map flush_complete_events in the buffer to SensorEventConnections which called
- // flush on the hardware sensor. mapFlushEventsToConnections[i] will be the
- // SensorEventConnection mapped to the corresponding flush_complete_event in
- // mSensorEventBuffer[i] if such a mapping exists (NULL otherwise).
- for (int i = 0; i < count; ++i) {
- mMapFlushEventsToConnections[i] = NULL;
- if (mSensorEventBuffer[i].type == SENSOR_TYPE_META_DATA) {
- const int sensor_handle = mSensorEventBuffer[i].meta_data.sensor;
- SensorRecord* rec = mActiveSensors.valueFor(sensor_handle);
- if (rec != NULL) {
- mMapFlushEventsToConnections[i] = rec->getFirstPendingFlushConnection();
- rec->removeFirstPendingFlushConnection();
- }
- }
- }
- // Send our events to clients. Check the state of wake lock for each client and release the
- // lock if none of the clients need it.
- bool needsWakeLock = false;
- size_t numConnections = activeConnections.size();
- for (size_t i=0 ; i < numConnections; ++i) {
- if (activeConnections[i] != 0) {
- activeConnections[i]->sendEvents(mSensorEventBuffer, count, mSensorEventScratch,
- mMapFlushEventsToConnections);
- needsWakeLock |= activeConnections[i]->needsWakeLock();
- // If the connection has one-shot sensors, it may be cleaned up after first trigger.
- // Early check for one-shot sensors.
- if (activeConnections[i]->hasOneShotSensors()) {
- cleanupAutoDisabledSensorLocked(activeConnections[i], mSensorEventBuffer,
- count);
- }
- }
- }
- if (mWakeLockAcquired && !needsWakeLock) {
- release_wake_lock(WAKE_LOCK_NAME);
- mWakeLockAcquired = false;
- }
- } while (!Thread::exitPending());
- ALOGW("Exiting SensorService::threadLoop => aborting...");
- abort();
- return false;
- }
3.6.1 首先是调用device.poll(mSensorEventBuffer, numEventMax);来获取sensor数据。这里调用的是前面open_sensors函数初始化的 dev->device.poll = poll__poll;
- static int poll__poll(struct sensors_poll_device_t *dev,
- sensors_event_t* data, int count) {
- sensors_poll_context_t *ctx = (sensors_poll_context_t *)dev;
- return ctx->pollEvents(data, count);
- }
可以看到最后调用的是pollEvents函数:
- int sensors_poll_context_t::pollEvents(sensors_event_t * data, int count)
- {
- int nbEvents = 0;
- int n = 0;
- int polltime = -1;
- do {
- // see if we have some leftover from the last poll()
- for (int i = 0; count && i < numSensorDrivers; i++) {
- SensorBase *const sensor(mSensors[i]);
- if ((mPollFds[i].revents & POLLIN)
- || (sensor->hasPendingEvents())) {
- int nb = sensor->readEvents(data, count);
- if (nb < count) {
- // no more data for this sensor
- mPollFds[i].revents = 0;
- }
- #ifndef ORI_NULL
- if ((0 != nb) && (acc == i)) {
- ((OriSensor *) (mSensors[ori]))->
- setAccel(&data[nb - 1]);
- }
- #endif
- count -= nb;
- nbEvents += nb;
- data += nb;
- }
- }
- if (count) {
- // we still have some room, so try to see if we can get
- // some events immediately or just wait if we don''t have
- // anything to return
- do {
- n = poll(mPollFds, numFds,
- nbEvents ? 0 : polltime);
- } while (n < 0 && errno == EINTR);
- if (n < 0) {
- ALOGE("poll() failed (%s)", strerror(errno));
- return -errno;
- }
- if (mPollFds[wake].revents & POLLIN) {
- char msg;
- int result = read(mPollFds[wake].fd, &msg, 1);
- ALOGE_IF(result < 0,
- "error reading from wake pipe (%s)",
- strerror(errno));
- ALOGE_IF(msg != WAKE_MESSAGE,
- "unknown message on wake queue (0x%02x)",
- int (msg));
- mPollFds[wake].revents = 0;
- }
- }
- // if we have events and space, go read them
- } while (n && count);
- return nbEvents;
- }
这里首先调用的是sensor->readEvents(data, count);也就是各个sensor对应的读取sensor event的方法。readEvents()函数先调用fill函数将所要读的sensor的input_event事件读取到一个环形缓冲区,然后在while循环中mInputReader.readEvent(&event)读取当前指针所指向的事件,mInputReader.next();将指针指向环形队列下一条事件来读取下一条事件。
- int STKLightSensor::readEvents(sensors_event_t* data, int count){
- if (count < 1)
- return -EINVAL;
- if (mHasPendingEvent) {
- mHasPendingEvent = false;
- mPendingEvent.timestamp = getTimestamp();
- *data = mPendingEvent;
- return mEnabled ? 1 : 0;
- }
- ssize_t n = mInputReader.fill(data_fd);
- if (n < 0)
- return n;
- int numEventReceived = 0;
- input_event const* event;
- while (count && mInputReader.readEvent(&event)) {
- int type = event->type;
- if (type == EV_ABS) {
- if (event->code == ABS_MISC) {
- mPendingEvent.light = (float)event->value;
- }
- } else if (type == EV_SYN) {
- mPendingEvent.timestamp = timevalToNano(event->time);
- if (mEnabled) {
- *data++ = mPendingEvent;
- count--;
- numEventReceived++;
- }
- } else {
- ALOGE("STKLightSensor: unknown event (type=%d, code=%d)",
- type, event->code);
- }
- mInputReader.next();
- }
- return numEventReceived;
- }
fill函数的参数data_fd是在前面sensors_poll_context_t()函数中的new STKLightSensor();进行初始化的,其主要是SensorBase(NULL, "lightsensor-level"),这里主要是openInput动作来获得一个fd从而从这个fd的文件中获取sensor event。
- ssize_t InputEventCircularReader::fill(int fd)
- {
- size_t numEventsRead = 0;
- if (mFreeSpace) {
- const ssize_t nread =
- read(fd, mHead, mFreeSpace * sizeof(input_event));
- if (nread < 0 || nread % sizeof(input_event)) {
- // we got a partial event!!
- return nread < 0 ? -errno : -EINVAL;
- }
- numEventsRead = nread / sizeof(input_event);
- if (numEventsRead) {
- mHead += numEventsRead;
- mFreeSpace -= numEventsRead;
- if (mHead > mBufferEnd) {
- size_t s = mHead - mBufferEnd;
- memcpy(mBuffer, mBufferEnd,
- s * sizeof(input_event));
- mHead = mBuffer + s;
- }
- }
- }
- return numEventsRead;
- }
- ssize_t InputEventCircularReader::readEvent(input_event const **events)
- {
- *events = mCurr;
- ssize_t available = (mBufferEnd - mBuffer) - mFreeSpace;
- return available ? 1 : 0;
- }
- void InputEventCircularReader::next()
- {
- mCurr++;
- mFreeSpace++;
- if (mCurr >= mBufferEnd) {
- mCurr = mBuffer;
- }
- }
- SensorBase::SensorBase(
- const char* dev_name,
- const char* data_name)
- : dev_name(dev_name), data_name(data_name),
- dev_fd(-1), data_fd(-1)
- {
- if (data_name) {
- data_fd = openInput(data_name);
- }
- }
openInput()实际上是打开各个sensor对应于/dev/input/event*文件节点最后返回的是sensor对应文件节点的fd,也就是上面fill函数的参数data_fd,在adb shell,然后getevent命令也是获取位于/dev/input/下所有的event事件。
- int SensorBase::openInput(const char* inputName) {
- int fd = -1;
- const char *dirname = "/dev/input";
- char devname[PATH_MAX];
- char *filename;
- DIR *dir;
- struct dirent *de;
- dir = opendir(dirname);
- if(dir == NULL)
- return -1;
- strcpy(devname, dirname);
- filename = devname + strlen(devname);
- *filename++ = ''/'';
- while((de = readdir(dir))) {
- if(de->d_name[0] == ''.'' &&
- (de->d_name[1] == ''\0'' ||
- (de->d_name[1] == ''.'' && de->d_name[2] == ''\0'')))
- continue;
- strcpy(filename, de->d_name);
- fd = open(devname, O_RDONLY);
- if (fd>=0) {
- char name[80];
- if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
- name[0] = ''\0'';
- }
- if (!strcmp(name, inputName)) {
- strcpy(input_name, filename);
- break;
- } else {
- close(fd);
- fd = -1;
- }
- }
- }
- closedir(dir);
- ALOGE_IF(fd<0, "couldn''t find ''%s'' input device", inputName);
- return fd;
在获取完数据之后就要通过 activeConnections[i]->sendEvents(mSensorEventBuffer, count, mSensorEventScratch,mMapFlushEventsToConnections);调用write函数将数据放到一个管道里面,sensor APP将数据取走。
- ssize_t size = SensorEventQueue::write(mChannel,
- reinterpret_cast<ASensorEvent const*>(scratch), count);
至此sensorservice的启动就讲完了,下一篇将从sensor APP端讲述sensor APP的工作流程。
Android Firebase addListenerForSingleValueEvent不起作用
我知道这已被问了很多,但我仍然可以让它工作.这是我的代码:
private int test;
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userRef = rootRef.child("Users");
test = 0;
userRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChild(userID))
test = 1;
else
test = 2;
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
Log.d(TAG, "Test = " + test);
这是Firebase上的内容:
> appname-e545e
>用户
> userID
我不知道为什么但变量“test”总是返回值0,这意味着addListenerForSingleValueEvent不起作用.感谢您提前帮助
解决方法:
数据是从Firebase异步加载的.到日志测试时,onDataChange还没有运行.
要查看此操作,请在onDataChange中添加更多日志记录:
私人int测试;
test = 0;
userRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChild(userID)) {
test = 1;
}
else {
test = 2;
}
Log.d(TAG, "Test2 = " + test);
}
@Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // Don't ignore errors
}
});
Log.d(TAG, "Test = " + test);
现在你先看到:
Test = 0
然后一旦数据加载并调用onDataChange:
Test = 1
要么
Test = 2
因此,您应始终在onDataChange()中放置(或调用)需要数据库数据的代码.
Android NDK and OpenCV Development With Android Studio
---------------- If you do NOT know Chinese, you can just skip this part ----------------
一直打算将原来的XFace进行改进,最近终于有了些时间可以动手了,改进计划如下:开发上使用Android Studio作为新的开发环境,配上新的构建系统Gradle;应用上将修改原来的UI设计,内部代码也将有很大的变化,可能会用上ContentProvider和Service等略高级内容;算法上打算让应用扩展性增强以适应不同的算法,并结合强大的Android Studio和Gradle让这个项目变得更加丰富。说了一堆废话,言归正传,本文的重点是介绍如何在Android Studio中进行NDK开发(目前它还不完全支持NDK开发),难点是NDK中还包含OpenCV的动态库。最后的最后,本文剩下部分将使用英文,因为它要成为我在StackOverflow上的处女答,么么哒 ~O(∩_∩)O~
---------------------------- Here is the right stuff you may need --------------------------------
This post shows how to develop an Android NDK application with OpenCV included using Android Studio and Gradle. If you''re working on migrating your original Eclipse Project to Android Studio, you may find this post is what exactly you want!
OK,Let''s start!
Section 1: Three things you must know
1.Firstly, if you are not familiar with Android Studio and Gradle, you may find these links useful. (if you already know these well, skip this part)
①Creating a new Project with Android Studio
②Building Your Project with Gradle
③Gradle Plugin User Guide or you may want to read a Chinese commented version in my blog here.
2.Secondly, if your android ndk project is not that complicated(for example, having no opencv included), you may wanna see ph0b
''s introduction here, it''s quite a nice job with a video recorded! (you can also follow Section 2 in this post to get a simple Android NDK demo application)
ph0b
''s post: ANDROID STUDIO, GRADLE AND NDK INTEGRATION
3.Thirdly, if those above two do not meet your needs, then I think you may want to customize the Android.mk with Gradle in Android Studio. Thanks to Gaku Ueda
, he had made a great job explaining how to achieve that goal. Actually I have found another nicer solution without adding that many codes and also achieve that goal. :-) Find it out in the next sections.
Gaku Ueda
''s post: Using custom Android.mk with Gradle/Android Studio
OK, I will cover all above and give another nice solution in the end, have fun!
Section 2: A simple Android NDK demo application
This section shows creating a simple Android NDK demo application, if you already know, you can directly go the section 3.
1.Create a new Android project named NDKDemo
with a blank Activity in AS(=Android Studio).
2.Give an id
to the TextView
in activity_my.xml
such as android:id="@+id/textview"
, then add these codes in MyActivity.java
.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
TextView textView = (TextView) findViewById(R.id.textview);
textView.setText(hello());
}
static {
System.loadLibrary("hello");
}
public native String hello();
3.Create a new directory jni
in folder app/src/main
, then you have java
, jni
and res
in this folder.
4.This step is very important! You can add a external tool to run the javah
command without typing that much code!
Open AS''s Preferences
, then find External Tools
in IDE Settings
, click +
to add one tool with the following configurations. (Make sure you have add JDK tools
in your system path
, if you don''t know how, click here)
With the help of this tool, each time we right click on a class file
, then choose Android Tools -> javah
to run this tool, it will automatically generate a C head file
for us in the target folder $ModuleFileDir$/src/main/jni
, in this case, it is app/src/main/jni
. Try this on MyActivity.java
file now! The console will print out a log like:
/usr/bin/javah -v -jni -d /Users/hujiawei/AndroidStudioProjects/NDKDemo/app/src/main/jni com.android.hacks.ndkdemo.MyActivity
[Creating file RegularFileObject[/Users/hujiawei/AndroidStudioProjects/NDKDemo/app/src/main/jni/
com_android_hacks_ndkdemo_MyActivity.h]]
Then you get a com_android_hacks_ndkdemo_MyActivity.h
file in jni
folder with the following content.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_android_hacks_ndkdemo_MyActivity */
#ifndef _Included_com_android_hacks_ndkdemo_MyActivity
#define _Included_com_android_hacks_ndkdemo_MyActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_android_hacks_ndkdemo_MyActivity
* Method: hello
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_android_hacks_ndkdemo_MyActivity_hello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
5.Write a simple C
implementation file named main.c
in jni
folder
#include <jni.h>
#include "com_android_hacks_ndkdemo_MyActivity.h"
JNIEXPORT jstring JNICALL Java_com_android_hacks_ndkdemo_MyActivity_hello
(JNIEnv * env, jobject obj){
return (*env)->NewStringUTF(env, "Hello from JNI");
}
6.In the build.gradle
file under app
module, add the following codes to configure ndk
in defaultConfig
element, here we just give the uni module a name hello
, you can find other configurations in Gradle Plugin User Guide.
defaultConfig {
applicationId "com.android.hacks.ndkdemo"
minSdkVersion 16
targetSdkVersion 20
versionCode 1
versionName "1.0"
ndk{
moduleName "hello"
}
}
7.In order to let Gradle run ndk-build
command (in some task, maybe NdkCompile
task), we should configure the ndk.dir
in local.properties
file in Project root.
sdk.dir=/Volumes/hujiawei/Users/hujiawei/Android/android_sdk
ndk.dir=/Volumes/hujiawei/Users/hujiawei/Android/android_ndk
8.OK, everything is ready, click Run
to give it a try, you will see the result like
All right, so what''s happening inside?
Since you have a jni
folder, Gradle will consider it as a default native code folder. When Gradle builds the app
, it will run ndk-build
command(since you have configured ndk.dir
, Gradle knows where to find it) with a generated Android.mk
file(locates in app/build/intermediates/ndk/debug/Android.mk
), after compiling the native codes, it will generate the libs
and obj
folder into folder app/build/intermediates/ndk/debug/
. Gradle will then package the libs
into final apk
file in folder app/build/outputs/apk/app-debug.apk
(you can unarchive this file to check whether libs
is contained)
app/build/intermediates/ndk/debug
(lib
and obj
folders)
app/build/outputs/apk/app-debug.apk
(and files within it)
Secontion 3: Using OpenCV
If your project do not use OpenCV, then the section 2 is just enough. But what if you wanna use OpenCV to do other stuff? Of course, we want to use OpenCV for Android
instead of JavaCV
here, and Of course, we need to package OpenCV library for Android into our application''s APK file (then users who use this app does not have to install OpenCV Manager
). So, how can we achieve these goals?
The simplest way has been posted by TGMCians
on Stack Overflow here, that is, let the main app include the OpenCV library as a dependency, and copy all <abi>/*.so
files in OpenCV for Android SDK to jniLibs
folder under app/src/main/
, Gradle will automatically package these <abi>/*.so
files into libs
folder within the final APK file. Of course, this method will work, but it has a few backwards: (1) Unless you only copy the needed *.so
files, you will always have a large APK due to this reason; (2) How about the building of the jni
files? How to run ndk-build
if these files contain opencv
related codes?
So, here comes to our Using custom Android.mk with Gradle and Android Studio
part. For testing, we first creat an Android.mk
and an Application.mk
file under jni
folder.
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c
LOCAL_LDLIBS += -llog
LOCAL_MODULE := hello
include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_ABI := armeabi
APP_PLATFORM := android-16
Thanks to Gaku Ueda
, he had made a great job explaining how to achieve that goal with this post. The core idea of his method is to run ndk-build
command in some task, then zip the <abi>/*.so
files under the output app/build/libs/
folder into a jar
file which is finally put in app/build/libs/
folder, then add a compile dependency to this jar file. The key code for his method listed below
Notice 1: When using custom Android.mk, we should first disable Gradle to build the jni
folder as before, and sourceSets.main.jni.srcDirs = []
just does this job!
Notice 2: The code is not exactly the same with Gaku Ueda''s code: tasks.withType(Compile)
to tasks.withType(JavaCompile)
, because Compile
is deprecated.
Notice 3: You can get $ndkDir
variable with project.plugins.findPlugin(''com.android.application'').getNdkFolder()
or you can define it in grade.properties
file under Project root, so you need to add ndkDir=path/to/your/ndk
in that file, if the file is not created, simply create a new one.
android{
...
sourceSets.main.jni.srcDirs = []
task ndkBuild(type: Exec, description: ''Compile JNI source via NDK'') {
ndkDir = project.plugins.findPlugin(''com.android.application'').getNdkFolder()
commandLine "$ndkDir/ndk-build",
''NDK_PROJECT_PATH=build'',
''APP_BUILD_SCRIPT=src/main/jni/Android.mk'',
''NDK_APPLICATION_MK=src/main/jni/Application.mk''
}
task ndkLibsToJar(type: Zip, dependsOn: ''ndkBuild'', description: ''Create a JAR of the native libs'') {
destinationDir new File(buildDir, ''libs'')
baseName ''ndk-libs''
extension ''jar''
from(new File(buildDir, ''libs'')) { include ''**/*.so'' }
into ''lib/''
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkLibsToJar
}
...
}
dependencies {
compile fileTree(dir: ''libs'', include: [''*.jar''])
// add begin
compile fileTree(dir: new File(buildDir, ''libs''), include: ''*.jar'')
// add end
}
But we can still do a little improvements here. We have already know that Gradle will take jniLibs
folder as its default native libraries folder, so we can simply output the libs/<abi>/*.so
files generated by ndk-build
command into jniLibs
folder, so there''s no need to zip these *.so
files into a jar
file.
The final build.gradle
file under app
module
apply plugin: ''com.android.application''
android {
compileSdkVersion 20
buildToolsVersion "20.0.0"
defaultConfig {
applicationId "com.android.hacks.ndkdemo"
minSdkVersion 16
targetSdkVersion 20
versionCode 1
versionName "1.0"
}
// add begin
sourceSets.main.jni.srcDirs = []
task ndkBuild(type: Exec, description: ''Compile JNI source via NDK'') {
ndkDir = project.plugins.findPlugin(''com.android.application'').getNdkFolder()
commandLine "$ndkDir/ndk-build",
''NDK_PROJECT_PATH=build/intermediates/ndk'',
''NDK_LIBS_OUT=src/main/jniLibs'',
''APP_BUILD_SCRIPT=src/main/jni/Android.mk'',
''NDK_APPLICATION_MK=src/main/jni/Application.mk''
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
// add end
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile(''proguard-android.txt''), ''proguard-rules.pro''
}
}
}
dependencies {
compile fileTree(dir: ''libs'', include: [''*.jar''])
}
So simple, right? ''NDK_LIBS_OUT=src/main/jniLibs''
helps us do the right job!
For testing, you can also add some lines relating with OpenCV in your Android.mk
file and some line in your main.c
to check whether everything is readlly working. For example, add #include <opencv2/core/core.hpp>
in main.c
file, and change Android.mk
to
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#opencv
OPENCVROOT:= /Volumes/hujiawei/Users/hujiawei/Android/opencv_sdk
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
OPENCV_LIB_TYPE:=SHARED
include ${OPENCVROOT}/sdk/native/jni/OpenCV.mk
LOCAL_SRC_FILES := main.c
LOCAL_LDLIBS += -llog
LOCAL_MODULE := hello
include $(BUILD_SHARED_LIBRARY)
In Gradle Console window, you can see these similar lines
*.so
files relating with OpenCV has been packaged into the final APK
One More Thing
Of course, maybe you don''t want to change your build.grale
file with that much code, and Of course, you also don''t want to run ndk-build
outside the IDE, then copy the <abi>/*.so
files into jniLibs
folder each time you want to rebuild the native codes!
At last, I came out another nicer solution, if you like, that is to create a ndk-build
external tool in Android Studio, and every time you want to rebuild the native codes, simply run the external tool, then it automatically generates the libs/<abi>/*.so
files into jniLibs
folder, so everything is ready to run this app, :-)
The configuration is simple
Parameters: NDK_PROJECT_PATH=$ModuleFileDir$/build/intermediates/ndk NDK_LIBS_OUT=$ModuleFileDir$/src/main/jniLibs NDK_APPLICATION_MK=$ModuleFileDir$/src/main/jni/Application.mk APP_BUILD_SCRIPT=$ModuleFileDir$/src/main/jni/Android.mk V=1
OK, I hope it is helpful. Let me know if it is really helpful, or tell me what''s your problem. :-)
关于Android SensorEvent时间戳常量和android 获取时间戳的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于(OK) android-5.0 sensor 工作原理 —sensorservice 的启动 (一)、(OK) android-5.0 sensor工作原理—sensorservice的启动(二)、Android Firebase addListenerForSingleValueEvent不起作用、Android NDK and OpenCV Development With Android Studio的相关知识,请在本站寻找。
本文标签: