Android JNI开发

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android JNI开发相关的知识,希望对你有一定的参考价值。

概述

    在开发framework的时候有时会遇到需要自己开发JNI,以便使Java能够调用自己底层开发的库。网上的文章一般都是介绍如何通过命名规则及javah,使jni层函数与java层函数自动建立链接(Java虚拟机通过命名规则建立),本文将讲解如何动态注册jni函数。

 

依赖库及头文件

    先贴出android.mk的代码

 1 LOCAL_PATH :=$(call my-dir)
 2 include $(CLEAR_VARS)
 3 
 4 LOCAL_SRC_FILES := kiki_jni.cpp  5                    kiki_local.cpp  6 
 7 LOCAL_SHARED_LIBRARIES := libandroid_runtime  8                        libnativehelper  9                        liblog 10 
11 #dependence
12 LOCAL_REQUIRED_MODULES := 13 
14 LOCAL_C_INCLUDES += $(JNI_H_INCLUDE) 15 
16 #add micro define
17 LOCAL_CFLAGS += 18 
19 LOCAL_MODULE := liblearnjni_jni
20 
21 include $(BUILD_SHARED_LIBRARY)

    LOCAL_SHARED_LIBRARIES:需要的共享库,在android中我们用刀libandroid_runtime及libnativehelper两个。

    LOCAL_C_INCLUDES:需要的头文件包含,写$(JNI_H_INCLUDE)即可。

    在Android的JNI架构中,一般会有一个JNI的cpp文件用于沟通上层Java与底层Cpp,具体的业务逻辑实现会放在底层Cpp的另一个文件中,因此这里用kiki_jni.cpp模拟JNI文件桥,kiki_local.cpp模拟实际实现逻辑的文件。

    实际使用的头文件主要有:

#include <jni.h>
#include <JNIHelp.h>
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"

 

JNI注册流程

    JNI注册流程其实很简单,主要分为三个步骤:

    1.实现JNI本地函数

    2.通过JNINativeMethod机构体将Java函数与本地函数一一关联,实现映射表

    3.调用AndroidRuntime::registerNativeMethods方法注册函数映射表

    那么什么时候注册函数映射表呢?一般实现在jint JNI_OnLoad(JavaVM* vm, void* reserved)函数中。这个函数会在动态库被加载后第一时间执行,因此一般会在里面做一些初始化操作。具体代码如下:

 1 using namespace android;
 2 
 3 static const char* const kLearnJni = "android/kiki/LearnJni";
 4 
 5 static jint 
 6 android_kiki_LearnJni_helloJni(JNIEnv *env, jobject thiz, jstring ip, jint port) {
 7     return getMagicNum();
 8 }
 9 
10 static JNINativeMethod gMethods[] = { 
11     {   
12         "helloJni",
13         "(Ljava/lang/String;I)I",
14         (void *)android_kiki_LearnJni_helloJni
15     },  
16 };
17 
18 int register_android_kiki_LearnJni(JNIEnv *env) {
19     return AndroidRuntime::registerNativeMethods(env, kLearnJni, gMethods, NELEM(gMethods));
20 }
21 
22 jint JNI_OnLoad(JavaVM* vm, void* reserved) {
23     JNIEnv *env = NULL;
24     jint res = -1; 
25 
26     if(vm -> GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
27         ALOGE("ERROR: Get java env failed");
28         goto bail;
29     }   
30 
31     if(register_android_kiki_LearnJni(env) <= 0) {
32         ALOGE("ERROR: LearnJni native registration failed");
33         goto bail;
34         
35     }   
36 
37     res = JNI_VERSION_1_4;
38     ALOGD("JNI library onload success");
39 
40 bail:
41     return res;
42 }

 

其他信息

    每个Java进程有一个JavaVM,每个执行线程有一个JNIEnv,可以通过JavaVM获取当前JNIEnv。JNIEnv的本质就是提供出来的JNI操作Java对象的方法和成员变量的API接口类。JNIEnv通过getMethod及get<Type>Field方法获得Java对象的方法及成员变量,通过对应的call<Type>Method方法及set<Type>Field方法操作他们。

    Java函数在JNI中签名对应如下:

技术分享

 

    比较特殊的就是boolean转为Z,long转为J。

 

以上是关于Android JNI开发的主要内容,如果未能解决你的问题,请参考以下文章

android开发源代码分析--多个activity调用多个jni库的方法

Android JNI开发示例

深入浅出 - Android系统移植与平台开发- Android JNI机制

Android JNI开发

Android官方开发文档Training系列课程中文版:Android的JNI相关

Android JNI编程—JNI基础