jni-01、宏、jni函數詳解、數據獲取、類型轉換、簽名規則

宏語法
// TODO 預處理器不是編譯器,預處理器主要完成文本替換的操作(文本替換,以後專門在Linux中去講),預處理器都是用 #xxx 的寫法,並不是註釋哦

/*
                                #include  導入頭文件
                                #if       if判斷操作  【if的範疇 必須endif】
                                #elif     else if
                                #else     else
                                #endif    結束if
          #define   定義一個宏
          #ifdef    如果定義了這個宏 【if的範疇 必須endif】
          #ifndef   如果沒有定義這個宏 【if的範疇 必須endif】
          #undef    取消宏定義
          #pragma   設定編譯器的狀態
 */

#include <iostream>
using namespace std;

int main() {
    // std::cout << "宏" << std::endl;

#if 1 // if
    cout <<  "真" << endl;

#elif 0 // else if
    cout <<  "假" << endl;

#else
    cout << "都不滿足" << endl;

#endif // 結束if
    cout << "結束if" << endl;

    return 0;
}
宏定義 解決循環拷貝的問題
  • T2.h
#ifndef CLIONCPPPROJECT_T2_H // 如果沒有定義這個宏  解決循環拷貝的問題
#define CLIONCPPPROJECT_T2_H // 我就定義這個宏

// 100 行代碼
// 第一次能夠進來
// 第二次  第n此進不來    直接 解決循環拷貝的問題了

// ---------------
#ifndef isRelease // 如果沒有isRelease這個宏
#define isRelease 1 // 是否是正式環境下 【我就定義isRelease這個宏】

#if isRelease == true
#define RELEASE // 正式環境下 定義RELEASE宏

#elif isRelease == false
#define DEBUG // 測試環境下  定義DEBUG宏

#endif // 結束裏面的if
#endif // 結束裏面的if
#endif //CLIONCPPPROJECT_T2_H // 結束外面的if
#include <iostream>
#include "T2.h"
using namespace std;

int main() {

    // if 條件判斷
    // ifdef xxx 是否定義了xxx這個宏

#ifdef DEBUG // 是否定義了DEBUG這個宏
    cout << "在測試環境下,迭代功能" << endl;
    // 省略 500行 ...

#else RELEASE
    cout << "在正式環境下,功能上下中" << endl;
    // 省略 500行 ...

#endif // 結束IF
}
宏的取消 #undef 宏
// 宏的取消 #undef 宏

#include <iostream>
using namespace std;

int main() {
    int i = 1

#ifndef DERRY // 如果沒有定義這個宏
#define DERRY // 我就定義宏
#ifdef DERRY // 是否定義了這個宏
    for (int i = 0; i < 6; ++i) {
        cout << "Derry 1" << endl;
    }
    // 省略 500行 ...

#ifdef DERRY // 是否定義了這個宏
    for (int i = 0; i < 6; ++i) {
        cout << "Derry 2" << endl;
    }
    // 省略 500行 ...

#undef DERRY // 取消宏的定義,下面的代碼,就沒法用這個宏了,相當於:沒有定義過DERRY宏

#ifdef DERRY
    cout << "你定義了Derry宏" << endl;
#else
    cout << "你沒有定義了Derry宏" << endl;

#endif
#endif
#endif
#endif
    return 0;
}
宏變量 真實開發中:宏都是大寫

#include <iostream>
using namespace std;

#define VALUE_I 9527
#define VALUE_S "AAA"
#define VALUE_F 545.3f

int main() {
    int i = VALUE_I; // 預處理階段 宏會直接完成文本替換工作,替換後的樣子:int i = 9527;
    string s = VALUE_S; // 預處理階段 宏會直接完成文本替換工作,替換後的樣子:string s = "AAA";
    float f = VALUE_F; // 預處理階段 宏會直接完成文本替換工作,替換後的樣子:float f = 545.3f;
    return 0;
}
宏函數 優缺點
#include <iostream>
using namespace std;

#define SHOW(V) cout << V << endl; // 參數列表 無需類型  返回值 看不到
#define ADD(n1, n2) n1 + n2
#define CHE(n1, n2) n1 * n2 // 故意製作問題 ,宏函數的注意事項

// 複雜的宏函數  宏函數要 \ 才能換行
#define LOGIN(V) if(V==1) {                         \
    cout << "滿足 你個貨輸入的是:" << V << endl;        \
} else {                                             \
    cout << "不滿足 你個貨輸入的是:" << V << endl;       \
} // 這個是結尾,不需要加 \

void show() {}

int main() {
    SHOW(8);
    SHOW(8.8f);
    SHOW(8.99);

    int r = ADD(1, 2);
    cout << r << endl;

    r = ADD(1+1, 2+2);
    cout << r << endl;

    // r = CHE(1+1, 2+2);
    r = 1+1 * 2+2; // 文本替換:1+1 * 2+2  先算乘法  最終等於 5
    cout << r << endl; // 我們認爲的是8,   但是打印5

    LOGIN(0);
    LOGIN(0);
    LOGIN(0);
    LOGIN(0);
    LOGIN(0);
    LOGIN(0);
    // 會導致代碼體積增大

    show();
    show();
    show();
    show();
    show();
    // 普通函數,每次都會進棧 彈棧 ,不會導致代碼體積增大

    return 0;
}

