1 概述

尝试在App层直接读取驱动的Input Event,获取触屏事件(本文获取的是电磁笔触屏事件),而不通过Android的Input Framework.

2 架构

3 实现

3.1 JNI层

共有以下几个文件:

3.1.1 input_pen.h

首先看input_pen.h

[cpp] view plaincopy
  1. #ifndef _INPUT_PEN_H
  2. #define _INPUT_PEN_H
  3. #include <pthread.h>
  4. #include <linux/input.h>
  5. #include <sys/types.h>
  6. #include <linux/types.h>
  7. #ifdef _cplusplus
  8. extern "C" {
  9. #endif
  10. //获取input_event数据的方法指针,由App层提供
  11. typedef void (*get_event_callback)(__u16 type, __u16 code, __s32 value );
  12. //创建线程的函数指针,通过Java虚拟机创建
  13. typedef pthread_t (*create_thread_callback)(const char* name, void (*start)(void *), void* arg);
  14. //释放线程资源的函数指针
  15. typedef int (*detach_thread_callback)(void);
  16. //回调函数结构体
  17. typedef struct {
  18. get_event_callback get_event_cb;
  19. create_thread_callback create_thread_cb;
  20. detach_thread_callback detach_thread_cb;
  21. } input_callback;
  22. /*******************************************************************/
  23. //public methods
  24. //初始化函数
  25. unsigned char input_pen_init(input_callback *callback);
  26. //退出函数
  27. void input_pen_exit();
  28. /*******************************************************************/
  29. #ifdef _cplusplus
  30. }
  31. #endif
  32. #endif

3.1.2 input_pen.cpp

