基本概念
JNI
Java Native Interface纲菌,Java本地接口试疙。
是為了方便Java調(diào)用C柳弄、C++等本地代碼所封裝的一層接口唱捣。
NDK
Android提供的一個(gè)工具集合两蟀,更加方便地在Android中實(shí)現(xiàn)JNI。
NDK提供了交叉編譯器震缭,開發(fā)人員可將JNI代碼生成各指定CPU平臺(tái)的動(dòng)態(tài)庫赂毯。
加載本地庫
Java代碼中通過
System.loadLibrary("library-name")
來導(dǎo)入本地庫。注意此處的library-name是本地庫的名字拣宰,不用包含后綴党涕,Java將根據(jù)當(dāng)前系統(tǒng)的類型自動(dòng)擴(kuò)展后綴名,如Linux擴(kuò)展成.so巡社,Windows擴(kuò)展成.dll遣鼓。
若要導(dǎo)入Linux下的libxxx.so,只要寫
System.loadLibrary("xxx")
即可重贺。
Native方法
-
定義Native方法有兩種方式
函數(shù)命名規(guī)則來匹配和映射;
運(yùn)行時(shí)動(dòng)態(tài)注冊(cè)(RegisterNatives)。
Java類中聲明native方法
- 在開發(fā)Native方法前气笙,先創(chuàng)建一個(gè)Java類次企,在此類中聲明與Native方法對(duì)應(yīng)的Java本地方法。
package com.daking.jni;
public class JNIUtil {
static {
System.loadLibrary("nativecode"); // 加載libnativecode.so
}
public static native String getLibName();
}
1. 函數(shù)命名規(guī)則來匹配和映射Native方法
- 匹配和映射Native方法的c函數(shù)命名規(guī)則為:
JNIEXPORT 返回類型 JNICALL Java_{packageName}_{className}_{functionName}(jni參數(shù)列表);
- 上面的Java類聲明的native方法對(duì)應(yīng)的c函數(shù)為:
JNIEXPORT jstring JNICALL Java_com_daking_jni_JNIUtil_getLibName(JNIEnv* env, jobject obj);
-
該c函數(shù)的特點(diǎn)是:
返回類型為jstring等JNI類型潜圃;
將{packageName}中的
.
全部改為_
缸棵。JNI方法的形參列表至少有以下兩個(gè)參數(shù):
-
JNIEnv* env
:JNI環(huán)境的引用,用env可調(diào)用所有JNI提供的函數(shù)谭期;
-
jobject obj
:調(diào)用該函數(shù)的對(duì)象的引用堵第。
-
2. 運(yùn)行時(shí)動(dòng)態(tài)注冊(cè)Native方法
運(yùn)行時(shí)動(dòng)態(tài)注冊(cè)的方式簡(jiǎn)化了函數(shù)名稱,并且可以動(dòng)態(tài)地更新映射關(guān)系隧出。
首先踏志,在C代碼中實(shí)現(xiàn)各Native方法。
jstring getLibName(JNIEnv* env, jobject obj) {
return (*env)->NewStringUTF(env, "hello world!");
}
- 接著胀瞪,創(chuàng)建JNINativeMethod數(shù)組针余。
static JNINativeMethod jniMethods[] = {
{"getLibName", "()Ljava/lang/String;", (void*)getLibName}
// 其他Java本地方法與C Native方法的映射...
};
-
此處的JNINativeMethod結(jié)構(gòu)有三個(gè)成員:
- const char* name:Java中聲明的native方法名
- const char* signature:native方法的簽名
- void* fnPtr:c函數(shù)指針
最后,在JNI_OnLoad中調(diào)用
RegisterNatives()
注冊(cè)各Native方法到JVM凄诞,建立映射關(guān)系圆雁。
static const char* className = "com/daking/jni/JNIUtil";
int JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4) != JNI_OK) {
return JNI_ERR;
}
jclass cls = (*env)->FindClass(env, className);
if (cls == NULL)
return JNI_ERR;
int len = sizeof(jniMethods) / sizeof(jniMethods[0]);
(*env)->RegisterNatives(env, cls, jniMethods, len);
return JNI_VERSION_1_4;
}
NDK Sample Project
1. 創(chuàng)建Android項(xiàng)目
- Android項(xiàng)目中創(chuàng)建JNIUtil類,作為調(diào)用JNI的入口工具類帆谍。
package com.daking.jni;
public class JNIUtil {
static {
System.loadLibrary("nativecode");
}
public static native String getLibName();
}
2. 創(chuàng)建JNI項(xiàng)目
- JNI項(xiàng)目的目錄結(jié)構(gòu)如下:
JNIDemo
├── jni
│ ├── Android.mk
│ ├── Application.mk
│ ├── include
│ │ └── api.h
│ └── src
│ └── api.c
-
api.h
文件內(nèi)容如下:
#include <jni.h>
#ifndef NATIVECODE_API_H
#define NATIVECODE_API_H
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL
Java_com_daking_jni_JNIUtil_getLibName(JNIEnv*, jobject);
#ifdef __cplusplus
}
#endif
#endif //NATIVECODE_API_H
-
api.c
文件內(nèi)容如下:
#include "api.h"
#include <stdio.h>
JNIEXPORT jstring JNICALL
Java_com_daking_recyclerdemo_jni_getLibName(JNIEnv* env, jobject thiz) {
return (*env)->NewStringUTF(env, "libnativecode.so");
}
-
Android.mk
文件內(nèi)容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := nativecode
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_SRC_FILES := ./src/api.c
include $(BUILD_SHARED_LIBRARY)
-
Application.mk
文件內(nèi)容如下:
APP_ABI := armeabi
- 在JNI項(xiàng)目中與jni子目錄同級(jí)的路徑下伪朽,執(zhí)行
ndk-build
,生成so庫汛蝙。
3. Android項(xiàng)目引用so庫
將JNI項(xiàng)目生成的so庫放到
Android項(xiàng)目/app/src/main/jniLibs/<abi>
下即可烈涮。<abi>
有armeabi、armeabi-v7a患雇、arm64-v8a跃脊、x86、x86_64苛吱、mips酪术、mips64。