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目錄即可。
這裏寫圖片描述

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