android JNI NDK入門

1、JNI(Java Native Interface) Java本地接口,又叫Java原生接口。它允許Java調(diào)用C/C++的代碼,同時(shí)也允許在C/C++中調(diào)用Java的代碼??梢园袹NI理解為一個(gè)橋梁,連接Java和底層

JNI的書寫步驟如下:
a.編寫帶有native聲明的方法的Java類
b.使用javac命令編譯編寫的Java類
c.使用java -jni ****來生成后綴名為.h的頭文件
d.引入.h的頭文件,使用其他語言(C、C++)實(shí)現(xiàn)本地方法
e.將本地方法編寫的文件生成動(dòng)態(tài)鏈接庫

class HelloWorld
{
    //native聲明,用于生成c/c++代碼
    public native void sayHelloWorld();

    //加載c/c++編譯好的庫
    static
    {
        System.loadLibrary("Dll1");
    }

    public static void main(String[] args)
    {
        new HelloWorld().sayHelloWorld();
    }
}

在Androidstudio Terminal直接運(yùn)行
運(yùn)行指令: javah -jni 包名.類名(直接放入java文件夾內(nèi)不用包名)

javah -jni HelloWorld
得到HelloWorld.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    sayHelloWorld
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_sayHelloWorld
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

使用visual studio創(chuàng)建一個(gè)dll項(xiàng)目
引入HelloWorld.h實(shí)現(xiàn)jni接口

JNIEXPORT void JNICALL Java_HelloWorld_sayHelloWorld
  (JNIEnv *, jobject){

    printf("Hello World !");
    return;
}

生成dll包,我這邊叫Dll1.dll。放到j(luò)ava目錄,就可以實(shí)現(xiàn)打印hello world了

2,為了使您能夠在 Android 應(yīng)用中使用 C 和 C++ 代碼,并提供眾多平臺(tái)庫(arm64-v8a x86等),需要、Android NDK 就是一套工具集合配置才能打出Android 平臺(tái)適用的so。

1、android端這邊的jni接口

public class JNITools {
    static {
       System.loadLibrary("jniyjn");
    }

    //加法
    public static native int  add(int a,int b);

    //減法
    public static native int sub(int a,int b);

    //乘法
    public static native int mul(int a,int b);

    //除法
    public static native int div(int a,int b);
}

2、在main下創(chuàng)建一個(gè)jni文件夾

2.1創(chuàng)建Android.mk文件

Android.mk文件是用來指定諸如編譯生成so庫名、引用的頭文件目錄、需要編譯的.c/.cpp文件和.a靜態(tài)庫文件等。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := jniyjn

LOCAL_SRC_FILES := jnitools.c

LOCAL_LDLIBS :=  -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)

3、創(chuàng)建一個(gè)c++文件,并實(shí)現(xiàn)注冊jni接口,并實(shí)現(xiàn)接口的功能

jni開發(fā)中C/C++ 之所以需要頭文件(.h),有兩個(gè)用處,一個(gè)是在開發(fā)編譯的時(shí)候,在各個(gè)編譯單元(Compile Unit)之間共享同樣的定義;一個(gè)是在發(fā)布程序庫的時(shí)候,讓使用者知道調(diào)用接口。本次使用中我們沒有使用java -jni ****來生成后綴名為.h的頭文件,所以需要注冊.

3.1 靜態(tài)注冊:

通過 JNIEXPORT 和 JNICALL 兩個(gè)宏定義聲明,在虛擬機(jī)加載 so 時(shí)發(fā)現(xiàn)上面兩個(gè)宏定義的函數(shù)時(shí)就會(huì)鏈接到對(duì)應(yīng)的 native 方法。

3.2 動(dòng)態(tài)注冊:

先通過JNI重載JNI_OnLoad()實(shí)現(xiàn)本地方法,然后直接在Java中調(diào)用本地方法。(當(dāng)前例子通過動(dòng)態(tài)注冊,更靈活)

jintools.c文件

#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <stdlib.h>


jint addNumber(JNIEnv *env,jclass clazz,jint a,jint b);

jint subNumber(JNIEnv *env,jclass clazz,jint a,jint b);

jint mulNumber(JNIEnv *env,jclass clazz,jint a,jint b);

jint divNumber(JNIEnv *env,jclass clazz,jint a,jint b);




JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){

    //打印日志,說明已經(jīng)進(jìn)來了
    __android_log_print(ANDROID_LOG_DEBUG,"JNITag","enter jni_onload");

    JNIEnv* env = NULL;
    jint result = -1;

    // 判斷是否正確
    if((*vm)->GetEnv(vm,(void**)&env,JNI_VERSION_1_6)!= JNI_OK){
        return result;
    }

    //注冊四個(gè)方法,注意簽名
    const JNINativeMethod method[]={
            {"add","(II)I",(void*)addNumber},
            {"sub","(II)I",(void*)subNumber},
            {"mul","(II)I",(void*)mulNumber},
            {"div","(II)I",(void*)divNumber}
    };

    //找到對(duì)應(yīng)的JNITools類  com.example.myapplication
    jclass jClassName=(*env)->FindClass(env,"com/example/jniyjn/JNITools");

    //開始注冊
    jint ret = (*env)->RegisterNatives(env,jClassName,method, 4);

     //如果注冊失敗,打印日志
    if (ret != JNI_OK) {
        __android_log_print(ANDROID_LOG_DEBUG, "JNITag", "jni_register Error");
        return -1;
    }

    return JNI_VERSION_1_6;
}


jint addNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a+b;
}

jint subNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a-b;
}

jint mulNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a*b;
}

jint divNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a/b;
}

4、Android 端使用

4.1 app下的buildgradle引入

defaultConfig下加入

        ndk{
            moduleName "jniyjn"
            abiFilters 'x86','armeabi-v7a','arm64-v8a'
            ldLibs "log"
        }

buildTypes下加入

        externalNativeBuild {
            ndkBuild {
                path 'src/main/jni/Android.mk'
            }
        }

4.2 調(diào)用

int result = JNITools.add(a, b);

5、第三方so的使用

常見的開源第三方c++代碼,可以使用linux配置打包,打出相應(yīng)平臺(tái)的so文件和一個(gè)jar包。這里基本上第三方平臺(tái)會(huì)把jni接口寫在jar中,native的實(shí)現(xiàn)在so中。Android使用時(shí)只要直接引入so和jar既可以直接調(diào)用。

一般的開源c++項(xiàng)目已經(jīng)寫好jni接口并實(shí)現(xiàn),直接打出so和jar,android引入既可用。

對(duì)于開源c++項(xiàng)目沒有實(shí)現(xiàn)jni接口的,又想通過JAVA程序調(diào)用C/C++動(dòng)態(tài)庫的話,則需要使用SWIG,Simplified Wrapper and Interface Generator,這是一個(gè)封裝C/C++動(dòng)態(tài)庫供其他編程語言調(diào)用的神器。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容