Android Java层和Native层通信实战大荟萃之JNI实现通信

Android Java层和Native层通信实战大荟萃之JNI实现通信


Android Java层和Naitve层通信之实战大荟萃目录:

Android Java层和Native层通信实战大荟萃之MessageQueue实现通信
Android Java层和Native层通信实战大荟萃之JNI实现通信
Android Java层和Native层通信实战大荟萃之LocalSocket实现通信
Android Java层和Native层通信实战大荟萃之原生socket实现通信



引言

   在前面篇章Android Java层和Native层通信实战大荟萃之原生socket实现通信Android Java层和Native层通信实战大荟萃之原生socket实现通信分别介绍了通过原生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端编程

   好了言归正传,在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库
    }
}




Jni端编程

   好了,前面Android端已经定义好了,Jni的native方法了,那么接下来就是进行具体的实现了,在这里可以有两种注册方法 ,一种是静态的一种是动态的,这里我使用动态注册的,并且实现跨线程回调。具体实现如下。

#include "com_xxx_android2native_JniManager.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <android/log.h>
#include <jni.h>
#include <assert.h>
#include <pthread.h>

#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的方法,尽请期待,下篇不见不散。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值