android调试+及JNI相关

来源:互联网 发布:淘宝店铺客服链接获取 编辑:程序博客网 时间:2024/05/18 02:36
原址

1.adb的使用

 ./adb remount  //使得文件系统可读写 

./adb push 8188eu.ko /system/lib/modules  传输文件

调试真机:

需要知道usb的厂商和产品ID,然后添加udev规则(否则,linux中可能识别不到真机的USB)。可以利用lsusb查看VID

  1. SUBSYSTEM=="usb", SYSFS{idVendor}=="04e8", MODE="0666" 
  2. /etc/udev/rules.d/53-android.rules 

1.1 查看设备(包括模拟设备和真实设备)

  1.  adb devices​

1.2 进入目标板的shell

  1. adb shell 

    如果存在多个设备,需要以下命令 

  1. adb shell -+ 设备名​

    进入shell之后,可以使用dmesg查看内核打印信息。

    这点在权限遇到问题时非常有用,内核打印可以直接看到gid和uid的问题。(笔者对内核底层比较熟悉,目前不清楚JAVA层会有什么相关提示)。例如:

  1. qtaguid: ctrl_counterset(1 10106): insufficient priv from pid=3318 tgid=3260 uid=1000
  2. <6>[16058.466766]  qtaguid: ctrl_counterset(0 10085): insufficient priv from pid=3318 tgid=3260 uid=1000

2.Android Studio 调试

    笔者使用的2.1.2,集成的编译环调试相当方便。

    在Monitor中集成了logcat,而且直接过滤出调试app的LOG。

 

3.JNI相关配置

 3.1 下载NDK

    建议直接下载,然后在Android Studio中直接配置。

    配置的方式为:在local.properties中添加NDK的路径 

  1. ndk.dir=D\:\\Android\\android-sdk\\android-ndk-r11c-windows-x86_64\\android-ndk-r11c​

3.2 build.gradle的defaultConfig配置添加

  1.  ndk{
  2.             moduleName "YanboberJniLibName"
  3.             ldLibs "log", "z", "m"
  4.             abiFilters "armeabi", "armeabi-v7a", "x86"
  5.         }

     moduleName是库文件的名字(不含.so或dll后缀)

     jni的目录结构:

3.3 JNI接口的声明和定义

3.3.1 C 中的声明

  1. #ifdef __cplusplus
  2. extern "C" {
  3. #endif
  4.  
  5. JNIEXPORT jboolean JNICALL native_sudo(JNIEnv *env, jobject obj, jstring xmd);
  6. JNIEXPORT jboolean JNICALL native_exit(JNIEnv *env, jobject obj);
  7. #ifdef __cplusplus
  8. }
  9. #endif

     JNIEnv 从linux的本质上来讲是本地线程的环境,JNIEnv只在当前线程中有效。指针不能从一个线程进入另一个线程,但可以在不同的线程中调用本地方法(这时,所传递的env是不同的)

    总结下:JNIEnv是与线程相关的变量,不同线程的JNIEnv彼此独立。而JavaVM是虚拟机的JNI层的代表,在虚拟机进程中只有一个JavaVM,所以该进程的所有线程都可以使用这个JavaVM。

    简单来讲,就是库函数的作用,具有良好可重载性。上一张图片就简单明了了:

3.3.2 使用JNINativeMethod注册到线程环境

JNINativeMethod定义如下:

  1. static JNINativeMethod methods[] = {
  2.       {"sudo","(Ljava/lang/String;)Z",(void*)native_sudo},
  3.       {"nexit","()Z",(void*)native_exit},
  4. };

    native_sudo是jni层定义的函数。

    sudo是JAVA层调用的函数。

    (Ljava/lang/String;)Z是函数传递参数和返回参数,多个参数使用";"进行分割。括号里的是形参,括号外的是返回值

    这里的参数表示,后续作下补充。

    注:该结构体,第二个参数的分号漏掉,会导致无法找到JNI函数。

 

    注册的过程直接上代码了,这里可以直接套用:

  1. static int registerNativeMethods(JNIEnv* env , const char* className , JNINativeMethod* gMethods, int numMethods)
  2. {
  3.     jclass clazz;
  4.     clazz = (*env)->FindClass(env, className);
  5.     if (clazz == NULL)
  6.     {
  7.         return JNI_FALSE;
  8.     }
  9.  
  10.     if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0)
  11.     {
  12.         return JNI_FALSE;
  13.     }
  14.  
  15.     return JNI_TRUE;
  16. }
  17.  
  18. //自定义函数
  19. static int registerNatives(JNIEnv* env)
  20. {
  21.     const char* kClassName = "io/github/yanbober/ndkapplication/NdkJniUtils";//指定要注册的类
  22.     return registerNativeMethods(env, kClassName, methods,  sizeof(methods) / sizeof(methods[0]));
  23. }
  24.  
  25. JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
  26. {
  27.     LOGD("customer---------------------------JNI_OnLoad-----into.\n");
  28.     JNIEnv* env = NULL;
  29.     jint result = -1;
  30.  
  31.     //test();
  32.  
  33.     if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK)
  34.     {
  35.         return -1;
  36.     }
  37.     assert(env != NULL);
  38.  
  39.     if (!registerNatives(env))
  40.     {
  41.         return -1;
  42.     }
  43.  
  44.     return JNI_VERSION_1_4;
  45. }

 3.3.3 JAVA层的定义

  1. public class NdkJniUtils {
  2.  
  3.     public native boolean sudo(String cmd);
  4.     public native boolean nexit();
  5.  
  6.     static {
  7.         System.loadLibrary("YanboberJniLibName");
  8.     }
  9. }

     这里使用了类封装。使用时,New一个NdkJniUtils即可调用JNI函数。

     函数使用native声明之后,JAVA链接器会认为这个函数时外部函数,因此没有在JNI层作相关定义,JAVA项目工程也是可以编译通过的。

     注:loadLibrary需要与之前build.gradle 中ndk字段声明的名字相同。

3.3.4 不使用JNINativeMethod的方法

 不使用JNINativeMethod的方法时,函数名定义的名称必须按照规定的格式,相对比较复杂。个人并不喜欢这样定义。

  以前作的笔记,直接copy了(需要使用javah工具):

 确认javah生成头文件的工具能否使用。如果不能,需要确认环境变量。

  使用方法:

  在声明好native函数之后,进行build。

 然后以build好的class,利用javah生成头文件,格式如下

  1. javah -[dest dir] -classpath [debug dir] [class name]

-d: 头文件存放的目录。个人选择main下的目录(\helloworld\app\src\main\java),最好创建新目录,用于存放native定义(如jni目录),这样可以在项目路径上看到。

-classpath : 类存放的路径;(\helloworld\app\build\intermediates\classes\debug)

[classname]:及之前声明了native函数的类

 

当然如果感觉比较麻烦,在debug目录使用相对路径创建.h文件,然后将文件复制到main目录。

 

然后,在-d的目录下创建.c文件,对native函数作具体的定义。

0 0