在前面篇章Android Framework层和Native层通过原生socket实现通信和Android Framework层和Native层通过LocalSocket实现通信分别介绍了通过原生socket和LocalSocket实现Android Framework层和和Native层的通信方式,前面两种方式都可以实现跨进程通信,那么今天我们要介绍的就是在Android同一个进程里面通过Jni实现Java世界和Natvie C/C++之间的通信方式。
老规矩,在正式开始代码编程和介绍之前,先奉上最终效果演示,正所谓眼见为实耳听为虚,不放个大招,各位读者咋有一鼓作气看下去的勇气呢,不然看到最好发现是个半成品或者残次品那就不好了。
(1) Jni端 :
C:\Users\xxx.xxx\Desktop
λ adb logcat -s JniNative
--------- beginning of system
--------- beginning of main
I/JniNative( 5258): Java_com_xxx_android2native_JniManager_openJni
I/JniNative( 5258): native_thread_start
I/JniNative( 5258): native thread creat success
I/JniNative( 5258): nativeThreadExec
I/JniNative( 5258): The current process id : 5258
I/JniNative( 5258): The current thread id : -1219081520
I/JniNative( 5258): native_thread_stop
I/JniNative( 5258): thread stoped
(2) Android端 :
λ adb logcat -s JniActivity,JniManager
--------- beginning of system
--------- beginning of main
E/JniActivity( 5258): The current process id : 5258
E/JniActivity( 5258): The current thread id : 1
E/JniManager( 5258): From jni count : 0
E/JniManager( 5258): From jni count : 1
E/JniManager( 5258): From jni count : 2
E/JniManager( 5258): From jni count : 3
E/JniManager( 5258): From jni count : 4
上面的相关打印日志为我们演示了Android通过Jni实现了在同一个进程里面C/C++跨线程回调的实例。好了演示效果已经OK了,下面我们一步步的来讲解怎么实现这种通信方式。
好了言归正传,在Android端我们必须定义好native方法和相关的回调方法,这里最好借助JAVAH生成相关的头文件,当然最好是掌握native方法相关的签名那样是最好了。放上相关代码:
package com.xxx.android2native;
import android.util.Log;
public class JniManager {
private static final String TAG = JniManager.class.getSimpleName();
public native void openJni();//native方法
public native void nativeThreadStart();//native方法
public native void nativeThreadStop();//native方法
public void callByJni(int count) {//供jni回调
Log.e(TAG, "From jni count : " + count);
}
static {
System.loadLibrary("jni");//加载jni库
}
}
好了,前面Android端已经定义好了,Jni的native方法了,那么接下来就是进行具体的实现了,在这里可以有两种注册方法 ,一种是静态的一种是动态的,这里我使用动态注册的,并且实现跨线程回调。具体实现如下。
#include "com_xxx_android2native_JniManager.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define TAG "JniNative"
#define LOGE(TAG,...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
JavaVM *gJavaVM;
jobject gJavaObj;
int volatile gIsThreadStop = 0;
static void* native_thread_exec(void *arg)
{
LOGE(TAG,"nativeThreadExec");
JNIEnv *env;
//从全局的JavaVM中获取到环境变量
gJavaVM->AttachCurrentThread(&env,NULL);
//get Java class by classPath
//获取Java层对应的类
jclass thiz = env->GetObjectClass(gJavaObj);
//get Java method from thiz
//获取Java层被回调的函数
jmethodID nativeCallback = env->GetMethodID(thiz,"callByJni","(I)V");
int count = 0;
//线程循环
while(!gIsThreadStop)
{
sleep(2);
//跨线程回调Java层函数
env->CallVoidMethod(gJavaObj,nativeCallback,count++);
}
gJavaVM->DetachCurrentThread();
LOGE(TAG,"thread stoped");
return ((void *)0);
}
JNIEXPORT void JNICALL Java_com_xxx_android2native_JniManager_nativeThreadStart
(JNIEnv * env, jobject object)
{
LOGE(TAG,"native_thread_start");
gIsThreadStop = 0;
pthread_t id;
//通过pthread库创建线程
if(pthread_create(&id,NULL,native_thread_exec,NULL)!=0)
{
LOGE(TAG,"native thread create fail");
return;
}
LOGE(TAG,"native thread creat success");
}
JNIEXPORT void JNICALL Java_com_xxx_android2native_JniManager_nativeThreadStop
(JNIEnv * env, jobject object)
{
gIsThreadStop = 1;
LOGE(TAG,"native_thread_stop");
}
JNIEXPORT void JNICALL Java_com_xxx_android2native_JniManager_openJni
(JNIEnv * env, jobject object)
{
LOGE(TAG, "Java_com_xxx_android2native_JniManager_openJni");
//注意,直接通过定义全局的JNIEnv和Object变量,在此保存env和object的值是不可以在线程中使用的
//线程不允许共用env环境变量,但是JavaVM指针是整个jvm共用的,所以可以通过下面
//的方法保存JavaVM指针,在线程中使用
env->GetJavaVM(&gJavaVM);
//同理,jobject变量也不允许在线程中共用,因此需要创建全局的jobject对象在线程
//中访问该对象
gJavaObj = env->NewGlobalRef(object);
gIsThreadStop = 0;
}
/*需要注册的函数列表,放在JNINativeMethod 类型的数组中,
以后如果需要增加函数,只需在这里添加就行了
参数:
1.java中用native关键字声明的函数名
2.签名(传进来参数类型和返回值类型的说明)
3.C/C++中对应函数的函数名(地址)
*/
static JNINativeMethod methods[] =
{
{ "openJni", "()V",(void*) Java_com_xxx_android2native_JniManager_openJni },
{ "nativeThreadStart", "()V",(void*) Java_com_xxx_android2native_JniManager_nativeThreadStart },
{ "nativeThreadStop", "()V",(void*) Java_com_xxx_android2native_JniManager_nativeThreadStop },
};
//此函数通过调用RegisterNatives方法来注册我们的函数
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* getMethods, int methodsNum) {
jclass clazz;
//找到声明native方法的类
clazz = env->FindClass(className);
if (clazz == NULL) {
return JNI_FALSE;
}
//注册函数 参数:java类 所要注册的函数数组 注册函数的个数
if (env->RegisterNatives(clazz, getMethods, methodsNum) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
static int registerNatives(JNIEnv* env) {
//指定类的路径,通过FindClass 方法来找到对应的类
const char* className = "com/xxx/android2native/JniManager";
return registerNativeMethods(env, className, methods,
sizeof(methods) / sizeof(methods[0]));
}
//回调函数
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
//获取JNIEnv
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
assert(env != NULL);
//注册函数 registerNatives ->registerNativeMethods ->env->RegisterNatives
if (!registerNatives(env)) {
return -1;
}
//返回jni 的版本
return JNI_VERSION_1_6;
}
有了具体的实现代码,还得有相关的编译脚本,编译脚本Android.mk的实现如下:
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS :=optional
LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
LOCAL_SHARED_LIBRARIES := libcutils liblog libutils libicuuc
LOCAL_LDLIBS := -lm -llog
LOCAL_MODULE:= libjni
LOCAL_SRC_FILES:= com_xxx_android2native_JniManager.cpp
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
如上就是使用Jni实现Android和C/C++通信的全部过程,这种方式在Android中使用的非常普遍,用得太多了,譬如Binder机制中Java和C++的互动,Android多媒体啊等等。熟练掌握Jni的实现技能对深入Android源码理解是必不可少的。本篇介绍的这种通过保存全局JavaVM和jobject实现Jni跨线程回调Java层的方法,只是其中的一种,在接下来的篇幅里面我们将介绍另外的方法来实现Jni跨线程回调Java的方法,尽请期待,下篇不见不散。