环境搭建
在Android Studio上设置NDK的路径时,会自动下载NDK开发环境, 之后我们新建新工程选中 Include C++ Support
即可.
工程新建完成后,可以看到如上的目录,其中:
cpp中包含了所有项目中使用的native源码文件、头文件和预编译库。native-lib.cpp是Android Studio自动生成的一个sample文件,放在module的src/main/cpp中。
External Build Files中包含了CMake或ndk-build的编译脚本。CMakeLists.txt是Android Studio自动生成的一个CMake脚本,放在module的根目录中。
CMakeLists.txt
在我们的模块的build.gradle里,可以看到有Cmake的编译设置.
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
我们去看一下CMakeLists.txt
文件
里面有如下配置
//声明库名称、类型、源码文件
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
// 将NDK库链接到native库中,这样native库才能调用NDK库中的函数
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
Java中调用Cpp的函数
当我们在Java文件写下
public native String getNativeString(Object obj);
时, AS便提示我们去完成这个native方法. 并自动帮我们生成了对应签名的jni文件.
JNIEXPORT jstring JNICALL
Java_com_jerey_testndk_MainActivity_getNativeString(JNIEnv *env, jclass type, jobject obj) {
return env->NewStringUTF("hello");
}
这个是JNI签名的方法,代表只有com.jerey.testndk包里面的MainActivity类才能调用这个函数
我们在这里直接返回一个”hello”字符串. 为什么NewStringUTF
呢, 因为C与Java中的字符串编码是不一样的, java中一个字符2个直接,c与c艹中是一个字节,所以需要转换一下.
此时看我们的Activity文件. Java文件中只System.loadLibrary
,即可在文件中调用Lib里面的方法. 可以看到我们打印了一下getNativeString. 至于传参,后面有用.
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e(TAG, "getNativeString: " + getNativeString(this));
}
public native String getNativeString(Object obj);
public void callBack(String s) {
Log.i(TAG, "callBack: " + s);
}
}
注意, 我们的cpp文件中的方法, 必须被extern "C" {
包裹着,由于jni走的是c的入口, 若不extern c,会直接报Jni找不到这个方法的异常.
此时,我们的程序是可以打印出”hello”了.
C++回调Java
我们上边的getNativeString,传了自己过去, 这是我的java思想导致的.不过可以先这么用.
我们在getNativeString时,完全可以回调自己的callback方法
extern "C" {
JNIEXPORT jstring JNICALL
Java_com_jerey_testndk_MainActivity_getNativeString(JNIEnv *env, jclass type, jobject obj) {
jobject mJObj = env->NewGlobalRef(obj);
jclass output = env->GetObjectClass(mJObj);
// 拿到callBack方法
jmethodID callback = env->GetMethodID(output, "callBack",
"(Ljava/lang/String;)V");
jstring jresult = env->NewStringUTF("callback string");
env->CallVoidMethod(mJObj, callback, jresult);
env->DeleteLocalRef(jresult);
return env->NewStringUTF("hello");
}
}
上面的代码, 通过jni GetMethodID拿到了我们java类的callBack方法,并回调传入了”callback string”. log如下
06-02 13:12:10.840 25538-25538/com.jerey.testndk I/MainActivity: callBack: callback string
06-02 13:12:10.840 25538-25538/com.jerey.testndk E/MainActivity: getNativeString: hello
本文作者:Anderson/Jerey_Jobs
博客地址 : http://jerey.cn/
简书地址 : Anderson大码渣
github地址 : https://github.com/Jerey-Jobs