天天看點

Android JNI開發二:實戰開發詳解

調用原理

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目錄即可。

Android JNI開發二:實戰開發詳解

繼續閱讀