背景
今天没事干,就开始学习ndk开发,实现了一下Android和C的函数方法互调,记录一下步骤
步骤
1、通过Android Studio的SDK Manager安装CMake、LLDB和NDK
2、新建工程,记得要勾选inclue C++ support,而后一路next,直到完成
3、而后,在工程目录下,随便哪个子目录里,右击新建一个C源文件,同时可以勾选生成同名的头文件
4、ok后,会在指定目录下生成my_ndk_util.c/.h文件,内容如下
头文件
#ifndef MYNDKDEMO_MY_NDK_UTIL_H
#define MYNDKDEMO_MY_NDK_UTIL_H
#endif //MYNDKDEMO_MY_NDK_UTIL_H
源文件
#include "my_ndk_util.h"
5、而后,在CMakeLists.txt文本文件里,在add_library()中添加我们的源文件的相对路径,如下
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/jni/my_ndk_util.c) # 工程目录下的相对路径
其他的都不用改
6、同步之后,在源文件里添加一个函数,完整代码如下
#include "my_ndk_util.h"
#include "jni.h"
#include "string.h"
#include "android/log.h" // 调用Android打日志方法而用
const char* tag = "my_ndk_util";
JNIEXPORT jstring JNICALL Java_com_example_songzeceng_myndkdemo_MyNdkUtil_getAppKey(JNIEnv* env, jclass type, jstring str){
char* content = "浔阳江头夜送客,枫叶荻花秋瑟瑟";
char* string = (*env)->GetStringUTFChars(env, str, JNI_FALSE); // 根据jstring获取字符串
string = strcat(string, content);
__android_log_print(ANDROID_LOG_INFO, tag, string); // 打印日志,相当于Log.i(tag, string);
return (*env)->NewStringUTF(env, content); // 根据content创建jstring
}
源文件里,头文件包含我就不说了,尤其要注意参数列表里前两个参数的位置是固定的,不能打乱,它们之后的参数都要显示写在对应的native函数的参数列表里,也是一一对应的
那么这个c函数对应的native函数在哪儿呢?看看函数名,函数名格式是Java_native函数所在类的包名_native函数所在类名_jni函数名,如包名有.分割,以_代之,所以我这个函数名就对应着com.example.songzeceng.myndkdemo包下的MyNdkUtil.java文件里的getAppKey()方法
7、在C函数对应的包名下创建MyNdkUtils类,先加载库,再定义native方法,代码如下
public class MyNdkUtil {
private static final String LIB = "native-lib";
static {
System.loadLibrary(LIB);
}
public static native String getAppKey(String s); // 这个参数s,就是C函数里的jstring参数
}
这个lib名字,是在CMakeLists.txt文件的add_library里指定的,详情请翻阅上文第5步
8、在MainActivity里,调用方法,进行验证
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String s = MyNdkUtil.getAppKey("白居易--");
Log.i(TAG, s);
}
}
由于在MyNdkUtil类加载的时候就加载了native库,所以不妨碍native函数的调用
效果如下:
踩的坑
1、报错cmake error: format string is not a string literal (potentially insecure) [-Werror,-Wformat-security]
这个由于我们是用CMake开发,不存在mk文件,所以只需要在CMakeLists.txt里加上这样一句话就可以
set(CMAKE_C_FLAGS "-Wno-error=format-security")
2、找不到asm/types.h文件,同样,在CMakeLists.txt文件里加上一句话
set(CMAKE_C_FLAGS "-isystem /Users/songzeceng/Library/Android/sdk/ndk-bundle/sysroot/usr/include/arm-linux-androideabi")
路径是自己ndk目录下的sysroot/usr/include/arm-linux-androideabi
另外,我这个是用的C文件,所以是CMAKE_C_FLAGS,如果用的是cpp文件,就得是CMAKE_CXX_FLAGS