赞
踩
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 ------
(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
上面的相关打印日志为我们演示了Android通过Jni实现了在同一个进程里面C/C++跨线程回调的实例。好了演示效果已经OK了,下面我们一步步的来讲解怎么实现这种通信方式。
好了言归正传,在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"); } }
好了,前面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"); }
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; }
有了具体的实现代码,还得有相关的编译脚本,编译脚本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)
如上就是使用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)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。