当前位置:   article > 正文

Android Java层和Native层通信实战大荟萃之MessageQueue实现通信_android native进程与java进程间通信

android native进程与java进程间通信

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


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

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


   在前面篇章Android Java层和Native层通信实战大荟萃之JNI实现通信介绍了Jni通过全局的jobject和JavaVM来实现跨线程回调Java的方法和完整代码,上述方法在Android的源代码中实际使用并不是非常广泛(重点:这里不是说Jni用的不是很广泛,说的是这种全局的跨线程回调Java的方法),使用比较广泛的是Native层的Looper中addFd方法监听事件,然后回调Java世界。




实际效果演示

   老规矩,在正式开始代码编程和介绍之前,先奉上最终效果演示,正所谓眼见为实耳听为虚,不放个大招,各位读者咋有一鼓作气看下去的勇气呢,不然看到最好发现是个半成品或者残次品那就不好了。

(1).Jni端

λ adb logcat  -s JniNative,MainStreamThread
--------- beginning of system
--------- beginning of main
I/JniNative( 5118): init called, pid=5118 uid=1000 gid=1000 pthread_id=-1225118740
I/MainStreamThread( 5118): The current thread Name : MainStreamThread
I/MainStreamThread( 5118): enter MainStreamThread::onFirstRef
I/MainStreamThread( 5118): init called, pid=5118 uid=1000 gid=1000 pthread_id=-1201086200
I/MainStreamThread( 5118): write_work: cmd=1
I/JniNative( 5118): ------ CMD_NOTIFY_NATIVE_CALLBACK Hello Java, Im from Native ------

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

