Android JNI學習-基礎篇

  • 簡介
    JNI(Java Native Interface)顧名思義java本地接口,說白了就是javac/c++ 通過JNI接口的規範實現通信,本文將實現最基本的 java調c的函數,c調java的函數
  • 相關知識
    C/C++基礎

編寫JAVA代碼

創建java文件 → 加載動態庫 → 定義本地函數 → 調用本地方法

package com.example.test;

public class MainActivity extends AppCompatActivity {

    //加載動態庫
    static {
        System.loadLibrary("test");
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //調用本地方法
        String str = javaCallNative("hello jni");
        Log.i("xxxMainActivity", str + " --> javaCallNative");
    }

    /**
     * 定義本地函數
     */
    public native String javaCallNative(String str);

    /**
     * 定義被本地調用的函數
     */
    public static void nativeCallJava(String str) {
        Log.i("xxxMainActivity", str + " --> nativeCallJava");
    }
}

編寫C代碼

根據jni規範,實現Java 中定義的函數

  • 在main目錄下面,創建jni文件夾

  • 在jni下面 , 創建 test.h 文件

    
    /**
     * 是否已經引用了test.h這個文件了,防止多次引用
     * 一般是
     * #ifndef XXX_H
     * #define XXX_H
     *
     * ...
     *
     * #endif
     *
     */
    #ifndef TEST_H
    #define TEST_H
    
    #include <jni.h>//引用jni.h
    
    /**
     * 聲明方法,具體在同名.cpp裏面實現,
     * 注意函數名規範 -->Java_對應java方法的絕對路徑(JNIEnv *env , jobject p0 , 對應java方法的參數)
     */
    extern "C"
    JNIEXPORT jstring JNICALL
    Java_com_example_test_MainActivity_javaCallNative(JNIEnv *env, jobject p0, jstring p1);
    
    
    #endif
    
    
  • 在jni下面 , 創建 test.cpp文件

    #include "test.h"
    
    /**
     * 定義c++函數  注意函數名規範 -->Java_對應java方法的絕對路徑(JNIEnv *env , jobject p0 , 對應java方法的參數)
     */
    extern "C"
    JNIEXPORT jstring JNICALL
    Java_com_example_test_MainActivity_javaCallNative(JNIEnv *env, jobject jobj, jstring p1) {
        //通過反射調用java中的方法
        //找class 使用FindClass方法,參數就是要調用的函數的類的完全限定名,但是需要把點換成/
        jclass clazz = env->FindClass("com/example/test/MainActivity");
        //獲取對應的函數: 參數1:類class,參數2:方法名,參數3:方法簽名
        jmethodID method = env->GetStaticMethodID(clazz, "nativeCallJava", "(Ljava/lang/String;)V");
        //調用返回空的靜態方法
        env->CallStaticVoidMethod(clazz, method, p1);
        return p1;
    }
    

建立Android.mk文件

在jni下面 , 創建 Android.mk 文件,將本地C代碼編譯成.so的動態庫

#LOCAL_PATH定義本地路徑,  $(call my-dir)是一個函數,意思是獲取當前函數
LOCAL_PATH := $(call my-dir)
#CLEAR_VARS由編譯系統提供,指定讓GNU MAKEFILE爲你清除許多LOCAL_XXX變量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。這是必要的,因爲所有的編譯控制文件都在同一個GNU MAKE執行環境中,所有的變量都是全局的。
include $(CLEAR_VARS)
#LOCAL_MODULE變量必須定義,以標識你在Android.mk文件中描述的每個模塊。名稱必須是唯一的,而且不包含任何空格。注意編譯系統會自動產生合適的前綴和後綴,換句話說,一個被命名爲'foo'的共享庫模塊,將會生成'libfoo.so'文件。
LOCAL_MODULE := test
#LOCAL_SRC_FILES變量必須包含將要編譯打包進模塊中的C或C++源代碼文件。注意,你不用在這裏列出頭文件和包含文件,因爲編譯系統將會自動爲你找出依賴型的文件;僅僅列出直接傳遞給編譯器的源代碼文件就好。
LOCAL_SRC_FILES := test.cpp
#BUILD_SHARED_LIBRARY 由編譯系統提供,表示編譯一個分享庫(動態庫.so)
include $(BUILD_SHARED_LIBRARY)

Android.mk官方文檔

建立Application.mk文件

指定 ndk-build 的項目範圍設置。默認情況下,它位於應用項目目錄中的 jni下
注意: Gradle 的 externalNativeBuild會忽略APP_ABI
注意: 使用 externalNativeBuild 進行編譯時,Android Studio 將根據您的編譯風格適當地設置此標記。

#Application Binary Interface編譯CPU架構
APP_ABI := armeabi
#指定目標Android API版本
APP_PLATFORM := android-14
#指定要使用的stl庫,默認爲system
APP_STL := system
#優化選項,默認值爲release。可選值爲debug和release。release模式下文件體積更小,debug模式下提供更多的調試信息
APP_OPTIM := release

Application.mk 官方介紹

在build.gradle 文件增加執行函數

build.gradle文件下增加執行函數,通過gradle自動編譯

android {
...

	defaultConfig {
   ...

        //設置編譯所支持的CPU架構
        ndk {
            abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
        }

    }
...
    externalNativeBuild {
    	//編譯的Android.mk文件地址
        ndkBuild {
            path file("src/main/jni/Android.mk")
        }
    }
...

}

點擊sync now

當你修改了build.gradle文件,右上角就會出現sync now ,點擊就可以了

如果報沒有ndk,就需要添加ndk

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章