[cpp] view plaincopy
  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <string.h>
  6. #include <unistd.h>
  7. #include <fcntl.h>
  8. #include <sys/types.h>
  9. #include <sys/epoll.h>
  10. #include <sys/wait.h>
  11. #include <sys/un.h>
  12. #include <stddef.h>
  13. #include <linux/input.h>
  14. #include "input_pen.h"
  15. #include "debug.h"
  16. //驱动路径
  17. #define DEV_PATH "/dev/input/event1"
  18. //最大侦听
  19. #define MAX_EVENTS 1
  20. //epoll_wait的最大时间
  21. #define EPOLL_SLEEP 200
  22. //线程是否由pthread创建,否则由JVM创建
  23. #define USING_PTHREAD 0
  24. #if USING_PTHREAD
  25. #define LOGD(fmt, arg...) do{printf(fmt"\n", ##arg);}while(0)
  26. #define LOGE LOGD
  27. #endif
  28. /**************************************************************************************/
  29. //static variable
  30. //当前驱动文件指针
  31. static int fd = 0;
  32. //epoll文件指针
  33. static int epoll_fd = 0;
  34. //epoll_event数组
  35. static struct epoll_event events[MAX_EVENTS];
  36. //回调函数
  37. static input_callback *callbacks = NULL;
  38. //标记线程是否已启动
  39. static unsigned char start_flag = 0;
  40. //标记线程是否已退出
  41. static unsigned char exit_flag = 0;
  42. //线程锁
  43. static pthread_mutex_t exit_mutex;
  44. /**************************************************************************************/
  45. //set non-blocking for fd
  46. static int set_non_blocking(int fd) {
  47. int opts;
  48. opts = fcntl(fd, F_GETFL);
  49. if (opts < 0) {
  50. LOGE("fcntl F_GETFL error: %s", strerror(errno));
  51. return -1;
  52. }
  53. opts = (opts | O_NONBLOCK);
  54. if (fcntl(fd, F_SETFL, opts) < 0) {
  55. LOGE("fcntl F_SETFL error: %s", strerror(errno));
  56. return -1;
  57. }
  58. return 0;
  59. }
  60. //register epoll events for fd
  61. static void epoll_register( int  epoll_fd, int  fd ) {
  62. struct epoll_event  ev;
  63. int         ret;
  64. ev.events  = EPOLLIN;//interested in receiving data
  65. ev.data.fd = fd;
  66. do {
  67. //register events for fd
  68. ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev );
  69. } while (ret < 0 && errno == EINTR);
  70. }
  71. //remove epoll events for fd
  72. static void epoll_unregister( int  epoll_fd, int  fd ) {
  73. int  ret;
  74. do {
  75. ret = epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL );
  76. } while (ret < 0 && errno == EINTR);
  77. }
  78. //通知退出线程,由其他线程调用
  79. static void thread_cancel() {
  80. LOGD("thread_cancel");
  81. pthread_mutex_lock(&exit_mutex);
  82. exit_flag = 1;
  83. pthread_mutex_unlock(&exit_mutex);
  84. }
  85. //停止线程,由本线程调用
  86. static void thread_exit() {
  87. unsigned char flag ;
  88. pthread_mutex_lock(&exit_mutex);
  89. flag = exit_flag;
  90. pthread_mutex_unlock(&exit_mutex);
  91. if (flag == 1) {
  92. LOGD("thread_exit");
  93. //close devices
  94. close(fd);
  95. //clean variablies
  96. fd = 0;
  97. epoll_fd = 0;
  98. start_flag = 0;
  99. exit_flag = 0;
  100. //release thread resources
  101. if (callbacks != NULL && callbacks->detach_thread_cb != NULL) {
  102. callbacks->detach_thread_cb();
  103. LOGD("callbacks->detach_thread_cb();\n");
  104. }
  105. //exit current thread
  106. pthread_exit(NULL);
  107. }
  108. }
  109. //线程运行函数
  110. #if USING_PTHREAD
  111. static void *run(void *args) {
  112. #else
  113. static void run(void *args) {
  114. #endif
  115. int n = 0;
  116. int i = 0;
  117. int res;
  118. struct input_event event;
  119. LOGD("run...");
  120. while (1) {
  121. thread_exit();//每次检测是否要退出运行
  122. n = epoll_wait(epoll_fd, events, MAX_EVENTS, EPOLL_SLEEP);//检测是否有事件发生
  123. if (n == -1) {
  124. LOGE("epoll_wait error:%s", strerror(errno));
  125. continue;
  126. }
  127. for (i = 0; i < n; i++) {
  128. if (events[i].data.fd == fd) { //有读事件发生
  129. res = read(fd, &event, sizeof(event));
  130. if (res < (int)sizeof(event)) {
  131. LOGE("could not get event\n");
  132. continue;
  133. }
  134. #if (!USING_PTHREAD)
  135. //把input_event的数据回调到java层
  136. if (callbacks != NULL && callbacks->get_event_cb != NULL) {
  137. callbacks->get_event_cb(event.type, event.code, event.value);
  138. }
  139. #else
  140. //printf("[%8ld.%06ld] ", event.time.tv_sec, event.time.tv_usec);
  141. if (event.type == EV_ABS) {
  142. printf("%04x %04x %08x\n", event.type, event.code, event.value);
  143. }
  144. #endif
  145. }
  146. }
  147. }
  148. #if USING_PTHREAD
  149. return NULL;
  150. #else
  151. return ;
  152. #endif
  153. }
  154. //初始化函数
  155. unsigned char input_pen_init(input_callback *cb) {
  156. pthread_t thread;
  157. LOGD("input_pen_init");
  158. if (start_flag) {
  159. return 1;
  160. }
  161. //callbacks
  162. callbacks = cb;
  163. //open device
  164. fd = open(DEV_PATH, O_RDWR);
  165. if (fd < 0) {
  166. LOGE("open device failed!\n");
  167. return 0;
  168. }
  169. //create epoll
  170. epoll_fd = epoll_create(MAX_EVENTS);
  171. if (epoll_fd == -1) {
  172. LOGE("epoll_create failed!\n");
  173. return 0;
  174. }
  175. //set non-blocking
  176. set_non_blocking(fd);
  177. //epoll register
  178. epoll_register(epoll_fd, fd);
  179. //mutex
  180. if (pthread_mutex_init(&exit_mutex, NULL) != 0) {
  181. LOGE("pthread_mutex_initn failed!");
  182. return 0;
  183. }
  184. //create thread
  185. #if USING_PTHREAD
  186. if (pthread_create(&thread, NULL, run, (void *)NULL) != 0) {
  187. LOGE("pthread_create failed!\n");
  188. return 0;
  189. }
  190. #else
  191. if (callbacks != NULL && callbacks->create_thread_cb != NULL) {
  192. thread = callbacks->create_thread_cb("input_pen_thread", run, NULL);
  193. if (thread == 0) {
  194. LOGE("create thread failed!\n");
  195. return 0;
  196. }
  197. start_flag = 1;
  198. LOGD("input_pen_init success!");
  199. return 1;
  200. }
  201. #endif
  202. return 0;
  203. }
  204. //退出函数
  205. void input_pen_exit() {
  206. thread_cancel();
  207. }
  208. #if USING_PTHREAD
  209. int main() {
  210. int count = 0;
  211. input_pen_init(NULL);
  212. while (1) {
  213. sleep(1);
  214. count ++;
  215. if (count == 20) {
  216. thread_cancel();
  217. sleep(1);
  218. break;
  219. }
  220. }
  221. return 0;
  222. }
  223. #endif

以上的关键流程为:

1、open驱动文件,初始化epoll,创建线程。

2、在线程中通过epoll_wait检测事件,有事件发生则通过read函数读取驱动的input_event数据,并通过get_event_cb回调到Jav层。

3.1.3 com_jiagutech_input_InputPen.cpp

[cpp] view plaincopy
  1. #include <stdlib.h>
  2. #include <malloc.h>
  3. #include <jni.h>
  4. #include <JNIHelp.h>
  5. #include <utils/Log.h>
  6. #include "android_runtime/AndroidRuntime.h"
  7. #include "input_pen.h"
  8. #include "debug.h"
  9. //Java类名
  10. #define CLASS_NAME "com/jiagutech/input/InputPen"
  11. using namespace android;
  12. static jobject mCallbacksObj = NULL;
  13. static jmethodID method_get_event;
  14. static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
  15. if (env->ExceptionCheck()) {
  16. LOGE("An exception was thrown by callback '%s'.", methodName);
  17. LOGE_EX(env);
  18. env->ExceptionClear();
  19. }
  20. }
  21. //获得input_event数据的回调函数
  22. static void GetEventCallback(__u16 type, __u16 code, __s32 value) {
  23. JNIEnv* env = AndroidRuntime::getJNIEnv();
  24. //invoke java callback method
  25. env->CallVoidMethod(mCallbacksObj, method_get_event, type, code, value);
  26. checkAndClearExceptionFromCallback(env, __FUNCTION__);
  27. }
  28. //创建线程的回调函数
  29. static pthread_t CreateThreadCallback(const char* name, void (*start)(void *), void* arg) {
  30. return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);
  31. }
  32. //释放线程资源的回调函数
  33. static int DetachThreadCallback(void) {
  34. JavaVM* vm;
  35. jint result;
  36. vm = AndroidRuntime::getJavaVM();
  37. if (vm == NULL) {
  38. LOGE("detach_thread_callback :getJavaVM failed\n");
  39. return -1;
  40. }
  41. result = vm->DetachCurrentThread();
  42. if (result != JNI_OK)
  43. LOGE("ERROR: thread detach failed\n");
  44. return result;
  45. }
  46. //回调函数结构体变量
  47. static input_callback mCallbacks = {
  48. GetEventCallback,
  49. CreateThreadCallback,
  50. DetachThreadCallback,
  51. };
  52. //初始化Java的回调函数
  53. static void jni_class_init_native
  54. (JNIEnv* env, jclass clazz) {
  55. LOGD("jni_class_init_native");
  56. method_get_event = env->GetMethodID(clazz, "getEvent", "(III)V");
  57. }
  58. //初始化
  59. static jboolean jni_input_pen_init
  60. (JNIEnv *env, jobject obj) {
  61. LOGD("jni_input_pen_init");
  62. if (!mCallbacksObj)
  63. mCallbacksObj = env->NewGlobalRef(obj);
  64. return  input_pen_init(&mCallbacks);
  65. }
  66. //退出
  67. static void jni_input_pen_exit
  68. (JNIEnv *env, jobject obj) {
  69. LOGD("jni_input_pen_exit");
  70. input_pen_exit();
  71. }
  72. static const JNINativeMethod gMethods[] = {
  73. { "class_init_native","()V", (void *)jni_class_init_native },
  74. { "native_input_pen_init","()Z", (void *)jni_input_pen_init },
  75. { "native_input_pen_exit","()V", (void *)jni_input_pen_exit },
  76. };
  77. static int registerMethods(JNIEnv* env) {
  78. const char* const kClassName = CLASS_NAME;
  79. jclass clazz;
  80. /* look up the class */
  81. clazz = env->FindClass(kClassName);
  82. if (clazz == NULL) {
  83. LOGE("Can't find class %s/n", kClassName);
  84. return -1;
  85. }
  86. /* register all the methods */
  87. if (env->RegisterNatives(clazz,gMethods,sizeof(gMethods)/sizeof(gMethods[0])) != JNI_OK) {
  88. LOGE("Failed registering methods for %s/n", kClassName);
  89. return -1;
  90. }
  91. /* fill out the rest of the ID cache */
  92. return 0;
  93. }
  94. jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  95. JNIEnv* env = NULL;
  96. jint result = -1;
  97. LOGI("InputPen JNI_OnLoad");
  98. if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
  99. LOGE("ERROR: GetEnv failed/n");
  100. goto fail;
  101. }
  102. if (env == NULL) {
  103. goto fail;
  104. }
  105. if (registerMethods(env) != 0) {
  106. LOGE("ERROR: PlatformLibrary native registration failed/n");
  107. goto fail;
  108. }
  109. /* success -- return valid version number */
  110. result = JNI_VERSION_1_4;
  111. fail:
  112. return result;
  113. }

