jni介绍

NDK技巧

  1. 加快ndk-build编译速度 NDK编译时加上-j参数,如:
    1
    ndk-build -j4 # -j4,让make最多允许4个编译命令同时执行

测试后编译速度至少可以提高一倍

native崩溃分析

定位crash错误位置

首先我们要先把Logcat里的show only selected application选项改成No Filters, 这时就能看到系统打印出的DEBUG信息. 在DEBUG信息里找到backtrace, 这段就是系统给出的造成崩溃的信息, 但仅仅给出了是哪个函数, 而没有准确给出是那一行代码造成的崩溃. 记下#00 pc 00013122(红框3)这个信息, 这个信息就是用来定位崩溃代码的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
12-27 10:45:41.580 6189-6761/com.wodekouwei.demo E/HwDecodeWrapper: dequeueOutputBuffer = -1
12-27 10:45:41.580 6189-6761/com.wodekouwei.demo E/mediacodec: [oar_mediacodec_receive_frame():266]outbufidx:-1
12-27 10:45:41.590 6189-6761/com.wodekouwei.demo E/HwDecodeWrapper: dequeueOutputBuffer = -1
12-27 10:45:41.590 6189-6761/com.wodekouwei.demo E/mediacodec: [oar_mediacodec_receive_frame():266]outbufidx:-1
12-27 10:45:41.595 4901-4901/? I/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
12-27 10:45:41.595 4901-4901/? I/DEBUG: Build fingerprint: 'Huawei/H60-L03/hwH60:5.1.1/HDH60-L03/C01B535:user/release-keys'
12-27 10:45:41.595 4901-4901/? I/DEBUG: Revision: '0'
12-27 10:45:41.595 4901-4901/? I/DEBUG: ABI: 'arm'
12-27 10:45:41.595 4901-4901/? I/DEBUG: pid: 6189, tid: 6762, name: Thread-4510 >>> com.wodekouwei.demo <<<
12-27 10:45:41.595 4901-4901/? I/DEBUG: signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x428d738d
12-27 10:45:41.610 4901-4901/? I/DEBUG: r0 428d737d r1 b86eae00 r2 00000001 r3 00000000
12-27 10:45:41.610 4901-4901/? I/DEBUG: r4 b8836580 r5 b88365c0 r6 b8836580 r7 9e4e0d48
12-27 10:45:41.610 4901-4901/? I/DEBUG: r8 b8836588 r9 b8836588 sl b6da8871 fp 9e4e0dd0
12-27 10:45:41.610 4901-4901/? I/DEBUG: ip a1ca2c90 sp 9e4e0cc0 lr a1c358c7 pc a1c35b4c cpsr 200f0030
12-27 10:45:41.610 4901-4901/? I/DEBUG: backtrace:
12-27 10:45:41.610 4901-4901/? I/DEBUG: #00 pc 000ccb4c /data/app/com.wodekouwei.demo-1/lib/arm/liboarp-lib.so
12-27 10:45:41.610 4901-4901/? I/DEBUG: #01 pc 000cc8c3 /data/app/com.wodekouwei.demo-1/lib/arm/liboarp-lib.so (oar_player_gl_thread+214)
12-27 10:45:41.610 4901-4901/? I/DEBUG: #02 pc 0001688f /system/lib/libc.so (__pthread_start(void*)+30)
12-27 10:45:41.610 4901-4901/? I/DEBUG: #03 pc 000148a3 /system/lib/libc.so (__start_thread+6)
12-27 10:45:42.005 3655-3655/? E/Thermal-daemon: [ap] temp_new :34 temp_old :33
12-27 10:45:42.385 5083-5397/? E/WifiStateMachine: ConnectedState !CMD_RSSI_POLL 16 0 "wonxing-H3C" 3c:8c:40:e1:dd:b1 rssi=-50 f=2437 sc=100 link=72 tx=5.5, 0.0, 0.0 rx=1.0 bcn=0 [on:0 tx:0 rx:0 period:3001] from screen [on:0 period:-1780713098] gl hn u24 rssi=-45 ag=0 hr ticks 0,1,56 ls-=0 [56,56,60,60,65] brc=0 lrc=0
12-27 10:45:42.385 5083-5397/? E/WifiStateMachine: L2ConnectedState !CMD_RSSI_POLL 16 0 "wonxing-H3C" 3c:8c:40:e1:dd:b1 rssi=-50 f=2437 sc=100 link=72 tx=5.5, 0.0, 0.0 rx=1.0 bcn=0 [on:0 tx:0 rx:0 period:1] from screen [on:0 period:-1780713097] gl hn u24 rssi=-45 ag=0 hr ticks 0,1,56 ls-=0 [56,56,60,60,65] brc=0 lrc=0
12-27 10:45:42.390 5083-5397/? E/WifiStateMachine: fetchRssiLinkSpeedAndFrequencyNative rssi=-49 linkspeed=26 SSID="wonxing-H3C"

