調用原理
java代碼編譯後,運行時會去某個目錄尋找so文件、並load,然後調用so文件中的方法,要保證這個流程準確無誤,就必須約定好如下條件:
- java代碼運行時,知道去哪裏加載so庫 —— so庫存放目錄;
- java代碼運行時,知道加載哪個so文件 —— so庫名稱;
- java代碼運行時,知道如何正確調用某個方法 —— so庫的方法名、參數、返回值;
第1個條件是android系統幫我們完成的,apk安裝時,會把so庫解壓到/data/app/com.example.test/lib/ABI/目錄下,運行時也會自動去這個目錄下加載so庫;
第2、3個條件則是我們自己開發時需要約定好的。具體如何約定,後面會講到。
實現步驟
知道了調用原理後,開發步驟就很容易了,大致分爲如下幾步:
- java代碼加載so文件(約定so文件名),聲明native方法(約定方法名、參數和返回值);
- 生成C/C++頭文件,聲明方法(方法名、參數和返回值與java代碼約定的一致);
- 實現C/C++頭文件中聲明的方法;
- 將C/C++文件編譯成Android平臺下的so文件(so文件名與java代碼約定的一致);
- 將編譯好的so文件放入對應的jniLib目錄下
java代碼加載so文件
package com.example.mytest;
public class JniTest {
static {
// load名稱爲useffmpeg的so文件。最終so文件名會在前面加上lib、後面加上.so,即libuseffmptg.so
System.loadLibrary("useffmpeg");
}
// 聲明本地方法名稱、參數、返回值
public native String getFFMpegInfo();
}
生成C/C++頭文件
通過javah命令,可以生成JniTest.java對應的C/C++頭文件,不過我覺得用代碼寫還方便一點。
頭文件命名爲com_example_mytest_JniTest.h,命名規則就是包名+類名,用下劃線代替.號,代碼如下:
/* 引用jni庫*/
#include <jni.h>
extern "C" {
#endif
/*
* 1.jstring是方法返回值,對應java代碼中native方法的返回值String;
* 2.Java_com_example_mytest_JniTest_getFFMpegInfo就是聲明的方法名,對應java代碼中的包名、類名和方法名;
* 3.JNIEnv *, jobject是必帶的參數,沒有其它參數對應的就是java代碼中的native方法沒有參數;
*/
JNIEXPORT jstring JNICALL Java_com_example_mytest_JniTest_getFFMpegInfo (JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
實現C/C++頭文件中的方法
具體實現類命名com_example_mytest_JniText.c,實現代碼如下:
/* 引入上面的.h文件 */
#include "com_example_mytest_JniTest.h"
/**
* 實現上面頭文件聲明的方法
*/
JNIEXPORT jstring JNICALL Java_com_example_mytest_JniTest_getFFMpegInfo (JNIEnv *env, jobject obj) {
/* NewStringUTF是jni.h庫中的方法。*/
/*至於本地方法如何用C代碼實現,那就要有一定C語言功底了,這裏只是簡單地返回一個字符串 */
return (*env)->NewStringUTF(env, "jni測試");
}
編譯so文件
編譯Android平臺的so文件需要四個文件:
- C/C++頭文件。作用是約定方法名、參數和返回值;
- 上面聲明的方法的具體實現類。用來編譯成具有實際功能的二進制文件;
- Android.mk文件。作用是約定so文件名稱,以及一些其它配置;
- Application.mk文件。作用是配置編譯的ABI;
Android.mk文件如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
// 約定so文件的名稱
LOCAL_MODULE := useffmpeg
// so文件的實現類
LOCAL_SRC_FILES := com_example_mytest_JniText.c
// 編譯so的時候,記得把註釋、中文、空行全刪掉,有些情況下可能會編譯不過
include $(BUILD_SHARED_LIBRARY)
Application.mk文件如下:
// 編譯所有ABI的so庫
APP_ABI := all
將這4個文件放在同一個目錄下,打開命令行,進入到該目錄下,使用命令“ndk-build”命令即可編譯出各個ABI的so庫。
使用so文件
將so文件放入jniLIbs目錄下,在gradle文件中指定jniLibs目錄即可。