3.1.4 Android.mk

[python] view plaincopy
  1. LOCAL_PATH:= $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_SRC_FILES:= \
  4. input_pen.cpp \
  5. com_jiagutech_input_InputPen.cpp
  6. LOCAL_MODULE_TAGS := optional
  7. LOCAL_SHARED_LIBRARIES := liblog libcutils libandroid_runtime libnativehelper
  8. LOCAL_PRELINK_MODULE := false
  9. LOCAL_MODULE:= libinput_pen
  10. include $(BUILD_SHARED_LIBRARY)
  11. #LOCAL_MODULE:=inputpen
  12. #include $(BUILD_EXECUTABLE)

1.1.1 Debug.h

[cpp] view plaincopy
  1. #ifndef _DEBUG_H
  2. #define _DEBUG_H
  3. #include <utils/Log.h>
  4. #ifdef ALOGD
  5. #define LOGD      ALOGD
  6. #endif
  7. #ifdef ALOGV
  8. #define LOGV      ALOGV
  9. #endif
  10. #ifdef ALOGE
  11. #define LOGE      ALOGE
  12. #endif
  13. #ifdef ALOGI
  14. #define LOGI      ALOGI
  15. #endif
  16. #define LOG_TAG "InputPen"
  17. #endif

3.2 App层

