您的位置:首页 > 移动开发 > Android开发

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

2017-10-13 11:26 489 查看
转载请注明出处:http://blog.csdn.net/long117long/article/details/78224048

在项目中遇到了加载so库比较诡异的一个现象,现记录下来,以做总结。
以下以举例的方式讲述:
项目中有两个so库,一个是libhellojni.so,一个是libhellojni2.so,这两个库的都有相同函数,一个是动态注册的,一个是静态注册的,如下:
编译libhellojni.so的代码如下:
jni_self.cpp

1
#include <jni.h>
2
#include <stdio.h>
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 <jni.h>
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<java.lang.Object>' (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.<init>()


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.<clinit>(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中增加对应的方法。

之后,要多思考和细心。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息