想要准确定位崩溃代码的地址的话我们就需要用到ndk工具包里的adrr2line这个工具了 首先先找到这个工具, linux系统这个工具在如下位置

1
/home/gavinandre/Documents/Android/android-sdk-linux/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-addr2line

在该目录下做个软链接后就能在任何目录使用这个命令了

1
sudo ln -s arm-linux-androideabi-addr2line /usr/local/bin/addr2line

找到造成崩溃的so文件地址, 在app目录下搜索so文件

1
2
3
4
5
6
7
find . -name "liboarp-lib.so"                                                                                           ✘
./app/build/intermediates/transforms/mergeJniLibs/debug/0/lib/armeabi-v7a/liboarp-lib.so
./app/build/intermediates/transforms/stripDebugSymbol/debug/0/lib/armeabi-v7a/liboarp-lib.so
./srsrtmpplayer/build/intermediates/cmake/debug/obj/armeabi-v7a/liboarp-lib.so
./srsrtmpplayer/build/intermediates/transforms/mergeJniLibs/debug/0/lib/armeabi-v7a/liboarp-lib.so
./srsrtmpplayer/build/intermediates/transforms/stripDebugSymbol/debug/0/lib/armeabi-v7a/liboarp-lib.so
./srsrtmpplayer/build/intermediates/intermediate-jars/debug/jni/armeabi-v7a/liboarp-lib.so

可以看到android studio编译后生成了许多so文件, 以我的经验正确的so文件一般是这个文件:./srsrtmpplayer/build/intermediates/transforms/mergeJniLibs/debug/0/lib/armeabi-v7a/liboarp-lib.so 然后就可以使用addrline命令了, 格式是addr2line -e 文件位置 崩溃地址:

1
addr2line -e ./srsrtmpplayer/build/intermediates/transforms/mergeJniLibs/debug/0/lib/armeabi-v7a/liboarp-lib.so 000ccb4c

如果so文件正确的话会打印如下信息,oar_player_gl_thread.c就是崩溃的cpp文件, 152, 然后检查下定位出来的位置是否在DEBUG信息里给出的函数里

1
oar_player_gl_thread.c:152

如果so文件错误的话会打印问号或者一个不对的位置, 这时就要换so文件多尝试了

C回调JAVA

  1. c中返回一个字符串

    1
    (*env)->NewStringUTF(env,"Huazi 华仔");
  2. c中返回一个数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .....................  
    int i = 0;
    jintArray array;
    array =(*env)->NewIntArray(env,8);
    for(;i<8;i++)

    // 赋值成 0 ~ 7
    (*env)->SetObjectArrayElement(env,array,i,i);
    }
    return array;
  3. c中使用调用传入的参数是数组array 是传入的数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    .........  
    int sum =0, i;
    int len = (*env)->GetArrayLength(env,array);
    jint *element =(*env)->GetIntArrayElement(env,array,0);
    for(i=0;i<len;i++)
    {
    sum+= *(element+i);
    }
    return sum;
  4. c中调用java中类的方法 没有参数 只有返回值String

    1
    2
    3
    4
    5
    6
    7
    //()Ljava/lang/String;" 表示参数为空 返回值是String类型  
    JNIEXPORT jstring JNICALLJava_com_huazi_Demo_getCallBack(JNIENV env,jobject object){
    jmethodID mid;
    jclass cls =(*env)->FindClass(env,"com/huazi/Demo"); //后面是包名+类名
    mid =(*env)->GetMethodID(env,cls,"TestMethod","()Ljava/lang/String;");//TestMethod java中的方法名
    jstring msg =(*env)->CallObjectMethod(env,object,mid); //object 注意下是jni传过来的jobject
    return msg;
  5. c中调用java中类的静态方法 没有参数 只有返回值String

    1
    2
    3
    4
    5
    6
    7
    8
    //@"()Ljava/lang/String;" 表示参数为空 返回值是String类型
    JNIEXPORT jstring JNICALLJava_com_huazi_Demo_getCallBack(JNIENV env,jobject object){
    jmethodID mid;
    jclass cls =(*env)->FindClass(env,"com/huazi/Demo"); //后面是包名+类名
    mid =(*env)->GeStatictMethodID(env,cls,"TestMethod","()Ljava/lang/String;");// TestMethod java中的方法名
    jstring msg =(*env)->CallStaticObjectMethod(env,cls,mid); //object 注意下是jni传过来的jobject
    return msg;
    }
坚持原创技术分享,您的支持将鼓励我继续创作!