?本文用一个demo验证Android?Studio如何编译生成一个C++动态库文件(so文件)?给?Java应用层使用。然后这个so库内部又如何调用?一个预有so库中的?C语言函数。
1. 新建一个NDK工程
?1.1 ?新建一个android studio Native C++工程
在main/cpp目录下默认有一个native-lib.cpp,还有一个CMakeList.txt文件(编译脚本),其中
native-lib.cpp实现了从JNI里返回一个C++字符串,具体代码如下:
#include <jni.h> #include <string>
extern "C" JNIEXPORT jstring Java_com_example_ndktest_MainActivity_stringFromJNI(JNIEnv* env,jobject) { ?? ?std::string hello = "Hello from cppmain"; ? ? return env->NewStringUTF(hello.c_str()); }
其中env->NewStringUTF(hello.c_str());将C++字符串hello转换成了jstring类型(JNI可以传递给JAVA的字符串类型)
?1.2 配置build.gradle
我们把CMakeList.txt移动到app moudle的根目录下,即与app模块下的build.gradle放在同一目录下,在以下脚本3中设置cmake中的path(CMakeList.txt编译脚本的存放路径),path值直接就是CMakeList.txt。
?android--defaultcofig--下添加如下脚本 ? ?脚本1:
? ?externalNativeBuild { ? ? ? ? ? ? cmake { ? ? ? ? ? ? ? ? abiFilters 'armeabi-v7a' ? ? ? ? ? ? } ? ?} ?注解:表示只编译armeabi-v7a CPU架构下已经存放的的so库。 ? ?脚本2: ? ? ?ndk{ ? ? ? ?abiFilters 'armeabi-v7a' ? ?} ? ?注解:表示只生成armeabi-v7a CPU架构下的so库,并打包到APP里。 ?同时,添加这个脚本是为了解决这个问题:用android studio直接运行项目会报错,找不到最终生成的libnative-lib.so,只有build生成apk,然后传到手机上运行才不报错。 ?添加了这个ndk脚本后就一切正常了。具体原因还需进一步研究下。 ? ?脚本3(在android配置下添加): ? ? ? externalNativeBuild { ? ? ? ? cmake { ? ? ? ? ? ? path "CMakeLists.txt" ? ? ? ? ? ? version "3.10.2" ? ? ? ? } ? ? } 注解:其中path是CMakeLists编译脚本文件的路径,以 path "CMakeLists.txt"为例,表示CMakeLists.txt文件在android studio工程的主app模块根目录下,与app模块的build.gradle文件在同一目录下。version "3.10.2"使用的CMake编译工具的版本。 ?? ?
2. 在native-lib.cpp中的stringFromJNI数里调用另一个so库libTest.so中的test()方法
2.1 修改native.cpp的stringFromJNI函数:调用test()
#include <jni.h> #include <string> #include <android/log.h>
extern "C"{ ? ?extern int test(); }
extern "C" JNIEXPORT jstring Java_com_example_ndktest_MainActivity_stringFromJNI(JNIEnv* env,jobject) { ? ? std::string hello = "Hello from cppmain"; ? ? __android_log_print(ANDROID_LOG_ERROR,"jni","libtest.so 里面的 test 方法:%d",test()); ? ? return env->NewStringUTF(hello.c_str()); }
(1)相比之前加了这样代码: ?__android_log_print(ANDROID_LOG_ERROR,"jni","libTest.so 里面的 test 方法:%d",test()); 折行代码用于打印日志,输出test()方法返回的int值。
(2)要调用test()方法,首先得用extern声明外部原型,因为test()函数的实现是在其它C语言文件里。加了一个extern "C"是因为test()是一个C语言程序,要在C++里调用C代码必须得声明关键字extern "C".
2.2 拷贝libTest.so库到我们新建的NDK项目中
将libTest.so复制到src/main/jniLibs/armeabi-v7a目录下,我们在此只编译armeabi-v7a CPU架构的so库
2.3 修改CMakeList.txt编译脚本,引入第三方库libTest.so,并链接到本地要生成的libnative-lib.so库
?脚本如下:
cmake_minimum_required(VERSION 3.10.2)?
#声明项目名 ? project("ndktest") ? ? ??
#将.C文件与C++(就是cpp)文件的查找路径赋值给一个变量source,多个文件路径用空格隔开。 file(GLOB source src/main/cpp/*.c src/main/cpp/*.cpp src/main/cpp/a/*.cpp ) ?
#将C/C++源码编译,生成一个本地so动态库,库名是native-lib,源码路径就是上面那个source变量的值,${source}取值,SHARED表示将cmake要编译生成的动态库(so文件) add_library( # Sets the name of the library. ? ? ? ? native-lib ? ? ? ? SHARED ? ? ? ? ${source}) ? # 在此项目中也可直接在此填写? src/main/cpp/native-lib.cpp
?# 将下面的log库的路径赋值给log-lib, 用于target_link_libraries中链接到将要生成的本地native- lib库
find_library(? ? ? ? ? log-lib? ? ? ? ? ? ? ? log ? ? ?# log库的路径,因为log为系统库,所以只需要名字,cmake就知道其路径了 ?? ??? ?) ?? ??? ? ?? ??? ? #配置第三方Test.so库的链接路径,我们的Test.so库放在了armeabi-v7a下,2.1中已经提到。 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")
#将Test库,log库与本地将要生成的动态库链接在一起,这样native-lib库里的stringFromJNI函数就可以调用test()函数与log库中的__android_log_print()函数了。 target_link_libraries( # Specifies the target library. ? ? ? ? native-lib ? ? ? ? Test
? ? ? ? # Links the target library to the log library?included in the NDK. ? ? ? ? ${log-lib}
? ? ? ? ) ?? ??? ? ?? ??? ? 3. 在java层调用本地库native-lib.so中的JNI函数stringFromJNI
?public class MainActivity extends AppCompatActivity { ? ? static { ? ? ? ? System.loadLibrary("native-lib"); ? ? } ? ? @Override ? ? protected void onCreate(Bundle savedInstanceState) { ? ? ? ? super.onCreate(savedInstanceState); ? ? ? ? setContentView(R.layout.activity_main); ? ? ? ? TextView tv = findViewById(R.id.sample_text); ? ? ? ? tv.setText(stringFromJNI()); ? ? } ? ? public native String stringFromJNI(); }
??
|