(2.Android端

λ adb logcat  -s  JniActivity
--------- beginning of system
--------- beginning of main
E/JniActivity( 5118): The current process id :  5118
E/JniActivity( 5118): The current thread id :  1
E/JniActivity( 5118): notifyNativeCall : Hello Java, Im from Native
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

上面的相关打印日志为我们演示了Android通过Jni实现了在同一个进程里面C/C++跨线程回调的实例。好了演示效果已经OK了,下面我们一步步的来讲解怎么实现这种通信方式。




Android端编程

   好了言归正传,在Android端我们必须定义好native方法和相关的回调方法,这里最好借助JAVAH生成相关的头文件,当然最好是掌握native方法相关的签名那样是最好了。放上相关代码:

package com.xxx.android2native;

import java.util.ArrayList;
import java.util.List;

import android.os.MessageQueue;

public class JniNative {


    // native methods call
    private int mNativeFieId = 0;

    private native void nativeInit(MessageQueue mqueue);

    public void NativeInit(MessageQueue mqueue) {
        nativeInit(mqueue);
    }

    private List<JniNativeListerner> mJniNativeListerners = new ArrayList<JniNativeListerner>();

    public interface JniNativeListerner {
        public void notifyNativeCall(String msg);

    }

    public void setNativeListen(JniNativeListerner mJniNativeListerner) {
        mJniNativeListerners.add(mJniNativeListerner);
    }

    public void removeNativeListen(JniNativeListerner mJniNativeListerner) {
        mJniNativeListerners.remove(mJniNativeListerner);
    }

    //Native method callback
    public void notifyNativeCall(String msg) {
        for (JniNativeListerner lis : mJniNativeListerners) {
            lis.notifyNativeCall(msg);
        }
    }
    
    static {
        System.loadLibrary("Native_jni");
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46



Jni端编程

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

com_xxx_android2native_JniNative.cpp

#include "com_xxx_android2native_JniNative.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 <poll.h>
#include <utils/threads.h>
#include <utils/RefBase.h>
#include "NativeCode.h"
#include <utils/Looper.h>
#include <android/looper.h>
#include "MainStreamThread.h"



//使用Android域
using namespace android;

#define TAG "JniNative"
#define LOGE(TAG,...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)

struct fields_t {
	jfieldID context;
	jmethodID notifyNativeCall;
};

static fields_t fields;
static NativeCode* mCode = NULL;

static const char* const kClassPathName = "com/xxx/android2native/JniNative";
static bool initNativeCode(JNIEnv *env, jobject obj, jobject messageQueue);
static sp<MainStreamThread> getMainStreamThread(JNIEnv *env, jobject thiz);
static sp<MainStreamThread> setMainStreamThread(JNIEnv *env, jobject thiz, sp<MainStreamThread> &mt);


JNIEXPORT void JNICALL Java_com_xxx_android2native_JniNative_nativeInit
  (JNIEnv * env, jobject object , jobject messageQueue){
	LOGE(TAG, "init called, pid=%d uid=%d gid=%d pthread_id=%d\n", getpid(), geteuid(), getegid(), pthread_self());
	if(mCode != NULL){
		delete mCode;
		mCode = NULL;
	}
    if(!initNativeCode(env, object, messageQueue)){
		return ;
	}

	char context[256] = "MainStreamThread";
	sp<MainStreamThread> mt = new MainStreamThread(context);
	mt->setNativeCode(mCode);
	setMainStreamThread(env, object, mt);

	//启动线程
	mt->run(context, PRIORITY_DEFAULT);
	
	return;
}





static bool read_work(int fd, NativeWork* outWork) {
    int res = read(fd, outWork, sizeof(NativeWork));
    // no need to worry about EINTR, poll loop will just come back again.
    if (res == sizeof(NativeWork)) return true;

    if (res < 0) 
		//ALOGW("Failed reading work fd: %s", strerror(errno));
		LOGE(TAG,"Failed reading work fd: %s\n", strerror(errno));
    else 
		//ALOGW("Truncated reading work fd: %d", res);
		LOGE(TAG,"Truncated reading work fd: %d\n", res);
    return false;
}


/*
 * Callback for handling native events on the application's main thread.
 */
static int mainWorkCallback(int fd, int events, void* data){
	NativeCode * code = (NativeCode *) data;
	
    if ((events & POLLIN) == 0) {
        return 1;
    }

	NativeWork work;
	//读取管道相关的数据
	if (!read_work(code->mainWorkRead, &work)) {
        return 1;
    }

	switch(work.cmd){
		case CMD_NOTIFY_NATIVE_CALLBACK:{
			const char *data	= (char *)work.obj;
			jstring string 		= code->env->NewStringUTF(data);
			//ALOGW("-------CMD_NOTIFY_CUREENT_WRITER_FILE_PATH----%s----",data);
			LOGE(TAG,"------ CMD_NOTIFY_NATIVE_CALLBACK %s ------\n",data);
            code->env->CallVoidMethod(code->clazz, fields.notifyNativeCall, string);
            code->messageQueue->raiseAndClearException(code->env, "notifyNativeCall");
            delete []data;	
			break;
		}
			
		 default:
			break;
	}	
	
	
	return 1;
}


static bool initNativeCode(JNIEnv *env, jobject obj, jobject messageQueue){
	if(NULL == mCode){
		mCode = new NativeCode();
		mCode->messageQueue =  android_os_MessageQueue_getMessageQueue(env, messageQueue);
		if(mCode->messageQueue == NULL){
			LOGE(TAG,"Unable to retrieve native MessageQueue\n");
			delete mCode;
			return false;
		}
		//创建管道
		int msgpipe[2];
		if(pipe(msgpipe)){
			LOGE(TAG,"Unable to  create pipe: %s\n", strerror(errno));
			delete mCode;
			return false;
		}

		mCode->mainWorkRead	     = msgpipe[0];
		mCode->mainWorkWrite	 = msgpipe[1];
		int result = fcntl(mCode->mainWorkRead, F_SETFL, O_NONBLOCK);
        result = fcntl(mCode->mainWorkWrite, F_SETFL, O_NONBLOCK);
        mCode->messageQueue->getLooper()->addFd(
			mCode->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, mCode);
		mCode->env = env;
		mCode->clazz = env->NewGlobalRef(obj);

	}

	return true;
}



static sp<MainStreamThread> setMainStreamThread(JNIEnv *env, jobject thiz, sp<MainStreamThread> &mt){
    sp<MainStreamThread> old = (MainStreamThread*)env->GetIntField(thiz, fields.context);

    if (mt.get()) {
        mt->incStrong(thiz);
    }

    if (old != 0) {
    	//ALOGW("decStrong");
    	LOGE(TAG,"decStrong\n");
        old->decStrong(thiz);
    }

    env->SetIntField(thiz, fields.context, (int)mt.get());
    return old;
}

static sp<MainStreamThread> getMainStreamThread(JNIEnv *env, jobject thiz){

	MainStreamThread* const p = (MainStreamThread*)env->GetIntField(thiz, fields.context);
    return sp<MainStreamThread>(p);
}



// Dalvik VM type signatures
static JNINativeMethod gMethods[] = {
	{ "nativeInit", "(Landroid/os/MessageQueue;)V",(void*) Java_com_xxx_android2native_JniNative_nativeInit },
};

#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
		var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
		LOG_FATAL_IF(!var, "Unable to find method " methodName);

//注册函数
int register_native_interface(JNIEnv *env){
	jclass clazz;

	clazz = env->FindClass(kClassPathName);

	if (clazz == NULL)
	    return JNI_FALSE;

	if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0){
	        return JNI_FALSE;
	 }

	GET_METHOD_ID(fields.notifyNativeCall, clazz, "notifyNativeCall", "(Ljava/lang/String;)V");

	//将java的mNativeFieId保存在jni层
	fields.context = env->GetFieldID(clazz, "mNativeFieId", "I");
    LOG_FATAL_IF(!fields.context, "Unable to find method mNativeFieId");

    return JNI_TRUE;
}

jint JNI_OnLoad(JavaVM * vm, void * reserved){
	JNIEnv * env = NULL;
	jint result = -1;

	if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
	    LOGE(TAG, "ERROR: GetEnv failed\n");
	    goto bail;
	}
	assert(env != NULL);


	if (register_native_interface(env) < 0) {
	    LOGE(TAG,"ERROR: native registration failed\n");
	    goto bail;
	}

	result = JNI_VERSION_1_4;
	bail:
	return result;
}

void JNI_OnUnload(JavaVM* vm, void* reserved){
	LOGE(TAG, "JNI_OnUnload called\n");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230

MainStreamThread.cpp

#define TAG "MainStreamThread"

#include <utils/Log.h>
#include <stdio.h>
#include <jni.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <utils/threads.h>
#include "MainStreamThread.h"
#include <android/log.h>
#include "NativeCode.h"
#define LOGE(TAG,...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)

using namespace android;

void MainStreamThread::write_work(int fd, int32_t cmd, int32_t arg1, int32_t arg2, const void *data) {
	NativeWork work;
    work.cmd 	= cmd;
    work.arg1 	= arg1;
    work.arg2	= arg2;
    work.obj	= data;

    LOGE(TAG,"write_work: cmd=%d\n", cmd);

restart:
    int res = write(fd, &work, sizeof(work));
    if (res < 0 && errno == EINTR) {
        goto restart;
    }

    if (res == sizeof(work)) return;

    if (res < 0) 
		LOGE(TAG,"Failed writing to work fd: %s\n", strerror(errno));
    else 
		LOGE(TAG,"Truncated writing to work fd: %d\n", res);
}

void MainStreamThread::notifyNativeCall(const char *data){	
	LOGE(TAG, "init called, pid=%d uid=%d gid=%d pthread_id=%d\n", getpid(), geteuid(), getegid(), pthread_self());	
	char *mData;
    int len 	= strlen(data) + 1;
    mData 		= new char[len];
    strcpy(mData, data);

	write_work(mNativeCode->mainWorkWrite, CMD_NOTIFY_NATIVE_CALLBACK, 0, 0, (void *)mData);
}


MainStreamThread::MainStreamThread(const char * clientName):
	Thread(false){
	LOGE(TAG,"The current thread Name : %s\n", clientName);
}


MainStreamThread::~ MainStreamThread(){
	LOGE(TAG,"enter MainStreamThread::~ MainStreamThread\n");	
}

void MainStreamThread::onFirstRef(){
	LOGE(TAG,"enter MainStreamThread::onFirstRef\n");
}


status_t MainStreamThread::readToRun(){
	LOGE(TAG,"enter MainStreamThread::readToRun\n");
	status_t err = NO_ERROR;

	notifyNativeCall("MainStreamThread::readToRun");
	return err;
}


bool MainStreamThread::threadLoop(){
    status_t err = NO_ERROR;
	char context[256] = "Hello Java, Im from Native";
	notifyNativeCall(context);
	
	//仅运行一次
	return false;
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84

有了具体的实现代码,还得有相关的编译脚本,编译脚本Android.mk的实现如下,且编译环境必须在Android源码下编译。

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS :=optional
LOCAL_C_INCLUDES := $(KERNEL_HEADERS) 		\
 					$(LOCAL_PATH)/include \
 					libnativehelper/include \
 					system/core/include \
 					system/core/include/system \
					frameworks/av/include \
					frameworks/native/include \
					frameworks/native/include/media/openmax \
					frameworks/base/include \
					frameworks/base/core/jni \


					  
LOCAL_SHARED_LIBRARIES := libcutils liblog	libutils libicuuc
LOCAL_LDLIBS	:= -lm -llog -landroid_runtime -lbinder 

LOCAL_MODULE:= libNative_jni
LOCAL_SRC_FILES:= com_xxx_android2native_JniNative.cpp MainStreamThread.cpp
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22



结语

   如上就是使用Jni实现Android和C/C++通信方式二实现的全部过程,这种方式在Android中使用的非常普遍,用得太多了,譬如Input输入子系统啊,NativeActivity等等。熟练掌握这种Jni的跨线程回调的实现技能,那么以后的任何Jni跨线程回调你就再也不会让妈妈担心了,so easy。本篇是Android和C/C++通过Jni实现通信系列的最后一篇了,在Android和C/C++通信实战大荟萃中已经将Android开发中绝大部分常用的通信方式都介绍并提供了一个具体使用案例,如果读者还有其它比较好的也可以告知博主。同时关于本篇使用的Native Looper的介绍可以参见最后的相关链接。
Android消息处理机制(Handler、Looper、MessageQueue与Message)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/133212
推荐阅读
相关标签
  

闽ICP备14008679号