android中如果需要调用c++代码需要写jni将java操作转接到c++代码中.但是大部分的文章都只是通过讲解通过 extern "C" 代码来访问具体功能.
在这里我要讲一种方便的方法将java对象和c++对象互相绑定起来.
第一步:
JNIObject.java:
public class JNIObject {
protected long mObj;
}
MediaController.java
public class MediaController extends JNIObject {
public MediaController()
{
construct();
}
@Override
protected void finalize() throws Throwable {
destruct();
super.finalize();
}
@Keep
private void logMessage(String msg){
Log.d("AAA", "msg:"+msg);
}
static {
System.loadLibrary("media_controller-lib");
}
private native void construct();
private native void destruct();
public native int ntopen(String file);
public native void ntclose();
public native int ntplay();
public native void ntpause();
public native void ntseek(int sec);
public native String nttest(int msg);
}
static JavaVM* g_VM = NULL;
class JNIMediaControl
:public MediaControl
{
public:
JNIMediaControl()
:javaVM(NULL),env_(NULL),thiz(NULL)
{
}
void attachParent(JavaVM * vm,JNIEnv *env,jobject object)
{
javaVM =vm;
thiz = env->NewGlobalRef(object);
}
void detachParent(JNIEnv *env)
{
env->DeleteGlobalRef(thiz);
thiz = NULL;
}
private:
void AttachThread()
{
if(javaVM == NULL) return;
if(thiz == NULL) return ;
int ret = javaVM->AttachCurrentThread(&env_,NULL);
if(ret != 0)
{
}
}
void DetachThread()
{
if(javaVM == NULL) return;
javaVM->DetachCurrentThread();
env_ = NULL;
}
virtual void Run()
{
AttachThread();
MediaControl::Run();
DetachThread();
}
void SendPacket(int64_t timestamp,AVFrame *frame)
{
JNIEnv *env = env_;
if(env == NULL) return;
char buffer[64];
sprintf(buffer,"%lld",timestamp);
jstring msg_ = env->NewStringUTF(buffer);
jclass clazz = env->GetObjectClass(thiz);
jmethodID jid = env->GetMethodID(clazz,"logMessage","(Ljava/lang/String;)V");
env->CallVoidMethod(thiz,jid,msg_);
env->DeleteLocalRef(msg_);
}
private:
JavaVM* javaVM;
JNIEnv *env_;
jobject thiz;
};
#define CONSTRUCT(T) { T *t = new T(); \
jclass clazz = (jclass)(*env).GetObjectClass(thiz); \
jfieldID fid = (jfieldID)(*env).GetFieldID(clazz, "mObj", "J"); \
jlong jstr = (jlong) (*env).GetLongField(thiz, fid); \
(*env).SetLongField(thiz, fid, (jlong)t);}
#define OBJECT(T,name) jclass clazz = (jclass)env->GetObjectClass(thiz); \
jfieldID fid = env->GetFieldID(clazz, "mObj","J"); \
T *name = (T *)env->GetLongField(thiz, fid);
#define DESTRUCT(T) {jclass clazz = (jclass)env->GetObjectClass(thiz); \
jfieldID fid = env->GetFieldID(clazz, "mObj","J"); \
T *object = (T *)env->GetLongField(thiz, fid); \
if(object != NULL) delete object; \
(*env).SetLongField(thiz, fid, (jlong)0);}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jfyang_mediacontrollerdemo_MediaController_construct(JNIEnv* env, jobject thiz){
CONSTRUCT(JNIMediaControl);
OBJECT(JNIMediaControl,control);
if(control == NULL) return ;
control->attachParent(g_VM,env,thiz);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jfyang_mediacontrollerdemo_MediaController_destruct(JNIEnv *env,
jobject thiz) {
{
OBJECT(JNIMediaControl, control);
if (control == NULL) return;
control->detachParent(env);
}
DESTRUCT(JNIMediaControl);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jfyang_mediacontrollerdemo_MediaController_ntclose(JNIEnv *env,
jobject thiz) {
OBJECT(JNIMediaControl,control);
if(control == NULL) return ;
control->Close();
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
assert(env != NULL);
g_VM = vm;
return JNI_VERSION_1_4;
}
在android中需要被底层使用的对象均继承自JNIObject.
JNI代码中 通过
CONSTRUCT构造一个自定义对象与JNIObject关联起来. OBJECT获取与JNIObject关联起来的对象 DESTRUCT释放与JNIObject关联起来的对象 具体的功能还是通过jni的c方法调用并转至c++对象中.
在jni底层线程中有时需要回调数据至android上层中,这种情况我们可以通过回调上层方法完成.
1.先将上层父对象建立全局引用
thiz = env->NewGlobalRef(object);
注意:所有的在底层线程中操作的上层对象均需要建立GlobalRef才能使用,否则会崩溃.
2.通过JNI_Onload获取本模块加载时传入的虚拟机并全局保存
3.通过虚拟机 在线程中创建一个线程相关JNIEnv.
4.通过线程相关JNIEnv 调用 GlobalRef对象的方法.