美文网首页
NDK开发学习(一)

NDK开发学习(一)

作者: KBein | 来源:发表于2019-05-22 13:21 被阅读0次
    timg.jpg

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

    不足之处,望多多指教

    后面还会持续更新

    源码

    相关文章

      网友评论

          本文标题:NDK开发学习(一)

          本文链接:https://www.haomeiwen.com/subject/dnxszqtx.html