在实际的Android开发中,我们经常要用到外部的动态链接库.so文件,有时我们是使用别人编译好的.so 文件,有时需要我们自己用NDK编译出.so文件,并且在另外的工程里使用。所以本文旨在编写自己的Jni并且编译生成特定的共享库.so文件,然后在另外的工程里使用该共享链接库。
首先我们先创建一个Naive C++工程,具体创建方法见我上一篇博客。创建完成的目录结构大致如下:
这里为了在调用.so文件时方便区分开来,我的工程名、包名以及共享库名和库源文件名都做了修改。所以对应的CMakeLists也做了修改:
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
testso-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
testso-lib.cpp)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
testso-lib
# Links the target library to the log library
# included in the NDK.
${log-lib})
在上篇博文里,我们知道初始创建的Native C++工程是在MainActivity调用Jni方法的,这里我们新建了一个接口类TestSoHelper,并通过这个类调用Jni方法。TestSoHelper的代码如下:
package com.test.mytestsoproject;
public class TestSoHelper {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("testso-lib");
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public static native int intFromJNI(int a, int b);
}
对应的Jni方法是:
extern "C"
JNIEXPORT jint JNICALL
Java_com_test_mytestsoproject_TestSoHelper_intFromJNI(JNIEnv *env, jclass instance, jint a,
jint b) {
// TODO
int returnValue = a + b;
return returnValue;
}
这个Jni的方法十分简单,方法传入两个整数,然后返回这两个数的和。关于Jni的编写,学过C语言的同学应该一看就会了,基本是跟C/C++编写一模一样,在此不再赘述。
下面就是对应的native方法的调用了,我这里在MainActivity里简单调用了一下,由于我们是为了编译成.so文件,给其他工程用的,所以在此不写native方法的调用也是可以的。下面是我的调用:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = findViewById(R.id.sample_text);
tv.setText(TestSoHelper.intFromJNI(1,2) + "");
}
}
基本是默认的工程,只是把要显示的文本改成了我Jni方法返回的数据。打包运行的结果就是在手机上输出数字1+2的和3。
然后我们把编写好的Jni封装成共享库.so。只需要点击Build [图片上传失败...(image-883908-1583744056196)]
Make Project即可生成.so文件。生成的.so文件可以在下面的路径下找到:app\build\intermediates\cmake\debug\obj。可以看到工程生成了对应不同android CPU架构的.so文件:
这样我们就可以在别的工程里使用该.so 文件了。
这里我是直接用上篇博客创建的工程(MyTestProject)来引用这个共享库.so文件的,当然,理论上这个共享库可以被任意android工程引用。首先,我们要把刚才生成的共享库.so文件拷贝到我们要使用它的工程的libs文件夹下,最好是把不同CPU架构的.so文件都拷贝进去,这样我们的应用就能支持各种android系统:
然后,在build.gradle里添加以下代码:
sourceSets.main {
jniLibs.srcDirs = ['libs']
jni.srcDirs = []
}
这是告诉工程要引用的共享库所在的位置。接下来把刚才生成.so文件的工程里的Jni接口类TestSoHelper拷贝到我们当前的工程里,注意,路径也必须与原工程相同!另外,该类里的native方法可能会显示为红色,无法解析,这个不用去管,程序可以正常编译运行。
到这里我们就可以直接在当前工程(MyTestProject)里调用共享库里的方法了,简单的调用如下:
打包执行下,结果果然与之前一样!
以上方法是我个人使用Android Jni 的经验,应该是比网上其他引用自编写共享库的方法简单很多,希望能帮助大家。如有错误和不足,希望大家指正!
您的赞赏是我坚持分享的最大动力 :)