// 宏函數
/*
 * 優點:
 *   1.文本替換,不會造成函數的調用開銷(開闢棧空間,形參壓棧,函數彈棧釋放 ..)
 *
 * 缺點:
 *   1.會導致代碼體積增大
 *
 */
jni函數詳解
  • native-lib.cpp
#include "com_derry_as_jni_project_MainActivity.h"

// NDK工具鏈裏面的 log 庫 引入過來
#include <android/log.h>

#define TAG "Derry"
// ... 不知道傳入什麼?  藉助JNI裏面的宏來自動幫我填充
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)


// extern "C": 必須採用C的編譯方式,爲什麼,請看JNIEnv內部源碼
// // 無論是C還是C++ 最終是調用到 C的JNINativeInterface,所以必須採用C的方式 extern "C"
// 函數的實現
extern "C"

JNIEXPORT  // 標記該方法可以被外部調用(VS上不加入 運行會報錯, AS上不加入運行沒有問題)
// Linux運行不加入,不報錯,  Win 你必須加入 否則運行報錯,   MacOS 還不知道

jstring // Java <---> native 轉換用的

JNICALL // 代表是 JNI標記,可以少

// Java_包名_類名_方法名  ,注意:我們的包名 _     native _1

// JNIEnv * env  JNI:的橋樑環境    300多個函數,所以的JNI操作,必須靠他

// jobject jobj  誰調用,就是誰的實例  MainActivity this
// jclass clazz 誰調用,就是誰的class MainActivity.class

Java_com_derry_as_1jni_1project_MainActivity_getStringPwd
        (JNIEnv * env, jobject jobj) {


}

// 靜態函數
extern "C"
JNIEXPORT jstring JNICALL
Java_com_derry_as_1jni_1project_MainActivity_getStringPwd2(JNIEnv *env, jclass clazz) {
    // TODO: implement getStringPwd2()
}

extern "C"
JNIEXPORT void JNICALL
Java_com_derry_as_1jni_1project_MainActivity_changeName(JNIEnv *env, jobject thiz) {
   // 獲取class
   jclass j_cls = env->GetObjectClass(thiz);

   // 獲取屬性  L對象類型 都需要L
   // jfieldID GetFieldID(MainActivity.class, 屬性名, 屬性的簽名)
   jfieldID j_fid = env->GetFieldID(j_cls, "name", "Ljava/lang/String;");

   // 轉換工作
   jstring j_str = static_cast<jstring>(env->GetObjectField(thiz ,j_fid));

   // 打印字符串  目標
   char * c_str = const_cast<char *>(env->GetStringUTFChars(j_str, NULL));
    LOGD("native : %s\n", c_str);
    LOGE("native : %s\n", c_str);
    LOGI("native : %s\n", c_str);

    // 修改成 Beyond
    jstring jName = env->NewStringUTF("Beyond");
    env->SetObjectField(thiz, j_fid, jName);

   // printf()  C
   // cout << << endl; // C++
}

extern "C"
JNIEXPORT void JNICALL
Java_com_derry_as_1jni_1project_MainActivity_changeAge(JNIEnv *env, jclass jcls) {

    const char * sig = "I";

   jfieldID j_fid = env->GetStaticFieldID(jcls, "age", sig);

   jint age = env->GetStaticIntField(jcls, j_fid);

   age += 10;

   env->SetStaticIntField(jcls, j_fid, age);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_derry_as_1jni_1project_MainActivity_callAddMethod(JNIEnv *env, jobject job) {
    // 自己得到 MainActivity.class
    jclass  mainActivityClass = env->GetObjectClass(job);

    // GetMethodID(MainActivity.class, 方法名, 方法的簽名)
   jmethodID j_mid = env->GetMethodID(mainActivityClass, "add", "(II)I");

   // 調用 Java的方法
   jint sum = env->CallIntMethod(job, j_mid, 3, 3);
   LOGE("sum result:%d", sum);

}
  • MainActivity
package com.derry.as_jni_project;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

// 生成頭文件:javah com.derry.as_jni_project.MainActivity
public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("native-lib");
    }

    public static final int A = 100;

    public String name = "Derry"; // 簽名:Ljava/lang/String;

    public static int age = 29; // 簽名:I

    // Java 本地方法  實現:native層
    public native String getStringPwd();
    public static native String getStringPwd2();

    // -------------  交互操作 JNI
    public native void changeName();
    public static native void changeAge();
    public native void callAddMethod();


    // 專門寫一個函數,給native成調用
    public int add(int number1, int number2) {
        return number1 + number2 + 8;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

         // Example of a call to a native method
         TextView tv = findViewById(R.id.sample_text);
        changeName();
        tv.setText(name);

        changeAge();
        tv.setText("" + age);

        callAddMethod();
    }

}
簽名規則 大寫
/*
   javap -s -p MainActivity     必須是.class

    Java的boolean  --- Z  注意點
    Java的byte  --- B
    Java的char  --- C
    Java的short  --- S
    Java的int  --- I
    Java的long  --- J     注意點
    Java的float  --- F
    Java的double  --- D
    Java的void  --- V
    Java的引用類型  --- Lxxx/xxx/xx/類名;
    Java的String  --- Ljava/lang/String;
    Java的array  int[]  --- [I         double[][][][]  --- [[[D
    int add(char c1, char c2) ---- (CC)I
    void a()     ===  ()V

    javap -s -p xxx.class    -s 輸出xxxx.class的所有屬性和方法的簽名,   -p 忽略私有公開的所有屬性方法全部輸出
 */
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章