NDK开发学习(一)

1、CmakeLists.txt配置

Android Studio 应该是在3.2的版本以前对Camke支持都不太好,没有提示,但最新的好像3.4的能够了;NDK学习,至于这里建立NDK项目以及Android Studio NDK配置就不讲了;网上不少教程;java

Cmake语法:

一、配置Cmake版本:git

  • cmake_minimum_required(VERSION 3.4.1)
  • 参数:最低版本 VERSION 3.4.1 (这里配置的最低版本是3.4.1版本)

二、添加库:github

add_library( <name>  
                                [STATIC | SHARED | MODULE]     
                                [source1] [source2] [...])  

   //例子:  
               add_library(native-lib   
                               SHARED   
                               src/main/cpp/native-lib.cpp)    
                               
复制代码
  • 参数一:表示库文件的名字,该库文件会根据命令里列出的源文件来建立
  • 参数二: STATIC、SHARED和MODULE的做用是指定生成的库文件的类型。STATIC库是目标文件的归档文件,在连接其它目标的时候使用。SHARED库会被动态连接(动态连接库),在运行时会被加载。MODULE库是一种不会被连接到其它目标中的插件,可是可能会在运行时使用dlopen-系列的函数
  • 参数三: [source1] [source2]指的是源文件的路径

三、查找一个库文件缓存

find_library(<VAR> 
                     name1)
例子:

        find_library(log-lib
                    log)

复制代码
  • 参数一:建立名为的缓存条目以存储此命令的结果。若是找到库,则结果存储在变量中,除非清除变量,不然不会重复搜索。
  • 参数二:库名

四、将目标文件与库文件进行连接:bash

target_link_libraries(<target> [item1] [item2] [...]
                      [[debug|optimized|general] <item>] ...)

例子:

        target_link_libraries(native-lib
                            ${log-lib})

复制代码
  • 参数一:目标文件
  • 参数二:库文件

Camke语法仍是挺多的,好比还有设置项目路径,还有当咱们导入OpenCV库设置OpenCV库路径。还有一些其余我暂时也没用到。这些是我在学习NDK开发时感受最经常使用的一些语法(可能学的仍是比较浅,暂时直接出到这些)app

2、NDK开发中的函数调用

这里讲一些最基础的,主要是用C调用Java类中的属性以及方法。这里分为调用静态和非静态的属性及方法来说。由于他们的调用方法略有不一样ide

/**
 * 建立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.一、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;
}

复制代码

这里来解释如下这段代码:ui

  • 首先说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类型

参数说明:

  • 参数1、JNIEnv *env --->是指向JNI环境的指针
  • 参数2、jobject instance --->这是一个类的对象,在java中Object就对应了jobject。instance 指的应该就是MainActivity对象(这里是个人猜想,由于这个getField()方法时在MainActivity中)
  • 注:另外参数二是根据MainActivity中getField()是不是静态来肯定的,若是方法是非静态的则为(jobject instance) ;若是方法是静态的则为(jclass class);他俩区别在于jobject instance是类对象(严格说不能是对象,C中是没有对象的,其实它是类的一个引用)而jclass class就是指类

一、FindClass("com/example/roger/ndkdemo/JavaClass")方法说明: 返回类型是jclass clazz

  • 看字面意思FindClass是寻找类,其实就是经过反射找到java类,而com/example/roger/ndkdemo/JavaClass是包名,这里包名的“.”改成“/”。

二、GetMethodID(clazz,"<init>","()V")方法说明:这个方法是用来获取非静态函数ID ,返回类型是jmethodID constrocMID

  • 参数一:clazz 是要调用的方法所在的类
  • 参数二:是方法名,而这里是<init>\指的是类JavaClass的构造函数
  • 参数三:是方法描述符,在java中便是方法签名,这里我理解为是java方法中的返回值类型,由于JavaClass的构造函数是没有返回值得因此用“()V”表示。好比:返回值是String 则为 “()Ljava/lang/String;”

三、NewObject(clazz,constrocMID)方法说明:用来new一个对象实例; 返回类型是jobject obj(在个引用类型)

  • 参数一:clazz 是要调用的方法所在的类
  • 参数二:构造函数ID
    这里new一个对象实例的缘由是为了咱们调JavaClass中的非静态属性;

四、GetFieldID(clazz,"nameStr","Ljava/lang/String;")方法说明:获取非静态属性的ID ,返回类型是jfieldID instance_field_id;

  • 参数一:clazz 是要调用的方法所在的类
  • 参数二:是属性名
  • 参数三:java中的域描述符;

五、GetObjectField(obj,instance_field_id)方法说明:获取属性值,返回类型为jobject;

  • 参数一:obj 是要调用的方法所在的类的引用(java中就是类对象)
  • 参数二:属性ID

最后咱们看到把jobject强转为jstring,jstring也是引用类型对应于java中的String;

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);
}

复制代码

不对之处,望多多指教
后面还会持续更新

源码

相关文章
相关标签/搜索