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

複製代碼

不對之處,望多多指教
後面還會持續更新

源碼

相關文章
相關標籤/搜索