我的视频课程(基础):《(NDK)FFmpeg打造Android万能音频播放器》

我的视频课程(进阶):《(NDK)FFmpeg打造Android视频播放器》

我的视频课程(编码直播推流):《Android视频编码和直播推流》

我的视频课程(C++ OpenGL):《Android C++ OpenGL教程》

 

 

目录:

        Android-Jni线程(一)— 创建线程

        Android-Jni线程(二)— 线程锁之生产者消费者

        Android-Jni线程(三)— JNI全局回调java方法

 

 

 

        在Android中用C/C++开发一部分功能时,不可避免的会把状态告诉给java层,由java层来做相应的处理或展示。而C/C++要把信息传递给java层就会调用java层原先写好的回调方法,这样才能把信息传递给java层。当C/C++文件很多时,里面线程也很多时,这时就需要我们使用统一的回调函数来处理了,不然会很混乱的。今天就给出一种解决方法能实现不同文件里面、不同线程里面都可以回调java方法。还是先看看效果:

Android-Jni线程(三)— JNI全局回调java方法_第1张图片

 

一、实现思路:

        1、jni里面调用java方法的大致步骤是:根据jobject获取jclass(静态方法就不用这一步了)--> 获取jmethodid --> 调用方法。

        2、jni里面调用java方法的环境分为2种。

        第一种:在env所在线程调用java方法,这种情况不需要做特殊处理,直接按照步骤执行即可。

        第二种:在pthread子线程调用java方法,这种情况下就需要做处理了。在jni中,子线程中是不能直接调用JNIEnv对象的,也不能直接调用env线程中的jobject对象,因为:jni中,JNIEnv是和线程相关的,每一个native方法所在线程就有一个当前线程相关的JNIEnv对象,而pthread线程中是不能调用native方法所在线程的JENnv对象的,解决办法是:利用JavaVM虚拟机,JavaVM是和进程相关的,一个进程里面的JavaVM都是同一个,所以在pthread线程中就可以通过JavaVM来获取(AttachCurrentThread)当前线程的JNIEnv指针,然后就可以使用JNIEnv指针操作数据了;还有在pthread线程中调用jobject对象时,首先需要把native线程里面的jobject创建全局引用(env->NewGlobalRef(jobj)),其返还的jobject对象就可以在程序中使用了。

        3、在JNI_OnLoad中获取我们需要的JavaVM指针。

 

二、代码实现:

        2.1、创建文件WlListener.cpp来统一管理回调方法,并在构造方法中传入所需参数。

头文件WlListener.h

 

//// Created by ywl5320//#pragma once#ifndef JNITHREAD_WLLISTENER_H#define JNITHREAD_WLLISTENER_H#include class WlListener {public:    JavaVM* jvm;//java虚拟机    _JNIEnv *jenv;//native线程env对象    jobject jobj;//全局对象    jmethodID jmid;//java 方法id,可以根据实际情况创建多个。public:    WlListener(JavaVM* vm, _JNIEnv *jenv, jobject obj);    ~WlListener();    void onError(int type, int code, const char *msg);};#endif //JNITHREAD_WLLISTENER_H

 

 

cpp文件WlListener.cpp

 

//// Created by ywl5320//#include "WlListener.h"#include "WlAndroidLog.h"WlListener::WlListener(JavaVM *vm, _JNIEnv *env, jobject obj) {    jvm = vm;    jenv = env;    jobj = obj;    jclass clz = env->GetObjectClass(jobj);    if(!clz)    {        LOGE("get jclass wrong");        return;    }    jmid = env->GetMethodID(clz, "onError", "(ILjava/lang/String;)V");    if(!jmid)    {        LOGE("get jmethodID wrong");        return;    }}/** * * @param type  0:env线程 1:子线程 * @param code * @param msg */void WlListener::onError(int type, int code, const char *msg) {    if(type == 0)    {        jstring jmsg = jenv->NewStringUTF(msg);        jenv->CallVoidMethod(jobj, jmid, code, jmsg);        jenv->DeleteLocalRef(jmsg);    }    else if(type == 1)    {        JNIEnv *env;        jvm->AttachCurrentThread(&env, 0);        jstring jmsg = env->NewStringUTF(msg);        env->CallVoidMethod(jobj, jmid, code, jmsg);        env->DeleteLocalRef(jmsg);        jvm->DetachCurrentThread();    }}

 

 

 

 

其中:jvm参数是为了获取子线程中的JNIEnv;jenv参数是native线程中的,在native线程中使用;jobj是全局对象;jmid是要调用的java层的方法id,还可以有其他方法。

 

        2.2、通过JNI_OnLoad获取JavaVM:

 

JavaVM* jvm;JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,void* reserved){    JNIEnv *env;    jvm = vm;    if(vm->GetEnv((void**)&env,JNI_VERSION_1_6)!=JNI_OK){        return -1;    }    return JNI_VERSION_1_6;}

 

 

 

JNI_OnLoad方法是在加载完.so库时就会自动调用的,所以在这里获取JavaVM是最佳的时机。

 

        2.3、主线程和子线程调用回调方法

pthread_t callbackThread;void *callBackT(void *data){    //获取WlListener指针    WlListener *wlListener = (WlListener *) data;    //在子线程中调用回调方法    wlListener->onError(1, 200, "Child thread running success!");    pthread_exit(&callbackThread);}extern "C"JNIEXPORT void JNICALLJava_com_ywl5320_jnithread_JniThread_callbackThread(JNIEnv *env, jobject jobj) {    WlListener *wlListener = new WlListener(jvm, env, env->NewGlobalRef(jobj));    //在主线程中调用java方法    wlListener->onError(0, 100, "JNIENV thread running success!");    //开启子线程,并把WlListener指针传递到子线程中    pthread_create(&callbackThread, NULL, callBackT, wlListener);}

 

        2.4、java方法

 

//3、回调线程    public native void callbackThread();    private OnErrorListener onErrorListener;    public void setOnErrorListener(OnErrorListener onErrorListener) {        this.onErrorListener = onErrorListener;    }    //Jni调用此方法,把结果返回到java层    public void onError(int code, String msg)    {        if(onErrorListener != null)        {            onErrorListener.onError(code, msg);        }    }    public interface OnErrorListener    {        void onError(int code, String msg);    }

 

 

这样就完成了在C/C++中不同线程回调java方法了。

 

源码下载:Github:Android-JniThread 欢迎star

 

 

 

更多相关文章

  1. Android的线程使用来更新UI------Thread Handler Looper TimerTa
  2. Android和蓝牙GPS结合的方法
  3. [ ]在Android系统上使用busybox——最简单的方法
  4. Android中的文件的读取方法
  5. Android设备开机后自动启动APP解决方法:(学习篇)
  6. Android 文字自动滚动(跑马灯)效果的两种实现方法[特别好使]
  7. Android Fragment 生命周期及回调方法
  8. 部分 CM11 系统 Android 平板执行植物大战僵尸 2 黑屏的解决的方

随机推荐

  1. 在Android上实现WLAN的一点理解
  2. Android调用WebService之服务端实现(一)
  3. android 自定义进度条颜色
  4. Android(安卓)AIDL机制范例解析
  5. Android群英传第五章Scroll分析读书笔记
  6. android反编译
  7. [Android] 基于 Linux 命令行构建 Androi
  8. 认识Android中的双向绑定
  9. Android(安卓)NDK学习之 一. Android(安
  10. 浅谈android的selector背景选择器