共有两个文件:

PenEvent.Java

InputPen.java

3.2.1 PenEvent.java

[java] view plaincopy
  1. package com.jiagutech.input;
  2. public class PenEvent {
  3. public PenEvent() {
  4. }
  5. public final static int ACTION_DOWN = 0x0;
  6. public final static int ACTION_UP = 0x1;
  7. public final static int ACTION_MOVE = 0x2;
  8. //表示事件类型,down/up/move
  9. private int action;
  10. //x轴坐标
  11. private float x;
  12. //y轴坐标
  13. private float y;
  14. //压力数据
  15. private float pressure;
  16. public void setAction(int action) {
  17. this.action = action;
  18. }
  19. public int getAction() {
  20. return action;
  21. }
  22. public void setX(float x) {
  23. this.x = x;
  24. }
  25. public float getX() {
  26. return x;
  27. }
  28. public void setY(float y) {
  29. this.y = y;
  30. }
  31. public float getY() {
  32. return y;
  33. }
  34. public void setPressure(float p) {
  35. this.pressure = p;
  36. }
  37. public float getPressure() {
  38. return pressure;
  39. }
  40. }

3.2.2 InputPen.java

[java] view plaincopy
  1. package com.jiagutech.input;
  2. import java.util.LinkedList;
  3. import android.os.Handler;
  4. public class InputPen {
  5. /**********************************************************************************/
  6. private static InputPen instance = null;
  7. private boolean mStarted = false;
  8. private final static String TAG = "InputPen";
  9. //主线程Handler
  10. private Handler mHandler = null;
  11. //PenEvent列表
  12. private LinkedList<PenEvent> mEventList = new LinkedList<PenEvent>();
  13. //锁
  14. private Object mListLock = new Object();
  15. /*******************************************************************/
  16. //以下定义请参考input_event.h文件
  17. private final static int EV_SYN = 0x0;
  18. private final static int EV_KEY = 0x01;
  19. private final static int EV_ABS = 0x03;
  20. private final static int  ABS_X = 0x00;
  21. private final static int  ABS_Y = 0x01;
  22. private final static int  ABS_PRESSURE = 0x18;
  23. private final static int  BTN_TOUCH = 0x14a;
  24. private final static int  BTN_TOOL_PEN = 0x140;
  25. private final static int  BTN_STYLUS = 0x14b;
  26. /*******************************************************************/
  27. //原始的x最大分辨率
  28. private static final float MAX_X = 15360.0f;
  29. //屏幕x最大分辨率
  30. private static final float MAX_X_STANDARD = 1280.0f;
  31. //原始的y最大分辨率
  32. private static final float MAX_Y = 9600.0f;
  33. //屏幕y最大分辨率
  34. private static final float MAX_Y_STANDARD = 800.0f;
  35. //原始的最大压力数据
  36. private static final float MAX_PRESSURE = 1023.0f;
  37. //Android标准最大压力数据
  38. private static final float MAX_PRESSURE_STANDARD= 1.0f;
  39. private int _x=-1,_y=-1,_pressure=-1;
  40. private int _bintouch = 0, _lastBinTouch = 0;
  41. //x轴转换系数
  42. private float xScale = MAX_X_STANDARD / MAX_X;
  43. //y轴转换系数
  44. private float yScale = MAX_Y_STANDARD / MAX_Y;
  45. //压力值转换系统
  46. private float pScale = MAX_PRESSURE_STANDARD / MAX_PRESSURE;
  47. //y轴便宜
  48. private float yOffset = 73.0f;
  49. /**
  50. * 加载libinput_pen.so,并初始化回调函数
  51. */
  52. static {
  53. try {
  54. System.loadLibrary("input_pen");
  55. class_init_native();
  56. } catch (UnsatisfiedLinkError e) {
  57. e.printStackTrace();
  58. }
  59. }
  60. private InputPen() {
  61. }
  62. /**
  63. * 单例模式
  64. * @return
  65. */
  66. public static synchronized InputPen getInstance() {
  67. if (instance == null) {
  68. instance = new InputPen();
  69. }
  70. return instance;
  71. }
  72. /**
  73. * 通知主线程获取PenEvent进行处理
  74. *
  75. */
  76. private void onPenTouch() {
  77. if (mHandler != null) {
  78. mHandler.sendEmptyMessage(0);
  79. }
  80. }
  81. /**
  82. * 设置主线程handler
  83. * @param handler
  84. */
  85. public void setHandler(Handler handler) {
  86. mHandler = handler;
  87. }
  88. /**
  89. * 添加PenEvent到list
  90. * @param event
  91. */
  92. private void addPenEvent(PenEvent event) {
  93. synchronized (mListLock) {
  94. mEventList.add(event);
  95. }
  96. }
  97. /**
  98. * 从list获取最旧的PenEvent
  99. * @return
  100. */
  101. public PenEvent getPenEvent() {
  102. PenEvent event = null;
  103. synchronized (mListLock) {
  104. if (mEventList.size() > 0) {
  105. event = mEventList.removeFirst();
  106. }
  107. }
  108. return event;
  109. }
  110. /*******************************************************************/
  111. //public method
  112. /**
  113. * 坐标转换,并生成PenEvent数据
  114. * @param event
  115. */
  116. protected void transform(PenEvent event) {
  117. float x = MAX_Y_STANDARD - ((float)_y) * yScale;
  118. float y = (float)_x * xScale - yOffset;
  119. float p = (float)_pressure * pScale;
  120. event.setX(x);
  121. event.setY(y);
  122. event.setPressure(p);
  123. }
  124. /**
  125. * 处理input_event数据
  126. */
  127. protected void processEvent() {
  128. if (_bintouch != _lastBinTouch ) {
  129. _lastBinTouch = _bintouch;
  130. //Log.d(TAG, String.format("x=%d,y=%d,pressure=%d,bintouch=%d", _x,_y,_pressure,_bintouch));
  131. if (_bintouch == 1) { //down事件
  132. PenEvent event = new PenEvent();
  133. event.setAction(PenEvent.ACTION_DOWN);
  134. transform(event);
  135. addPenEvent(event);
  136. onPenTouch();
  137. } else { //up事件
  138. PenEvent event = new PenEvent();
  139. event.setAction(PenEvent.ACTION_UP);
  140. transform(event);
  141. addPenEvent(event);
  142. onPenTouch();
  143. }
  144. } else if (_bintouch == 1) { //move事件
  145. PenEvent event = new PenEvent();
  146. event.setAction(PenEvent.ACTION_MOVE);
  147. transform(event);
  148. addPenEvent(event);
  149. onPenTouch();
  150. }
  151. }
  152. /**
  153. * 获取input_event数据,由jni层调用此函数
  154. * @param type
  155. * @param code
  156. * @param value
  157. */
  158. protected void getEvent(int type, int code, int value) {
  159. switch (type) {
  160. case EV_SYN:
  161. processEvent();
  162. break;
  163. case EV_KEY:
  164. if (code == BTN_TOUCH) {
  165. _bintouch = value;
  166. }
  167. break;
  168. case EV_ABS:
  169. if (code == ABS_X) {
  170. _x = value;
  171. } else if (code == ABS_Y) {
  172. _y = value;
  173. } else if (code == ABS_PRESSURE) {
  174. _pressure = value;
  175. }
  176. break;
  177. default:
  178. break;
  179. }
  180. }
  181. /**
  182. * 启动线程
  183. */
  184. protected void start() {
  185. if (!mStarted) {
  186. if (native_input_pen_init()) {
  187. mStarted = true;
  188. }
  189. }
  190. }
  191. /**
  192. * 停止线程
  193. */
  194. protected void stop() {
  195. if (mStarted) {
  196. native_input_pen_exit();
  197. mStarted = false;
  198. }
  199. }
  200. public void dispose() {
  201. stop();
  202. }
  203. @Override
  204. protected void finalize() throws Throwable {
  205. stop();
  206. // TODO Auto-generated method stub
  207. super.finalize();
  208. }
  209. /*******************************************************************/
  210. //native method
  211. protected  static native void class_init_native();
  212. protected  native boolean native_input_pen_init();
  213. protected  native void native_input_pen_exit();
  214. }

