上篇博客已经讲到了 android jni 调用java类方法和成员变量,源于ffmpeg的调用android的硬解码的灵感,则想着,在非jni函数中调用java层代码。便花了点时间研究了下。
大家都知道jni函数中NIEnv *env, jobject instance,一个 是包含的jvm环境一个是 传下来类对象。于是在jni的函数最终保存了env,然后在非jni函数调用,但会失败,报错env无效。
于是就学ffmpeg调用android的java层mediaCodec类一样,
1、JNI_OnLoad中保存虚拟机变量,
2、非jni函数调用AttachCurrentThread获得当前jvm环境
3、创建java类对象,并调用方法。
下面demo为,利用oepncv和assets在jni层调用上层nativeUtils中的getAssetsImageMat方法加载assets中的图片到C++层。
头文件就是注册了getAssetsImage这里就不给出了。下面为cpp
JavaVM *g_jvm = NULL;
JNIEnv *env;
extern "C"
JNIEXPORT
jint JNI_OnLoad(JavaVM *vm, void *res) {
// av_jni_set_java_vm(vm, 0);
// 返回jni版本
g_jvm = vm;
return JNI_VERSION_1_4;
}
// 非jni函数
Mat* getAssetsImage(const char* image)
{
//获取当前线程的jni环境
if (g_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
return NULL;
}
LOGE("getAssetsImage start 0");
jclass jclass = env->FindClass("com/example/opengldraw2multicolorbuffer/NativeUtils");
jmethodID construct_id= env->GetMethodID(jclass,"<init>","()V");
jobject newNativeUtils = env->NewObject(jclass,construct_id);
jmethodID getAssetsImageMat_id= env->GetMethodID(jclass,"getAssetsImageMat","(Ljava/lang/String;)J");
jstring path = env->NewStringUTF("lammy.jpg");
long imageAddr = env->CallLongMethod(newNativeUtils, getAssetsImageMat_id, path);
cv::Mat *srcMat = ( cv::Mat*)imageAddr;// 这里mat像素格式为rgba
return srcMat;
}
下面为java层的代码:
public class NativeUtils {
static {
System.loadLibrary("lammy");
System.loadLibrary("opencv_java4");
}
public NativeUtils()
{
}
private static AssetManager assetManager;
public static void setAssetManager(AssetManager assetManager2){
assetManager = assetManager2;
}
public long getAssetsImageMat(String imageAssetPath)
{
Bitmap bitmap= null;
try {
bitmap = BitmapFactory.decodeStream(assetManager.open(imageAssetPath));
} catch (IOException e) {
e.printStackTrace();
}
Mat mat = new Mat();
Utils.bitmapToMat(bitmap , mat);
return mat.getNativeObjAddr();
}
}
完整demo包含opencv 4.1在android配置,以及图片加载。欢迎大家下载。https://gitee.com/zp2009112217/openglDraw2MultiColorbuffer2