如果Android工程有第三方so库,并且jni编译后使用下面的cmake指定输出目录到 jniLibs下,有可能会发生 so库重复的错误。
请采用以下方法避免错误:
android {
..........
packagingOptions {
//解决AS BUG. 优先选择JNILIBS下的so库
pickFirst 'lib/arm64-v8a/libDriver.so'
pickFirst 'lib/armeabi/libDriver.so'
pickFirst 'lib/armeabi-v7a/libDriver.so'
}
}
每次生成的so所在的目录不是在 jniLibs下,虽然知道如果打包,会将它打包进去,但就是觉得看不见它,想提供给别人用,还要去某个目录找。设置:
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
生成多个so
#指定需要CMAKE的最小版本
cmake_minimum_required(VERSION 3.4.1)
#C 的编译选项是 CMAKE_C_FLAGS
# 指定编译参数,可选
SET(CMAKE_CXX_FLAGS "-Wno-error=format-security -Wno-error=pointer-sign")
#设置生成的so动态库最后输出的路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
#设置头文件搜索路径(和此txt同个路径的头文件无需设置),可选
#INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/common)
#指定用到的系统库或者NDK库或者第三方库的搜索路径,可选。
#LINK_DIRECTORIES(/usr/local/lib)
#添加子目录,将会调用子目录中的CMakeLists.txt
ADD_SUBDIRECTORY(one)
ADD_SUBDIRECTORY(two)
这样就会先去跑到子目录下的 one 和 two 的CmakeLists.txt,执行成功再返回。
此时子目录one下的CmakeLists.txt:
#继承上一层的CMakeLists.txt的变量,也可以在这里重新赋值
#C 的编译选项是 CMAKE_C_FLAGS
# 指定编译参数,可选
#SET(CMAKE_CXX_FLAGS "-Wno-error=format-security -Wno-error=pointer-sign")
#生成so动态库
ADD_LIBRARY(one-lib SHARED one.cpp)
target_link_libraries(one-lib log)
子目录two下的CmakeLists.txt:
#继承上一层的CMakeLists.txt的变量,也可以在这里重新赋值
#C 的编译选项是 CMAKE_C_FLAGS
# 指定编译参数,可选
#SET(CMAKE_CXX_FLAGS "-Wno-error=format-security -Wno-error=pointer-sign")
#生成so动态库
ADD_LIBRARY(two-lib SHARED two.cpp)
target_link_libraries(two-lib log)
最后生成了以下两个so文件,并自动按照abi分别放置在了 jniLibs下
findClass失败
通常,我们在native层想调用Java方法时,我们首先要获取Java中的方法所在的类。我们一般的方法是:
result = env->FindClass(name);
但如果在子线程中获取时,就会出现找不到类的情况。关于这一问题,详见StackOverFlow。
简单来讲,当我们在自己创建的子线程想要通过JVM获取Class时,Android会为我们启动系统的ClassLoader而不是我们App的ClassLoader。
Google提供了几种解决方法,在这里不一一赘述。本文中采用的方法是:通过缓存一个静态的全局ClassLoader对象,当env->findClass失败时,通过缓存的ClassLoader获取需要的类。
void JniHelper::setJVM(JNIEnv *env) {
jvmEnv = env;
jclass randomClass = env->FindClass("com/example/oceanlong/ndkmaintest/MainActivity");
jclass classClass = env->GetObjectClass(randomClass);
jclass classLoaderClass = env->FindClass("java/lang/ClassLoader");
jmethodID getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader",
"()Ljava/lang/ClassLoader;");
jobject localClassLoader = env->CallObjectMethod(randomClass, getClassLoaderMethod);
gClassLoader = env->NewGlobalRef(localClassLoader);
//我在Android中用findClass不行,改成loadClass才可以找到class
gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass",
"(Ljava/lang/String;)Ljava/lang/Class;");
}
jclass JniHelper::findClass(JNIEnv *env, const char* name) {
jclass result = nullptr;
if (env)
{
result = env->FindClass(name);
jthrowable exception = env->ExceptionOccurred();
if (exception)
{
env->ExceptionClear();
return static_cast<jclass>(env->CallObjectMethod(gClassLoader, gFindClassMethod, env->NewStringUTF(name)));
}
}
return result;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。