Android 开发菜鸟,请大家多多指教!
1、 在C中直接调用与开启线程调用java方法是有所不同,这是由JNIEnv *env的使用限制引起的。
JNIEnv *env是一个结构体指针它指向JNI函数表,通过它能调用JNI所有函数来使用虚拟机的各种功能,例如;查找某个类(验证是否被VM加载、创建各种基本类数组、调用java方法等,它的方法几乎都是很有的),它是一个指向线程的局部数据,不能被保存来供其它线程使用(方法调用的相关线程中也有效),它与线程是一一对应对应关系(不同的线程有不同的*env,JVM(Dalvik)是一个多线程执行绪,*env中的各种方法归根结底就是通过VM操纵Java线程中的数据,因此,避免造成资源冲突,VM(Dalvik)会给每个方法都传递一个属于当前线程的*env指针)。
通过VM指针每个线程都可以获取一个属于自己所在线程的JNIEnv *env,在任何线程中可以通过一个全局VM指针访问AttachCurrentThread(JNIEnv** p_env, void* thr_args)或GetEnv(void** env, jint version)方法获取自己的JNIEnv *env,而前者是用于应对该线程是在C中产生而非在VM中的情况,代码注释中有介绍。
如何在C中获取VM指针?方法有很多,最常见的方式是在任意一个C组件中(但整个.so中有且只有一该方法)实现一个叫做JNI_OnLoad的方法,代码如下:
JavaVM* gs_jvm; //当加载.so库时该方法会被VM自动调用,每个*.so库只能有一个这个入口,即使没有定义的也会有一个默认的方法, jint JNI_OnLoad(JavaVM* vm, void* reserved){ gs_jvm=vm;//获取一个全局的VM指针 //TODO 如果想进一步提高JVM访问c中方法的效率可以在这里将C中定义的方法注册到JVM中 (使用JNIHelp.cpp中方法) return JNI_VERSION_1_4; }
但,在本文中同时还需要保存一个Java对象供C中全局操纵,为了方便使用了另一种方法,具体实现在下面代码中有。
功能需求:在开发IM的一个APP中,接收消息与发送都是在C中实现,要求APP随时能接收到C层发送消息(json格式的String)。
具体实现:使用一个java单例接收来自C线程发送的消息,然后再解析与分发消息;
java代码:
1、定义消息监听接口:
- package com.xx.xx.message.listener;
- public interface OnMessageArriveListener {
- void onMessageArrive(String message);
- }
2、实现消息监听接口:
- package com.xx.xx.message.receive;
- import android.util.Log;
- import com.xx.xx.message.listener.OnMessageArriveListener;
- /**
- * Created by admin on 2018/6/6.
- */
- public class ReceiveMessageImpl implements ReceiveMessage {
- private static final String TAG