NDK学习
一、CmakeLists.txt配置
Android Studio 应该是在3.2的版本之前对Camke支持都不太好,没有提示,但最新的好像3.4的可以了;NDK学习,至于这里创建NDK项目以及Android Studio NDK配置就不讲了;网上很多教程;
Cmake语法:
1、配置Cmake版本:
- cmake_minimum_required(VERSION 3.4.1)
- 参数:最低版本 VERSION 3.4.1 (这里配置的最低版本是3.4.1版本)
2、添加库:
add_library( <name>
[STATIC | SHARED | MODULE]
[source1] [source2] [...])
//例子:
add_library(native-lib
SHARED
src/main/cpp/native-lib.cpp)
- 参数一:<name>表示库文件的名字,该库文件会根据命令里列出的源文件来创建
- 参数二: STATIC、SHARED和MODULE的作用是指定生成的库文件的类型。STATIC库是目标文件的归档文件,在链接其它目标的时候使用。SHARED库会被动态链接(动态链接库),在运行时会被加载。MODULE库是一种不会被链接到其它目标中的插件,但是可能会在运行时使用dlopen-系列的函数
- 参数三: [source1] [source2]指的是源文件的路径
3、查找一个库文件
find_library(<VAR>
name1)
例子:
find_library(log-lib
log)
- 参数一:<VAR>创建名为的缓存条目以存储此命令的结果。如果找到库,则结果存储在变量中,除非清除变量,否则不会重复搜索。
- 参数二:库名
4、将目标文件与库文件进行链接:
target_link_libraries(<target> [item1] [item2] [...]
[[debug|optimized|general] <item>] ...)
例子:
target_link_libraries(native-lib
${log-lib})
- 参数一:目标文件
- 参数二:库文件
Camke语法还是挺多的,比如还有设置项目路径,还有当我们导入OpenCV库设置OpenCV库路径。还有一些其他我暂时也没用到。这些是我在学习NDK开发时感觉最常用的一些语法(可能学的还是比较浅,暂时直接出到这些)
二、NDK开发中的函数调用
这里讲一些最基础的,主要是用C调用Java类中的属性以及方法。这里分为调用静态和非静态的属性及方法来讲。因为他们的调用方法略有不同
/**
* 创建MainActivity
* 里面定义了四个按钮,以及四个native方法
*
*/
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = findViewById(R.id.sample_text);
}
//调用类JavaClass中的非静态方法;
public native String javaMethod();
//调用类JavaClass中的静态方法
public static native String staticJavaMethod();
//调用类JavaClass 中非静态属性
public native String getField();
//调用类JavaClass 中静态属性
public native String getStaticField();
//四个按钮的点击事件
public void onClickBtn(View view) {
switch (view.getId()) {
case R.id.btn1:
String javaMethod = javaMethod();
tv.setText(javaMethod);
Log.i("KBein", "onCreate: javaMethod() == "+javaMethod);
break;
case R.id.btn2:
String field = getField();
tv.setText(field);
Log.i("KBein", "onCreate: getField() == "+field);
break;
case R.id.btn3:
String staticField = getStaticField();
tv.setText(staticField);
Log.i("KBein", "onCreate: getStaticField() == "+ staticField);
break;
case R.id.btn4:
String staticJavaMethod = staticJavaMethod();
tv.setText(staticJavaMethod);
Log.i("KBein", "onClickBtn: staticJavaMethod() == "+staticJavaMethod);
break;
}
}
}
/**
* 创建一个Java类
* 类中定义静态属性和方法,以及非静态属性和方法
*
*/
public class JavaClass {
public String nameStr = "instance Field";
public static String staticField = "static Field";
public JavaClass(){
}
private String methodJava(){
String str = "这是java的方法methodJava()-->通过native String javaMethod()调用";
return str;
}
private static String staticMethod(){
return "这是JavaClass类中的静态方法-->通过static native String staticJavaMethod()调用";
}
}
注:以下用到的均是此Java类和MainActivity,统一在此贴出
2.1、C调用Java类中的属性:
2.1.1 C调用Java类中的非静态属性(上C代码):
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_roger_ndkdemo_MainActivity_getField(JNIEnv *env, jobject instance) {
jclass clazz;
jobject obj;
jfieldID instance_field_id;
jstring instance_field;
//FindClass是用反射找到这个Java类,参数时JavaClass完成包名,
clazz = env->FindClass("com/example/roger/ndkdemo/JavaClass");
jmethodID constrocMID = env->GetMethodID(clazz,"<init>","()V");
obj = env->NewObject(clazz,constrocMID);
instance_field_id = env->GetFieldID(clazz,"nameStr","Ljava/lang/String;");
instance_field = (jstring)env->GetObjectField(obj,instance_field_id);
return instance_field;
}
这里来解释以下这段代码:
- 首先说Java_com_example_roger_ndkdemo_MainActivity_getField()这一段字母,“com_example_roger_ndkdemo_MainActivity”是包名+MainActivity, 而getField()即是MainActivity中的native方法-->public native String getField();所有的jni方法前面都加一个Java;因此这样就能与我们MainActivity中的getField()方法对应起来了,当我们调用getField()方法时NDK就会给我们执行这段C代码;JNIEXPORT jstring JNICALL 中的jstring就是我们方法getField()返回值String类型
参数说明:
- 参数一、JNIEnv *env --->是指向JNI环境的指针
- 参数二、jobject instance --->这是一个类的对象,在java中Object就对应了jobject。instance 指的应该就是MainActivity对象(这里是我的猜测,因为这个getField()方法时在MainActivity中)
- 注:另外参数二是根据MainActivity中getField()是否是静态来确定的,如果方法是非静态的则为(jobject instance) ;如果方法是静态的则为(jclass class);他俩区别在于jobject instance是类对象(严格说不能是对象,C中是没有对象的,其实它是类的一个引用)而jclass class就是指类
1、FindClass("com/example/roger/ndkdemo/JavaClass")方法说明: 返回类型是jclass clazz
- 看字面意思FindClass是寻找类,其实就是通过反射找到java类,而com/example/roger/ndkdemo/JavaClass是包名,这里包名的“.”改为“/”。
2、GetMethodID(clazz,"<init>","()V")方法说明:这个方法是用来获取非静态函数ID ,返回类型是jmethodID constrocMID
- 参数一:clazz 是要调用的方法所在的类
- 参数二:是方法名,而这里是<init>\指的是类JavaClass的构造函数
- 参数三:是方法描述符,在java中即是方法签名,这里我理解为是java方法中的返回值类型,因为JavaClass的构造函数是没有返回值得所以用“()V”表示。比如:返回值是String 则为 “()Ljava/lang/String;”
3、NewObject(clazz,constrocMID)方法说明:用来new一个对象实例; 返回类型是jobject obj(在个引用类型)
- 参数一:clazz 是要调用的方法所在的类
- 参数二:构造函数ID
这里new一个对象实例的原因是为了我们调JavaClass中的非静态属性;
4、GetFieldID(clazz,"nameStr","Ljava/lang/String;")方法说明:获取非静态属性的ID ,返回类型是jfieldID instance_field_id;
- 参数一:clazz 是要调用的方法所在的类
- 参数二:是属性名
- 参数三:java中的域描述符;
5、GetObjectField(obj,instance_field_id)方法说明:获取属性值,返回类型为jobject;
- 参数一:obj 是要调用的方法所在的类的引用(java中就是类对象)
- 参数二:属性ID
最后我们看到把jobject强转为jstring,jstring也是引用类型对应于java中的String;
2.2、C调用Java类中的方法:
2.2.1 C调用Java类中的非静态方法(上C代码):
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_roger_ndkdemo_MainActivity_javaMethod(JNIEnv *env, jobject instance) {
jclass clazz;
jobject obj;
jmethodID constrocMID;
jmethodID methodId;
//获取JavaClass类
clazz = env->FindClass("com/example/roger/ndkdemo/JavaClass");
//AllocObject()是指为clazz开辟一个新的对象,不调用此Class的构造方法
//obj = env->AllocObject(clazz);
//获取JavaClass 无参构造函数的jmethodID
constrocMID = env->GetMethodID(clazz,"<init>","()V");
//根据clazz和constrocMID new一个新的JavaClass对象
obj = env->NewObject(clazz,constrocMID);
/**
* 第一个参数:是指定要调用的方法是在那个类
* 第二个参数:是指定要调用的方法名字(UTF-8)
* 第三个参数:是代表要调用方法的java方法签名(这边狭义的理解为返回值类型)
*/
methodId = env->GetMethodID(clazz,"methodJava","()Ljava/lang/String;");
return (jstring)env->CallObjectMethod(obj,methodId);
}
这个就不多解释了,注释都有,下面我把native-lib.cpp文件所有代码都贴出来,里面还有调用静态属性和静态方法,这个和调用非静态的还存在略微的不同
#include <jni.h>
/**
* 创建的cpp文件
* MainActivity中的native方法即是调用一下一下方法
*/
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_roger_ndkdemo_MainActivity_javaMethod(JNIEnv *env, jobject instance) {
jclass clazz;
jobject obj;
jmethodID constrocMID;
jmethodID methodId;
//获取JavaClass类
clazz = env->FindClass("com/example/roger/ndkdemo/JavaClass");
//AllocObject()是指为clazz开辟一个新的对象,不调用此Class的构造方法
//obj = env->AllocObject(clazz);
//获取JavaClass 无参构造函数的jmethodID
constrocMID = env->GetMethodID(clazz,"<init>","()V");
//根据clazz和constrocMID new一个新的JavaClass对象
obj = env->NewObject(clazz,constrocMID);
/**
* 第一个参数:是指定要调用的方法是在那个类
* 第二个参数:是指定要调用的方法名字(UTF-8)
* 第三个参数:是代表要调用方法的java方法签名(这边狭义的理解为返回值类型)
*/
methodId = env->GetMethodID(clazz,"methodJava","()Ljava/lang/String;");
return (jstring)env->CallObjectMethod(obj,methodId);
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_roger_ndkdemo_MainActivity_getField(JNIEnv *env, jobject instance) {
jclass clazz;
jobject obj;
jfieldID instance_field_id;
jstring instance_field;
clazz = env->FindClass("com/example/roger/ndkdemo/JavaClass");
jmethodID constrocMID = env->GetMethodID(clazz,"<init>","()V");
obj = env->NewObject(clazz,constrocMID);
instance_field_id = env->GetFieldID(clazz,"nameStr","Ljava/lang/String;");
instance_field = (jstring)env->GetObjectField(obj,instance_field_id);
return instance_field;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_roger_ndkdemo_MainActivity_getStaticField(JNIEnv *env, jobject instance) {
jclass clazz;
jfieldID static_field_id;
jstring static_field;
clazz = env->FindClass("com/example/roger/ndkdemo/JavaClass");
static_field_id = env->GetStaticFieldID(clazz,"staticField","Ljava/lang/String;");
static_field = (jstring)env->GetStaticObjectField(clazz,static_field_id);
return static_field;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_roger_ndkdemo_MainActivity_staticJavaMethod(JNIEnv *env, jclass type) {
jclass clazz;
jmethodID static_method_id;
clazz = env->FindClass("com/example/roger/ndkdemo/JavaClass");
static_method_id = env->GetStaticMethodID(clazz,"staticMethod","()Ljava/lang/String;");
return (jstring)env->CallStaticObjectMethod(clazz,static_method_id);
}
不足之处,望多多指教
后面还会持续更新
网友评论