Android开发中遇到加载有相同函数的so库时的问题
2017-10-13 11:26
579 查看
转载请注明出处:http://blog.csdn.net/long117long/article/details/78224048
在项目中遇到了加载so库比较诡异的一个现象,现记录下来,以做总结。
以下以举例的方式讲述:
项目中有两个so库,一个是libhellojni.so,一个是libhellojni2.so,这两个库的都有相同函数,一个是动态注册的,一个是静态注册的,如下:
编译libhellojni.so的代码如下:
jni_self.cpp
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
编译libhellojni2.so的代码如下:
com_example_fun_HelloJni.h
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Android.mk
1
1
2
3
4
5
在代码中调用HelloJni,如下所示:
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
其中“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
1
2
发现第一个方法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中增加对应的方法。
之后,要多思考和细心。
在项目中遇到了加载so库比较诡异的一个现象,现记录下来,以做总结。
以下以举例的方式讲述:
项目中有两个so库,一个是libhellojni.so,一个是libhellojni2.so,这两个库的都有相同函数,一个是动态注册的,一个是静态注册的,如下:
编译libhellojni.so的代码如下:
jni_self.cpp
1
#include <jni.h>2
#include <stdio.h>3
#include "jni_self.h"45
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
}1112
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
}1819
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
}2526
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
};3233
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
}4748
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>23
#ifndef _Included_com_example_fun_HelloJni4
#define _Included_com_example_fun_HelloJni5
#ifdef __cplusplus6
extern "C" {7
#endif89
JNIEXPORT jstring JNICALL Java_com_example_fun_HelloJni_getString10
(JNIEnv *, jobject);1112
JNIEXPORT jstring JNICALL Java_com_example_fun_HelloJni_getFirst13
(JNIEnv *, jobject);1415
JNIEXPORT jstring JNICALL Java_com_example_fun_HelloJni_getSecond16
(JNIEnv *, jobject);17
#ifdef __cplusplus18
}19
#endif20
#endifcom_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)23
include $(CLEAR_VARS)45
LOCAL_MODULE:= libhellojni26
LOCAL_SRC_FILES := \7
com_example_fun_HelloJni.cpp89
LOCAL_LDLIBS := -llog1011
LOCAL_SHARED_LIBRARIES := \12
libstlport \13
libcutils \14
libutils1516
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
}910
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.so2
7f76241000-7f76242000 r--p 00000000 b3:1e 1708893/data/app/com.zxl.test-1/lib/arm64/libhellojni2.so3
7f76242000-7f76243000 rw-p 00001000 b3:1e 1708893/data/app/com.zxl.test-1/lib/arm64/libhellojni2.so4
7f76243000-7f76244000 r-xp 00000000 b3:1e 1708894/data/app/com.zxl.test-1/lib/arm64/libhellojni.so5
7f76253000-7f76254000 r--p 00000000 b3:1e 1708894/data/app/com.zxl.test-1/lib/arm64/libhellojni.so6
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通过Jni加载so库遇到UnsatisfiedLinkError问题!!!
- android开发遇到make is not found in path 或者是报加载不到so库的错误
- android开发中遇到的2个路径问题和html解析问题
- android开发遇到的问题(WidgetProvider, service, Thread,Handler)
- android开发搭建环境时遇到问题
- android开发环境搭建遇到的问题(MyEclipse8.5+android sdk2.3+ADT-8.0.1
- android开发过程中遇到的一些问题汇总
- 关于android开发时,eclipse的函数自动提示功能很卡的问题解决方法
- Android 开发遇到的一些问题整理
- android开发遇到的问题
- android开发——记下今天处理KeyDown和Menu事件所遇到的问题
- 菜鸟初学Android开发:Android Snake工程加载超时问题
- 动态加载程序集[仿Petshop架构应用开发遇到的问题]
- Android开发平台搭建遇到的ADT无法安装问题
- Android开发中遇到的奇怪问题
- android开发过程遇到的问题和解决方案(不断更新)
- 在开发环境中常遇到更改存储过程、函数、视图等对象,解决SQL Server2005里sp_helptext输出格式错行问题
- android开发过程中遇到的一些问题(包括自定义ProgressBar, Intent, Animation, ListView, RadioButton)
- Android综合下载系统的开发步骤以及遇到问题
- 关于android开发遇到的一些问题的解决办法---间断的更新