1. 程式人生 > >Android Jni 例程

Android Jni 例程

1.Jni的作用

Java是一種比較高階的語言,Java呼叫c庫,呼叫c++庫是必不可少的,所以Jni就應運而生了。

  • NDK:NDK是Native Development Kit的縮寫,是Google提供的一套工具集,可以讓你其他語言(C、C++或彙編)開發 Android的 JNI。NDK可以編譯多平臺的so,開發人員只需要簡單修改 mk 檔案說明需要的平臺,不需要改動任何程式碼,NDK就可以幫你編譯出所需的so庫。
  • JNI:JNI是Java本機介面(Java Native Interface),是一個本機程式設計介面,它是Java軟體開發工具箱(Java Software Development Kit,SDK)的一部分。JNI允許Java程式碼使用以其他語言編寫的程式碼和程式碼庫。Invocation API(JNI的一部分)可以用來將Java虛擬機器(JVM)嵌入到本機應用程式中,從而允許程式設計師從本機程式碼內部呼叫Java程式碼。不過,對Java外部的呼叫通常不能移植到其他平臺,在applet中還可能引發安全異常。實現原生代碼將使您的Java應用程式無法通過100%純Java測試。但是,如果必須執行本地呼叫,則要考慮幾個準則:
    1、將您的所有本地方法都封裝到一個類中,這個類呼叫單個的DLL。對每一種目標作業系統平臺,都可以用特定於適當平臺的版本的DLL。這樣可以將原生代碼的影響減少到最小,並有助於將以後所需要的移植問題考慮在內。
    2、本地方法儘量簡單。儘量使您的本地方法對第三方(包括Microsoft)執行時DLL的依賴減少到最小。使您的本地方法儘量獨立,以將載入您的DLL和應用程式所需的開銷減少到最小。如果需要執行時DLL,必須隨應用程式一起提供.

2.生成一個JNI 的libjni.so庫

我這個例子主要是在有android原始碼的編譯環境下測試的,我有Android 7.1的開發編譯環境,如果大家做Android 系統開發或者應用開發,有一個Android的開發環境非常有必要,或者需要自己下載Android NDK。

2.1.在原始碼位置下新建一個原始碼路徑

程式碼結構如圖

HelloWorld.java原始碼如下

package com.wqf.hellojni;

public class HelloWorld{
    
    public static native String helloWorld();
    public static native int add(int a,int b);
    
}

2.2 用命令編譯生成標頭檔案

1、用命令javac com/wqf/hellojni/HelloWorld.java,生成HelloWorld.class
2、用命令javah -classpath . -jni com.wqf.hellojni.HelloWorld生成標頭檔案
編譯後如圖

注意:
網上很多說明用javah -jni com.wqf.hellojni.HelloWorld 生成標頭檔案,我試過很多次,都是失敗,還是用我上面那個命令,可以看看javah的命令幫助
在這裡插入圖片描述

2.3 編譯生成libjni.so庫步驟

2.3.1 在jni目錄裡面建立幾個檔案如下

1、把上面生成的com_wqf_hellojni_HelloWorld.h放到這個目錄來
2、新建一個jni.c檔案用來實現上面標頭檔案裡面的方法
3、新建一個Android.mk檔案用來編譯

Android.mk的內容如下

#########################################
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= jni.c
LOCAL_MODULE := libjni 
include $(BUILD_SHARED_LIBRARY)  

2.3.2 jni.c的內容如下

//自己建立一個c檔案,實現自己定義的native方法,也就是.h檔案中的方法
//引入自己生成的.h標頭檔案
#include "com_wqf_hellojni_HelloWorld.h"

//返回一個字串
JNIEXPORT jstring JNICALL Java_com_wqf_hellojni_HelloWorld_helloWorld(JNIEnv *env, jclass jobj) {
      return (*env)->NewStringUTF(env,"HelloWorld 我是用jni調用出來的字串");
}
//返回 a+b的結果
JNIEXPORT jint JNICALL Java_com_wqf_hellojni_HelloWorld_add(JNIEnv *env, jclass jobj, jint a, jint b){
     return a+b;
}

2.3.3 編譯

使用mm -B 命令編譯生成so檔案編譯結果如下圖
編譯結果生成的so在out/target/product/rk3399_mid/obj/libjin.so
說個小細節,我們編譯生成的libjni.so,我們要會看他的幾個引數
1、檔案型別,使用file命令檢視是32位的庫還是64位的庫
在這裡插入圖片描述
2、使用md5sum命令檢視md5碼,這個可以檢視檔案的唯一md5碼,以後遇到一個檔名字一樣的可以用這個方式知道是不是同一個檔案
在這裡插入圖片描述

3. 寫個簡單的apk測試一下

我們的APK編寫用的是android studio來編寫的,現在android studio是非常主流的android開發工具,如果大家想自己做一個小的DEMO的話,建議裝這個工具,網上安裝流程非常多了,下載SDK後,最好不要放在C盤,要不然重灌系統後又沒有了。

3.1先來一個整體的執行效果圖片

在這裡插入圖片描述
我們在圖片上可以看到add = 9,這個是我們在JNI裡面的輸出結果
在這裡插入圖片描述

3.2 APK編譯步驟和幾個注意的細節

我是在Android 7.1上編寫APK進行測試的,相比其他Android版本,Android 7.1的問題更多,我說下操作步驟和細節。

1、我們先編寫一個可以執行的APK

我們需要編寫一個helloworld的簡單apk,這個apk不需要引用庫檔案,直接輸出一個helloworld。
在這裡插入圖片描述

2、然後我們引用我們的庫檔案

這裡注意的是HelloWorld.java 裡面的內容
載入庫的程式碼

package com.wqf.hellojni;

public class HelloWorld{

    static {
        //名字必須和libjni.so 名字對應起來
        System.loadLibrary("jni");
    }

    public static native String helloWorld();
    public static native int add(int a,int b);

}

在MainActivity.java裡面匯入包並呼叫add函式
在這裡插入圖片描述

4. 原始碼連線

原始碼我放在github上面,方面檢視,有原始碼的例程大家研究起來更加得心應手
https://github.com/weiqifa0/androidjni

參考:
https://blog.csdn.net/a_zhon/article/details/53097512#

5.鳴謝

覺得不錯的可以關注我的微信公眾號【嵌入式Linux】關注後可以獲取二維碼加入微信技術群