3.2.3 Activity

Activity可以通过调用InputPen的start函数,启动读取线程,再调用setHandler设置Handler,从而就可在Handler中处理PenEvent数据了。

Android App层通过JNI从驱动获取Input Event相关推荐

  1. android jni framework,Android Framework层的JNI机制(二)

    Java框架层中有很多地方使用JNI机制,每一个部分的框架层代码,都可能有与之对应的JNI库.先了解Java框架层的组成,继续看一下JNI在框架层中的使用. Java框架层的组成 Java框架层的实体 ...

  2. 【Android App】利用腾讯地图获取地点信息和规划导航线路讲解及实战(附源码和演示视频 超详细必看)

    需要源码请点赞关注收藏后评论区留言~~~ 一.获取地点信息 至于如何集成腾讯地图和调用腾讯地图接口此处不再赘述,有需要请参见我之前的博客 腾讯地图用来搜索POI地点的工具是TencentSearch, ...

  3. Android App层 单独使用SystemProperties

    单独拷贝成 SystemProperties.java 即可享用. import java.lang.reflect.Method;public class SystemProperties {pub ...

  4. Android APP层 ShellUtils

    拷贝成 ShellUtils.java 即可享用. import java.io.BufferedReader; import java.io.DataOutputStream; import jav ...

  5. Android 底层驱动开发步骤——linux内核层、HAL层、JNI层

    1.Linux驱动实现 2.Linux内核驱动测试 3.Android HAL层实现 4.Aidl实现 5.Service java实现 6.Service jni 实现 7.注册service和jn ...

  6. android l camera no panorama,Android Camera从App层到framework层到HAL层的初始化过程

    Android camera 从上到下能够分为四个部分: Application层. framework层. HAL(hardware abstract layer)层. Kernel层 通常面向开发 ...

  7. 编译FFmpeg4.1.3并移植到Android app中使用(最详细的FFmpeg-Android编译教程)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/bobcat_kay/article/d ...

  8. android 动态库获取路径问题,一种Android App在Native层动态加载so库的方案

    这篇文章通过实战案例,介绍了一种有条理的组织Native层代码层级结构的方法.并且,在良好的代码层级.作用分工的基础上,实现了动态的按需加载.卸载so库.文章的最后,还介绍了实践过程中遇到的困难以及对 ...

  9. android系统底层驱动多个物理按键上报同一个键值给app层,app层如何区分

    如果设备有多个按键上报同一个键值给app层,app通过getScanCode()可以区分是哪个物理按键,得到的值就是linux驱动层的扫描码

最新文章

  1. Android NDK JNI C++ 13 pthread多线程
  2. 一文了解 Apache Flink 核心技术
  3. 抓包工具Charles使用技巧
  4. python保存变量_Python变量存储
  5. 人脸对齐(二十)--PRN
  6. Linux内存管理:Linux 可加载内核模块剖析:2.6 内核透视图
  7. ACL2021 | 一种巧妙解决NER覆盖和不连续问题的方法
  8. 幂的后三位相同 详解(C++)
  9. Maya: Motion Graphics Workflow with MASH Maya教程:运动图形工作流程与MASH Lynda课程中文字幕
  10. 真无线蓝牙耳机,享受高品质杜比音效
  11. java连接点菜基站_基于JAVA的电信基站接口调用代码实例
  12. Fluent Mybatis 牛逼!
  13. 分布式计算原理之分布式协调与同步(1)——分布式选举
  14. linux 有线链接树莓派,linux-通过公共互联网连接到树莓派
  15. 智能穿戴设备的“最强心脏”
  16. 社交返利APP正在被返利机器人,普通返利APP集体围攻绞杀
  17. 微信JSAPI支付对接流程及支付接口设计
  18. 【大学生项目与竞赛】2021年全国大学生电子设计大赛 (二)模块储备
  19. 两车相撞的力怎么计算_两车正面碰撞事故车辆行驶速度计算方法
  20. 机器学习4. 贝叶斯

热门文章

  1. 存储过程传递参数时出现类型转换错误!如:varchar转换为int时出错
  2. Shader 学习笔记 ---Depth of Field 介绍
  3. CodeMirror 多功能在线代码编辑器
  4. mfc 如何将cstring转byte_如何将PDF转成JPG?PDF转图片的技巧
  5. linux虚拟化技术 教程,Linux上实现虚拟化技术的优势
  6. flink 异步io mysql 缓存_Flink用于外部数据访问的异步I/O
  7. [数据结构]P2.1 二叉搜索树
  8. laravel 运用
  9. 基于最大堆实现最大优先队列【代码】
  10. Java 中的array数组总结之一