Android开发中遇到加载有相同函数的so库时的问题

 转载请注明出处: http://blog.csdn.net/long117long/article/details/78224048

    在项目中遇到了加载so库比较诡异的一个现象,现记录下来,以做总结。
    以下以举例的方式讲述:
    项目中有两个so库,一个是libhellojni.so,一个是libhellojni2.so,这两个库的都有相同函数,一个是动态注册的,一个是静态注册的,如下:
    编译libhellojni.so的代码如下:
      jni_self.cpp
1
#include 
2
#include 
3
#include "jni_self.h"
4
5
jstring com_example_fun_HelloJni_getString(JNIEnv* env, jobject thiz)
6
{
7
    char *c = "HelloJni";
8
    jstring s = (*env).NewStringUTF(c);
9
    return s;
10
}
11
12
jstring com_example_fun_HelloJni_getFirst(JNIEnv* env, jobject thiz)
13
{
14
    char *c = "first";
15
    jstring s = (*env).NewStringUTF(c);
16
    return s;
17
}
18
19
jstring com_example_fun_HelloJni_getSecond(JNIEnv* env, jobject thiz)
20
{
21
    char *c = "second";
22
    jstring s = (*env).NewStringUTF(c);
23
    return s;
24
}
25
26
static JNINativeMethod nativeMethods[] = 
27
{
28
    {"getString",       "()Ljava/lang/String;",            (void*)com_example_fun_HelloJni_getString},
29
    {"getSecond",       "()Ljava/lang/String;",            (void*)com_example_fun_HelloJni_getSecond},
30
    {"getFirst",       "()Ljava/lang/String;",            (void*)com_example_fun_HelloJni_getFirst} 
31
};
32
33
static int registerNativeMethods(JNIEnv* env)
34
{
35
    int result = -1;
36
    jclass clazz = env->FindClass("com/example/fun/HelloJni");
37
    if (NULL != clazz) 
38
    {
39
        if (env->RegisterNatives(clazz, nativeMethods, sizeof(nativeMethods)
40
                    / sizeof(nativeMethods[0])) == JNI_OK) 
41
        {
42
            result = 0;
43
        }
44
    }
45
    return result;
46
}
47
48
jint JNI_OnLoad(JavaVM* vm, void* reserved)
49
{
50
    JNIEnv* env = NULL;
51
    jint result = -1;
52
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK)
53
    {
54
        if (NULL != env && registerNativeMethods(env) == 0) 
55
        {
56
            result = JNI_VERSION_1_4;
57
        }
58
    }
59
    return result;
60
}
     Android.mk
1
LOCAL_PATH:= $(call my-dir)
2
3
include $(CLEAR_VARS)
4
5
LOCAL_MODULE    :=  libhellojni 
6
LOCAL_SRC_FILES := \
7
    jni_self.cpp 
8
    
9
LOCAL_LDLIBS := -llog
10
11
LOCAL_SHARED_LIBRARIES :=       \
12
    libstlport \
13
    libcutils \
14
    libutils    
15
16
include $(BUILD_SHARED_LIBRARY)
    编译libhellojni2.so的代码如下:
     com_example_fun_HelloJni.h
1
#include 
2
3
#ifndef _Included_com_example_fun_HelloJni
4
#define _Included_com_example_fun_HelloJni
5
#ifdef __cplusplus
6
extern "C" {
7
#endif
8
9
JNIEXPORT jstring JNICALL Java_com_example_fun_HelloJni_getString
10
  (JNIEnv *, jobject);
11
12
JNIEXPORT jstring JNICALL Java_com_example_fun_HelloJni_getFirst
13
  (JNIEnv *, jobject);
14
15
JNIEXPORT jstring JNICALL Java_com_example_fun_HelloJni_getSecond
16
  (JNIEnv *, jobject);
17
#ifdef __cplusplus
18
}
19
#endif
20
#endif
    com_example_fun_HelloJni.cpp     
1
#include "com_example_fun_HelloJni.h"
2
3
JNIEXPORT jstring JNICALL Java_com_example_fun_HelloJni_getString(JNIEnv * env, jobject jobj)
4
{
5
    char *c = "HelloJni2";
6
    jstring s = (*env).NewStringUTF(c);
7
    return s;
8
}
9
10
JNIEXPORT jstring JNICALL Java_com_example_fun_HelloJni_getFirst(JNIEnv * env, jobject jobj)
11
{
12
    char *c = "first2";
13
    jstring s = (*env).NewStringUTF(c);
14
    return s;
15
}
16
17
JNIEXPORT jstring JNICALL Java_com_example_fun_HelloJni_getSecond(JNIEnv * env, jobject jobj)
18
{
19
      char *c = "second2";
20
    jstring s = (*env).NewStringUTF(c);
21
    return s;
22
}
     Android.mk
1
LOCAL_PATH:= $(call my-dir)
2
3
include $(CLEAR_VARS)
4
5
LOCAL_MODULE    :=  libhellojni2 
6
LOCAL_SRC_FILES := \
7
    com_example_fun_HelloJni.cpp 
8
    
9
LOCAL_LDLIBS := -llog
10
11
LOCAL_SHARED_LIBRARIES :=       \
12
    libstlport \
13
    libcutils \
14
    libutils    
15
16
include $(BUILD_SHARED_LIBRARY)
     而对应的com.example.fun.HelloJni如下:
1
public class HelloJni {
2
    public native String getString();
3
    public native String getFirst();
4
    //相比 本地层方法 少了一个 getSecond();
5
}
    在代码中调用HelloJni,如下所示:
1
static {
2
        System.loadLibrary("hellojni2");
3
        try{
4
            System.loadLibrary("hellojni");
5
        }catch (Throwable t){
6
            t.printStackTrace();
7
        }
8
    }
9
10
 private void testLib(){
11
        HelloJni helloJni = new HelloJni();
12
        Log.v("testLib",helloJni.getString());
13
        Log.v("testLib",helloJni.getFirst());
14
}
    在程序启动时,会有看到如下日志:
1
E/art: ----- class 'Lcom/example/fun/HelloJni;' cl=0x12c6ba00 -----
2
E/art:   objectSize=420 (412 from super)
3
E/art:   access=0x0000.0001
4
E/art:   super='java.lang.Class' (cl=0x0)
5
E/art:   vtable (2 entries, 11 in super):
6
E/art:      0: java.lang.String com.example.fun.HelloJni.getFirst()
7
E/art:      1: java.lang.String com.example.fun.HelloJni.getString()
8
E/art:   direct methods (1 entries):
9
E/art:      0: void com.example.fun.HelloJni.()
10
E/art: Failed to register native method com.example.fun.HelloJni.getSecond()Ljava/lang/String; in /data/app/com.zxl.test-1/base.apk
11
W/System.err: java.lang.UnsatisfiedLinkError: JNI_ERR returned from JNI_OnLoad in "/data/app/com.zxl.test-1/lib/arm64/libhellojni.so"
12
W/System.err:     at java.lang.Runtime.loadLibrary(Runtime.java:371)
13
W/System.err:     at java.lang.System.loadLibrary(System.java:989)
14
W/System.err:     at com.zxl.test.TestLib.TestLibActivity.(TestLibActivity.java:64)
15
W/System.err:     at java.lang.reflect.Constructor.newInstance(Native Method)
16
W/System.err:     at java.lang.Class.newInstance(Class.java:1572)
17
W/System.err:     at android.app.Instrumentation.newActivity(Instrumentation.java:1065)
18
W/System.err:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2199)
19
W/System.err:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
20
W/System.err:     at android.app.ActivityThread.access$800(ActivityThread.java:144)
21
W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
22
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:102)
23
W/System.err:     at android.os.Looper.loop(Looper.java:135)
24
W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5221)
25
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
26
W/System.err:     at java.lang.reflect.Method.invoke(Method.java:372)
27
W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898)
28
W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693)
29
W/System.err:     at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:133)
    其中“Failed to register native method com.example.fun.HelloJni.getSecond()Ljava/lang/String;” 和
    “java.lang.UnsatisfiedLinkError: JNI_ERR returned from JNI_OnLoad in "/data/app/com.zxl.test-1/lib/arm64/libhellojni.so"”
     但是,我们看maps表(cat /proc/进程名/maps |grep hellojni)如下:
1
7f76231000-7f76232000 r-xp 00000000 b3:1e 1708893                        /data/app/com.zxl.test-1/lib/arm64/libhellojni2.so
2
7f76241000-7f76242000 r--p 00000000 b3:1e 1708893                        /data/app/com.zxl.test-1/lib/arm64/libhellojni2.so
3
7f76242000-7f76243000 rw-p 00001000 b3:1e 1708893                        /data/app/com.zxl.test-1/lib/arm64/libhellojni2.so
4
7f76243000-7f76244000 r-xp 00000000 b3:1e 1708894                        /data/app/com.zxl.test-1/lib/arm64/libhellojni.so
5
7f76253000-7f76254000 r--p 00000000 b3:1e 1708894                        /data/app/com.zxl.test-1/lib/arm64/libhellojni.so
6
7f76254000-7f76255000 rw-p 00001000 b3:1e 1708894                        /data/app/com.zxl.test-1/lib/arm64/libhellojni.so
    发现虽然libhellojni.so加载出错了,但是还是被加载到内存中了。
    执行前面的方法testLib(),日志如下:
1
V/testLib1: HelloJni
2
V/testLib1: first2
    发现第一个方法getString()调用到的是libhellojni.so库,而第二个方法getFirst()调用到的竟然是libhellojni2.so中的方法。

    出现此种情况的原因,应该是动态注册 和 静态注册 的缘故。当加载libhellojni.so时,动态注册了getString()方法,而由于动态注册getSecond()方法时,HelloJni.java中没有声明此方法导致动态注册中断,而我们又在java代码中catch了这个异常,因此会继续加载libhellojni2.so。
    在调用方法时,我们调用getString()方法,由于这个方法动态注册成功了,因此会调用到libhellojni.so中,而再调用getFirst()方法,这个方法动态注册失败了,因此系统不会去libhellojni.so中查找了,转而内存中加载的其他的so库中查找看有没有能够匹配getFirst()方法的函数,结果在libhellojni2.so中查找到了,因此调用到libhellojni2.so库中。
    
    总结:
    出现这个现象的原因在于不了解机制和疏忽大意:
    1. 只在cpp中增加了对应的函数,并且增加的函数注册方法也不是放在nativeMethods[]声明的最后,而是放到了中间。
    2. 忘记在HelloJni.java中增加对应的方法。
    
    之后,要多思考和细心。


你可能感兴趣的:(android)