JNI连接着java和c++/c层。Android有大量驱动必须在native层实现,还有一些注重性能、功耗的功能在c++/c实现比较好。
从java到native建立的是函数关系。但是从natvie到java,必须先得到java的对象引用,才能调用该对象的方法。
通过jni调用是在同一个线程中的,但是可能打印出来的线程id不同。这是语言的关系。
1.装载JNI动态库
为了使用JNI,在调用本地方法前。必须把c/c++代码所在的动态库装载到内存中。
public static void loadLibrary(String libName)
传入的libName,在linux上为libmyjni.so在windows上是myjni.dll。这边是因为java是跨平台的所以只用了中间部分相同的名字。
一般将loadLibrary放在static域中,这样能在进程初始化的时候就加载了。
static {
System.loadLibrary("my_jni");
}
2.定义native方法
Java类中native方法很简单
private static native final void init()
3.动态注册JNI函数
JNI动态库和非JNI动态库区别:JNI动态库中定义了一个JNI_Onloader函数,在动态库加载的时候被调用。用于完成JNI函数的注册。
jint JNI_Onload(JavaVM* vm, void *)
这个函数中最重要的一件事就是调用registerNativeMethods函数来完成JNI函数注册。就是将java函数和native函数一一对应。
static JNINativeMethod gMethods[] = {
{"enableFunc", "(IZ)Z",(void*)native_enableFunc},
{"disableFunc", "(IZ)Z",(void*)native_disableFunc},
};
jint JNI_Onload(JavaVM* vm, void*) {
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("GetEnv failed!");
return result;
}
if(AndroidRuntime::registerNativeMethods(env, "com/android/TestService",gMethods,NELEM(gMethods))>=0) {
result = JNI_VERSION_1_4
}
return result;
}
其中gMethods的类型定义如下:
typedef struct {
const char* name;//java中对应的函数名
const char* signature;//签名,入参和返回值
void* fnPtr;//native函数指针
} JNINativeMethod;
对应的类型,其中"["代表数组的意思。(IZ)Z,代表入参是int boolean 返回值是boolean
Z boolean
C char
B byte
S short
I int
J long
F float
D double
下面看看Android中Alarm的jni方法注册:
#include "JNIHelp.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_AssetAtlasService(JNIEnv* env);
int register_android_server_BatteryStatsService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputApplicationHandle(JNIEnv* env);
int register_android_server_InputWindowHandle(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
int register_android_server_PersistentDataBlockService(JNIEnv* env);
int register_android_server_fingerprint_FingerprintService(JNIEnv* env);
int register_android_server_Watchdog(JNIEnv* env);
};
using namespace android;
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("GetEnv failed!");
return result;
}
ALOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_PowerManagerService(env);
register_android_server_SerialService(env);
register_android_server_InputApplicationHandle(env);
register_android_server_InputWindowHandle(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
register_android_server_AlarmManagerService(env);
register_android_server_UsbDeviceManager(env);
register_android_server_UsbHostManager(env);
register_android_server_VibratorService(env);
register_android_server_SystemServer(env);
register_android_server_location_GpsLocationProvider(env);
register_android_server_location_FlpHardwareProvider(env);
register_android_server_connectivity_Vpn(env);
register_android_server_AssetAtlasService(env);
register_android_server_ConsumerIrService(env);
register_android_server_BatteryStatsService(env);
register_android_server_hdmi_HdmiCecController(env);
register_android_server_tv_TvInputHal(env);
register_android_server_PersistentDataBlockService(env);
register_android_server_fingerprint_FingerprintService(env);
register_android_server_Watchdog(env);
return JNI_VERSION_1_4;
}
下面是具体的Alarm的JNI方法注册
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"init", "()J", (void*)android_server_AlarmManagerService_init},
{"close", "(J)V", (void*)android_server_AlarmManagerService_close},
{"set", "(JIJJ)V", (void*)android_server_AlarmManagerService_set},
{"waitForAlarm", "(J)I", (void*)android_server_AlarmManagerService_waitForAlarm},
{"setKernelTime", "(JJ)I", (void*)android_server_AlarmManagerService_setKernelTime},
{"setKernelTimezone", "(JI)I", (void*)android_server_AlarmManagerService_setKernelTimezone},
};
int register_android_server_AlarmManagerService(JNIEnv* env)
{
return jniRegisterNativeMethods(env, "com/android/server/AlarmManagerService",
sMethods, NELEM(sMethods));
}
4.jstring的常用方法
NetStringUTF将根据一个UTF-8字符串得到一个jstring对象。输入就是本地字符串。
GetStringUTFChars函数可以将jstring对象转换成本地字符串
RealeaseStringUTFChars对应释放本地字符串。
const char* pathstr = env->GetStringUTF(path, NULL);//其中path是jstring类型
//最后调用释放
env->ReleaseStringUTFChars(path,pathstr);
5.调用java方法
NewObject生成一个java对象
jobject NewObject(jclass clazz, jmethod methodID,..)
methodID是构造函数
FindClass
jclass FindClass(const char* name);
例如:输入参数为"/java/lang/String"
调用一个java对象的方法之前需要知道方法或者域变量的Id。
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig);
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig);
其中name是类的方法名或是成员变量名
调用java类的方法
jchar CallCharMethod(jobject object, jmethodID, methodID,.......);
jboolean CallBooleanMethod.....
....
void CallVoidMethod
静态方法:
jchar CallStaticCharMethod(jclass clazz, jmethodID, methodID,.......);
jboolean CallStaticBooleanMethod.....
....
void CallStaticVoidMethod
获取成员变量:
jobject GetObjectField(jobject obj, jfieldID fieldID);
.......
设置成员变量
void SetObjectField(jobject obj, jfieldID fieldID, jobject value);
.......
得到静态变量值:
jobject GetStaticObjectField(jclass clazz, jfieldID fieldID);
.......
设置静态变量值:
void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value);
.......
6.JNI中的引用
static jobject save_this = NULL;//定义一个全局的jobject
static void android_media_MediaScanner_processFile(JNIEnv* env, jobject thiz, jstring path, jstring mimeType, jobject client)
{
.......
save_this = thiz;//保存java层传入的jobject对象
.......
return;
}
//假设在某个时间,有个地方调用callMediaScanner函数
void callMediaScanner() {
//在这个函数里面操作save_this有问题吗
}
上面这种做法肯定有问题,因为save_this是java层传过来的,很有可能被垃圾回收了。也就是save_this可能是一个野指针。而在c++层使用该对象是不会增加引用计数的。
这种情况可以使用全局引用和局部引用。
MyMediaScannerClient(JNIEnv* env, jobject client) : mEnv(env),
mClient(env->NewGlobalRef(client)),//调用NewGlobalRef创建一个全局引用,这样就不会被垃圾回收了
mScanFileMethodID(0)
{
...........
}
virtual ~MyMediaScannerClient()
{
mEnv->DeleteGlobalRef(mClient);//释放全局引用
}
局部引用的话使用DeleteLocalRef
jstring pathstr;
pathstr = mEnv->NewStringUTF(path);//新建一个局部变量
mEnv->DeleteLocalRef(pathstr);//释放局部变量