服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

分享两种方式:如何开启JNI的“大门”?

日期: 来源:鸿洋收集编辑:字节卷动

1
要介绍本篇博客的原因


前段时间学习OpenGL ES相关技术,下载了一个Github项目学习,项目地址在:https://github.com/githubhaohao/NDK_OpenGLES_3_0
项目的关键代码都是C++实现的,所以需要使用JNI技术。
我打开定义native方法的java类,如下所示:全部都是红色警告。


原因是,C++代码层没有对应的遵循特定JNI格式的JNI函数。

其实这个项目没有使用静态注册方法,而是使用了动态注册方法。下面我们分别来讲一下两种方式的区别。


2
静态注册


2.1 实现原理

根据函数名来建立java方法和JNI函数间的一一对应关系。

2.2 实现过程

  1. 编写.java代码;
  2. 编译.java代码,生成.class文件;
  3. 用过javah指令,利用生成的.class文件生成JNI的.h文件;
  4. 生成后的JNI头文件中包含了Java函数在JNI层的声明。
可以参考下面几篇博客:

• 【我的Android进阶之旅】Android Studio中JNI开发如何通过Extranal Tools 快速一键生成.h头文件

https://blog.csdn.net/ouyang_peng/article/details/8715602


• 【我的Android进阶之旅】Android调用JNI出错 java.lang.UnsatisfiedLinkError: No implementation found for的解决方法

https://blog.csdn.net/ouyang_peng/article/details/52997698


• 【我的Android进阶之旅】如果通过NDK编程,使用JNI来调用已经封装完毕的无法修改的so库(第三方)

https://blog.csdn.net/ouyang_peng/article/details/112382141


• 【我的Android进阶之旅】如何通过JNI来封装已有的C源码算法,然后让Java层调用C语言写的算法

https://blog.csdn.net/ouyang_peng/article/details/109299224


2.3 弊端

  1. 书写很不方便,因为JNI层函数的名字必须遵循特定的格式,且名字特别长;
  2. 会导致程序员的工作量很大,因为必须为所有声明了native函数的java类编写JNI头文件;
  3. 程序运行效率低,因为初次调用native函数时需要根据根据函数名在JNI层中搜索对应的本地函数,然后建立对应关系,这个过程比较耗时。
关于第1和第2点,随着Android Studio升级,现在可以直接写完java native方法 就可以使用快捷键【Alt  + Enter】就会弹出提示提示【Create JNI function for xxx native method】,如下所示:
public class MyNativeRender {
    ...  其他代码

    static {
        System.loadLibrary("native-render");
    }

    public native void native_Init();

    public native void native_UnInit();

    ...  其他代码
}


比如我们生成一下,自动生成如下代码:
extern "C"
JNIEXPORT void JNICALL
Java_com_byteflow_app_MyNativeRender_native_1Init(JNIEnv *env, jobject thiz) {
    // TODO: implement native_Init()
}


再回到java层,就可以看到坐标有个C++的logo,点击即可跳转到对应的cpp代码的jni函数。


2.4 示例

目前我项目中还是使用的这种静态注册的方法。大家可以在下面的github查看。

https://github.com/ouyangpeng/OpenGLESDemo
  1. java 层代码
java层代码编写native函数在com/oyp/openglesdemo/render/MyNativeRenderer.kt中,如下所示:
package com.oyp.openglesdemo.render

import android.app.Activity
import android.content.res.AssetManager
import android.opengl.GLSurfaceView
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10

class MyNativeRenderer(activity: Activity) : GLSurfaceView.Renderer, RenderAction {
    private var mActivity: Activity = activity
    var mSampleType = 0

    init {
        System.loadLibrary("ouyangpeng-opengles-lib")
    }

    // 通用的
    private external fun nativeSurfaceCreate(assetManager: AssetManager)
    private external fun nativeSurfaceChange(width: Int, height: Int)
    private external fun nativeDrawFrame()
    private external fun nativeSetRenderType(sampleCategoryType: Int, renderSampleType: Int)
    private external fun nativeOnDestroy()

    // 特定的方法
    private external fun nativeSwitchBlendingMode()

    // 特定的方法
    private external fun nativeSetDelta(x: Float, y: Float)
    private external fun nativeSetMinFilter(filter: Int)
    private external fun nativeSetMagFilter(filter: Int)

    private external fun nativeSetImageData(
        format: Int,
        width: Int,
        height: Int,
        imageData: ByteArray?
    )

    private external fun nativeSetImageDataWithIndex(
        index: Int,
        format: Int,
        width: Int,
        height: Int,
        imageData: ByteArray?
    )

    private external fun nativeUpdateTransformMatrix(
        rotateX: Float,
        rotateY: Float,
        scaleX: Float,
        scaleY: Float
    )


    override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
        val assetManager: AssetManager = mActivity.assets
        nativeSurfaceCreate(assetManager)
    }

    override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {
        nativeSurfaceChange(width, height)
    }

    override fun onDrawFrame(gl: GL10) {
        nativeDrawFrame()
    }

    fun setRenderType(sampleCategoryType: Int, renderSampleType: Int) {
        if (sampleCategoryType == IMyNativeRendererType.SAMPLE_TYPE) {
            mSampleType = renderSampleType
        }
        nativeSetRenderType(sampleCategoryType, renderSampleType)
    }

    fun onDestroy() {
        nativeOnDestroy()
    }

    override fun switchBlendingMode() {
        nativeSwitchBlendingMode()
    }

    override fun setMinFilter(filter: Int) {
        nativeSetMinFilter(filter)
    }

    override fun setMagFilter(filter: Int) {
        nativeSetMagFilter(filter)
    }

    override fun setDelta(deltaX: Float, deltaY: Float) {
        nativeSetDelta(deltaX, deltaY)
    }

    override fun setImageData(
        format: Int,
        width: Int,
        height: Int,
        imageData: ByteArray
    ) {
        nativeSetImageData(format, width, height, imageData)
    }

    override fun setImageDataWithIndex(
        index: Int,
        format: Int,
        width: Int,
        height: Int,
        imageData: ByteArray
    ) {
        nativeSetImageDataWithIndex(index, format, width, height, imageData)
    }

    override fun updateTransformMatrix(
        rotateX: Float,
        rotateY: Float,
        scaleX: Float,
        scaleY: Float
    ) {
        nativeUpdateTransformMatrix(rotateX, rotateY, scaleX, scaleY)
    }
}


  1. 对应的JNI方法
对应的JNI方法实现在app/src/main/cpp/jni/JniImpl.cpp文件中,如下所示:
//
// Created by OuyangPeng on 2021/11/26.
//
#include <jni.h>
#include <MyGLRenderContext.h>
#include <EGLRender.h>

extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_MyNativeRenderer_nativeSurfaceCreate(
        JNIEnv *env, jobject thiz, jobject asset_manager) {
    MyGLRenderContext::GetInstance()->OnSurfaceCreated(env, asset_manager);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_MyNativeRenderer_nativeSurfaceChange(
        JNIEnv *env, jobject thiz, jint width, jint height) {
    MyGLRenderContext::GetInstance()->OnSurfaceChanged(width, height);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_MyNativeRenderer_nativeDrawFrame(JNIEnv *env, jobject thiz) {
    MyGLRenderContext::GetInstance()->OnDrawFrame();
}

extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_MyNativeRenderer_nativeSetRenderType(
        JNIEnv *env, jobject thiz, jint sampleCategoryType, jint renderSampleType) {
    MyGLRenderContext::GetInstance()->SetRenderType(sampleCategoryType, renderSampleType);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_MyNativeRenderer_nativeOnDestroy(JNIEnv *env, jobject thiz) {
    MyGLRenderContext::DestroyInstance();
}

extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_MyNativeRenderer_nativeSwitchBlendingMode(JNIEnv *env, jobject thiz) {
    MyGLRenderContext::GetInstance()->SwitchBlendingMode();
}
extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_MyNativeRenderer_nativeSetDelta(
        JNIEnv *env, jobject thiz, jfloat x, jfloat y) {
    MyGLRenderContext::GetInstance()->SetDelta(x, y);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_MyNativeRenderer_nativeSetMinFilter(
        JNIEnv *env, jobject thiz, jint filter) {
    MyGLRenderContext::GetInstance()->SetMinFilter(filter);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_MyNativeRenderer_nativeSetMagFilter(
        JNIEnv *env, jobject thiz, jint filter) {
    MyGLRenderContext::GetInstance()->SetMagFilter(filter);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_MyNativeRenderer_nativeSetImageData(
        JNIEnv *env, jobject thiz,
        jint format, jint width, jint height, jbyteArray imageData) {

    int len = env->GetArrayLength(imageData);
    u_int8_t *buf = new u_int8_t[len];
    env->GetByteArrayRegion(imageData, 0, len, reinterpret_cast<jbyte *>(buf));

    MyGLRenderContext::GetInstance()->SetImageData(format, width, height, buf);
    delete[]buf;
    env->DeleteLocalRef(imageData);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_MyNativeRenderer_nativeSetImageDataWithIndex(
        JNIEnv *env, jobject thiz,
        jint index, jint format, jint width, jint height, jbyteArray imageData) {

    int len = env->GetArrayLength(imageData);
    u_int8_t *buf = new u_int8_t[len];
    env->GetByteArrayRegion(imageData, 0, len, reinterpret_cast<jbyte *>(buf));

    MyGLRenderContext::GetInstance()->SetImageDataWithIndex(index, format, width, height, buf);
    delete[]buf;
    env->DeleteLocalRef(imageData);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_MyNativeRenderer_nativeUpdateTransformMatrix(
        JNIEnv *env, jobject thiz, jfloat rotateX,jfloat rotateY,jfloat scaleX,jfloat scaleY) {
    MyGLRenderContext::GetInstance()->UpdateTransformMatrix(rotateX, rotateY, scaleX, scaleY);
}

   EGL 渲染相关 

extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_egl_NativeEglRender_nativeEglRenderInit(JNIEnv *env, jobject thiz, jobject asset_manager) {
    EGLRender::GetInstance()->Init(env,asset_manager);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_egl_NativeEglRender_nativeEglRenderSetImageData(
        JNIEnv *env, jobject thiz, jbyteArray data, jint width, jint height) {

    int len = env->GetArrayLength (data);
    uint8_t* buf = new uint8_t[len];
    env->GetByteArrayRegion(data, 0, len, reinterpret_cast<jbyte*>(buf));

    EGLRender::GetInstance()->SetImageData(buf, width, height);

    delete[] buf;
    env->DeleteLocalRef(data);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_egl_NativeEglRender_nativeEglRenderSetFragmentShaderType(
        JNIEnv *env, jobject thiz, jint param_type, jint fShaderType) {
    EGLRender::GetInstance()->SetFragmentShaderType(param_type, fShaderType);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_egl_NativeEglRender_nativeEglRenderDraw(JNIEnv *env, jobject thiz) {
    EGLRender::GetInstance()->Draw();
}
extern "C"
JNIEXPORT void JNICALL
Java_com_oyp_openglesdemo_render_egl_NativeEglRender_nativeEglRenderUnInit(JNIEnv *env, jobject thiz) {
    EGLRender::GetInstance()->UnInit();
}

   EGL 渲染相关 
实际上在Android Studio中,显示的效果还行,如下所示:

3
动态注册


由上面的介绍可以发现,通过Android Studio等IDE的功能越来越完善,关于JNI函数名很长的问题其实并不是瓶颈。
上面有提到一个弊端是程序运行效率低 :因为初次调用native函数时需要根据根据函数名在JNI层中搜索对应的本地函数,然后建立对应关系,这个过程比较耗时。

应用层的Java类别通过VM而调用到native函数。一般是通过VM去寻找*.so里的native函数。


如果需要连续呼叫很多次,每次都需要寻找一遍,会多花许多时间。


此时,C组件开发者可以将本地函数向VM进行注册,以便能加快后续调用native函数的效率。
可以这么想象一下,假设VM内部一个native函数链表,初始时是空的,在未动态注册之前此native函数链表是空的,每次java调用native函数之前会首先在此链表中查找需要查找需要调用的native函数,如果找到就直接使用,如果未找到,得再通过载入的.so文件中的函数列表中去查找,且每次java调用native函数都是进行这样的流程,因此,效率就自然会下降。
为了克服这样现象,我们可以通过在.so文件载入初始化时,即JNI_OnLoad函数中,先行将native函数注册到VM的native函数链表中去,这样一来,后续每次java调用native函数时都会在VM中的native函数链表中找到对应的函数,从而加快速度。
注:在Android 源码开发环境下,大多采用动态注册native方法。
下面我们来介绍一下动态注册的方式。

3.1 实现原理

直接告诉native函数其在JNI中对应函数的指针。

3.2 实现过程

  1. 利用结构体JNINativeMethod保存Java Native函数和JNI函数的对应关系;
  2. 在一个JNINativeMethod数组中保存所有native函数和JNI函数的对应关系;
  3. 在Java中通过System.loadLibrary加载完JNI动态库之后,调用JNI_OnLoad函数,开始动态注册;
  4. JNI_OnLoad中会调用AndroidRuntime::registerNativeMethods函数进行函数注册;
  5. AndroidRuntime::registerNativeMethods中最终调用jni RegisterNativeMethods完成注册。

3.3 优点

克服了静态注册的弊端。
  1. RegisterNatives方法能帮助你把C/C++中的方法隐射到Java中的native方法,而无需遵循特定的方法命名格式。
  2. 更有效率去找到函数。  
  3. 在执行期间进行抽换。

3.4 示例

我们就以 https://github.com/githubhaohao/NDK_OpenGLES_3_0的代码作为示例:
1. java层代码
代码定义在 app/src/main/java/com/byteflow/app/MyNativeRender.java 文件中。
/**
 *
 * Created by 公众号:字节流动 on 2021/3/12.
 * https://github.com/githubhaohao/NDK_OpenGLES_3_0
 * 最新文章首发于公众号:字节流动,有疑问或者技术交流可以添加微信 Byte-Flow ,领取视频教程, 拉你进技术交流群
 *
 * */

package com.byteflow.app;

public class MyNativeRender {
    其他代码


    static {
        System.loadLibrary("native-render");
    }

    public native void native_Init();

    public native void native_UnInit();

    public native void native_SetParamsInt(int paramType, int value0, int value1);

    public native void native_SetParamsFloat(int paramType, float value0, float value1);

    public native void native_UpdateTransformMatrix(float rotateX, float rotateY, float scaleX, float scaleY);

    public native void native_SetImageData(int format, int width, int height, byte[] bytes);

    public native void native_SetImageDataWithIndex(int index, int format, int width, int height, byte[] bytes);

    public native void native_SetAudioData(short[] audioData);

    public native void native_OnSurfaceCreated();

    public native void native_OnSurfaceChanged(int width, int height);

    public native void native_OnDrawFrame();
}


2. Native层代码


代码定义在 app/src/main/cpp/JniImpl.cpp 文件中。

  • 定义JNI NativeMethod的映射关系。
g_RenderMethods[]和g_BgRenderMethods[]是2个<名称,函数指针>对照表,在程序执行时,可多次呼叫RegisterNativeMethods()函数来更换本地函数之指针,而达到弹性抽换本地函数之目的。
static JNINativeMethod g_RenderMethods[] = {
        {"native_Init",                      "()V",       (void *)(native_Init)},
        {"native_UnInit",                    "()V",       (void *)(native_UnInit)},
        {"native_SetImageData",              "(III[B)V",  (void *)(native_SetImageData)},
        {"native_SetImageDataWithIndex",     "(IIII[B)V", (void *)(native_SetImageDataWithIndex)},
        {"native_SetParamsInt",              "(III)V",    (void *)(native_SetParamsInt)},
        {"native_SetParamsFloat",            "(IFF)V",    (void *)(native_SetParamsFloat)},
        {"native_SetAudioData",              "([S)V",     (void *)(native_SetAudioData)},
        {"native_UpdateTransformMatrix",     "(FFFF)V",   (void *)(native_UpdateTransformMatrix)},
        {"native_OnSurfaceCreated",          "()V",       (void *)(native_OnSurfaceCreated)},
        {"native_OnSurfaceChanged",          "(II)V",     (void *)(native_OnSurfaceChanged)},
        {"native_OnDrawFrame",               "()V",       (void *)(native_OnDrawFrame)},
};

static JNINativeMethod g_BgRenderMethods[] = {
        {"native_EglRenderInit",          "()V",       (void *)(native_EglRenderInit)},
        {"native_EglRenderSetImageData",  "([BII)V",   (void *)(native_EglRenderSetImageData)},
        {"native_EglRenderSetIntParams",  "(II)V",     (void *)(native_EglRenderSetIntParams)},
        {"native_EglRenderDraw",          "()V",       (void *)(native_EglRenderDraw)},
        {"native_EglRenderUnInit",        "()V",       (void *)(natuve_BgRenderUnInit)},
};


  • 定义要关联的对应Java类。
#define NATIVE_RENDER_CLASS_NAME "com/byteflow/app/MyNativeRender"
#define NATIVE_BG_RENDER_CLASS_NAME "com/byteflow/app/egl/NativeEglRender"


  • 然后定义RegisterNativeMethods函数和UnregisterNativeMethods函数。
static int RegisterNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int methodNum)
{
    LOGCATE("RegisterNativeMethods");
    jclass clazz = env->FindClass(className);
    if (clazz == NULL)
    {
        LOGCATE("RegisterNativeMethods fail. clazz == NULL");
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, methods, methodNum) < 0)
    {
        LOGCATE("RegisterNativeMethods fail");
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

static void UnregisterNativeMethods(JNIEnv *env, const char *className)
{
    LOGCATE("UnregisterNativeMethods");
    jclass clazz = env->FindClass(className);
    if (clazz == NULL)
    {
        LOGCATE("UnregisterNativeMethods fail. clazz == NULL");
        return;
    }
    if (env != NULL)
    {
        env->UnregisterNatives(clazz);
    }
}



  • JNI_OnLoad 函数和 JNI_OnUnload函数。
JNI组件被成功加载和卸载时,会进行函数回调。
当VM执行到System.loadLibrary(xxx)函数时,首先会去执行JNI组件中的JNI_OnLoad()函数。而当VM释放该组件时会呼叫JNI_OnUnload()函数。
JNI_OnLoad()有两个重要的作用:
  1. 指定JNI版本:告诉VM该组件使用那一个JNI版本(若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI 1.1版),如果要使用新版本的JNI,例如JNI 1.6版,则必须由JNI_OnLoad()函数返回常量JNI_VERSION_1_6(该常量定义在jni.h中) 来告知VM。
  2. 初始化设定,当VM执行到System.loadLibrary()函数时,会立即先呼叫JNI_OnLoad()方法,因此在该方法中进行各种资源的初始化操作最为恰当。
JNI_OnUnload()的作用与JNI_OnLoad()对应,当VM释放JNI组件时会呼叫它,因此在该方法中进行善后清理,资源释放的动作最为合适。
extern "C" jint JNI_OnLoad(JavaVM *jvm, void *p)
{
    LOGCATE("===== JNI_OnLoad =====");
    jint jniRet = JNI_ERR;
    JNIEnv *env = NULL;
    if (jvm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK)
    {
        return jniRet;
    }

    jint regRet = RegisterNativeMethods(env, NATIVE_RENDER_CLASS_NAME, g_RenderMethods,
                                        sizeof(g_RenderMethods) /
                                        sizeof(g_RenderMethods[0]));
    if (regRet != JNI_TRUE)
    {
        return JNI_ERR;
    }

    regRet = RegisterNativeMethods(env, NATIVE_BG_RENDER_CLASS_NAME, g_BgRenderMethods,
                                        sizeof(g_BgRenderMethods) /
                                        sizeof(g_BgRenderMethods[0]));
    if (regRet != JNI_TRUE)
    {
        return JNI_ERR;
    }

    return JNI_VERSION_1_6;
}

extern "C" void JNI_OnUnload(JavaVM *jvm, void *p)
{
    JNIEnv *env = NULL;
    if (jvm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK)
    {
        return;
    }

    UnregisterNativeMethods(env, NATIVE_RENDER_CLASS_NAME);

    UnregisterNativeMethods(env, NATIVE_BG_RENDER_CLASS_NAME);
}



  • 完整代码

    完整代码如下所示:

//
// Created by ByteFlow on 2019/7/9.
//
#include "util/LogUtil.h"
#include <MyGLRenderContext.h>
#include <EGLRender.h>
#include "jni.h"

#define NATIVE_RENDER_CLASS_NAME "com/byteflow/app/MyNativeRender"
#define NATIVE_BG_RENDER_CLASS_NAME "com/byteflow/app/egl/NativeEglRender"

#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_byteflow_app_MyNativeRender
 * Method:    native_Init
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_Init(JNIEnv *env, jobject instance)
{
    MyGLRenderContext::GetInstance();

}

/*
 * Class:     com_byteflow_app_MyNativeRender
 * Method:    native_UnInit
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_UnInit(JNIEnv *env, jobject instance)
{
    MyGLRenderContext::DestroyInstance();
}

/*
 * Class:     com_byteflow_app_MyNativeRender
 * Method:    native_SetImageData
 * Signature: (III[B)V
 */
JNIEXPORT void JNICALL native_SetImageData
(JNIEnv *env, jobject instance, jint format, jint width, jint height, jbyteArray imageData)
{
    int len = env->GetArrayLength (imageData);
    uint8_t* buf = new uint8_t[len];
    env->GetByteArrayRegion(imageData, 0, len, reinterpret_cast<jbyte*>(buf));
    MyGLRenderContext::GetInstance()->SetImageData(format, width, height, buf);
    delete[] buf;
    env->DeleteLocalRef(imageData);
}

/*
 * Class:     com_byteflow_app_MyNativeRender
 * Method:    native_SetImageDataWithIndex
 * Signature: (IIII[B)V
 */
JNIEXPORT void JNICALL native_SetImageDataWithIndex
        (JNIEnv *env, jobject instance, jint index, jint format, jint width, jint height, jbyteArray imageData)
{
    int len = env->GetArrayLength (imageData);
    uint8_t* buf = new uint8_t[len];
    env->GetByteArrayRegion(imageData, 0, len, reinterpret_cast<jbyte*>(buf));
    MyGLRenderContext::GetInstance()->SetImageDataWithIndex(index, format, width, height, buf);
    delete[] buf;
    env->DeleteLocalRef(imageData);
}

/*
 * Class:     com_byteflow_app_MyNativeRender
 * Method:    native_SetParamsInt
 * Signature: (III)V
 */
JNIEXPORT void JNICALL native_SetParamsInt
        (JNIEnv *env, jobject instance, jint paramType, jint value0, jint value1)
{
    MyGLRenderContext::GetInstance()->SetParamsInt(paramType, value0, value1);
}

/*
 * Class:     com_byteflow_app_MyNativeRender
 * Method:    native_SetParamsFloat
 * Signature: (IFF)V
 */
JNIEXPORT void JNICALL native_SetParamsFloat
        (JNIEnv *env, jobject instance, jint paramType, jfloat value0, jfloat value1)
{
    MyGLRenderContext::GetInstance()->SetParamsFloat(paramType, value0, value1);
}


/*
 * Class:     com_byteflow_app_MyNativeRender
 * Method:    native_SetAudioData
 * Signature: ([B)V
 */
JNIEXPORT void JNICALL native_SetAudioData
        (JNIEnv *env, jobject instance, jshortArray data)
{
    int len = env->GetArrayLength(data);
    short *pShortBuf = new short[len];
    env->GetShortArrayRegion(data, 0, len, reinterpret_cast<jshort*>(pShortBuf));
    MyGLRenderContext::GetInstance()->SetParamsShortArr(pShortBuf, len);
    delete[] pShortBuf;
    env->DeleteLocalRef(data);
}

/*
 * Class:     com_byteflow_app_MyNativeRender
 * Method:    native_UpdateTransformMatrix
 * Signature: (FFFF)V
 */
JNIEXPORT void JNICALL native_UpdateTransformMatrix(JNIEnv *env, jobject instance, jfloat rotateX, jfloat rotateY, jfloat scaleX, jfloat scaleY)
{
    MyGLRenderContext::GetInstance()->UpdateTransformMatrix(rotateX, rotateY, scaleX, scaleY);
}

/*
 * Class:     com_byteflow_app_MyNativeRender
 * Method:    native_OnSurfaceCreated
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_OnSurfaceCreated(JNIEnv *env, jobject instance)
{
    MyGLRenderContext::GetInstance()->OnSurfaceCreated();
}

/*
 * Class:     com_byteflow_app_MyNativeRender
 * Method:    native_OnSurfaceChanged
 * Signature: (II)V
 */
JNIEXPORT void JNICALL native_OnSurfaceChanged
(JNIEnv *env, jobject instance, jint width, jint height)
{
    MyGLRenderContext::GetInstance()->OnSurfaceChanged(width, height);

}

/*
 * Class:     com_byteflow_app_MyNativeRender
 * Method:    native_OnDrawFrame
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_OnDrawFrame(JNIEnv *env, jobject instance)
{
    MyGLRenderContext::GetInstance()->OnDrawFrame();

}


/*
 * Class:     com_byteflow_app_egl_NativeBgRender
 * Method:    native_EglRenderInit
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_EglRenderInit(JNIEnv *env, jobject instance)
{
    EGLRender::GetInstance()->Init();

}

/*
 * Class:     com_byteflow_app_egl_NativeBgRender
 * Method:    native_EglRenderSetImageData
 * Signature: ([BII)V
 */
JNIEXPORT void JNICALL native_EglRenderSetImageData(JNIEnv *env, jobject instance, jbyteArray data, jint width, jint height)
{
    int len = env->GetArrayLength (data);
    uint8_t* buf = new uint8_t[len];
    env->GetByteArrayRegion(data, 0, len, reinterpret_cast<jbyte*>(buf));
    EGLRender::GetInstance()->SetImageData(buf, width, height);
    delete[] buf;
    env->DeleteLocalRef(data);


}

/*
 * Class:     com_byteflow_app_egl_NativeBgRender
 * Method:    native_EglRenderSetIntParams
 * Signature: (II)V
 */
JNIEXPORT void JNICALL native_EglRenderSetIntParams(JNIEnv *env, jobject instance, jint type, jint param)
{
    EGLRender::GetInstance()->SetIntParams(type, param);

}

/*
 * Class:     com_byteflow_app_egl_NativeBgRender
 * Method:    native_EglRenderDraw
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_EglRenderDraw(JNIEnv *env, jobject instance)
{
    EGLRender::GetInstance()->Draw();
}

/*
 * Class:     com_byteflow_app_egl_NativeBgRender
 * Method:    natuve_BgRenderUnInit
 * Signature: ()V
 */
JNIEXPORT void JNICALL natuve_BgRenderUnInit(JNIEnv *env, jobject instance)
{
    EGLRender::GetInstance()->UnInit();
}

#ifdef __cplusplus
}
#endif

static JNINativeMethod g_RenderMethods[] = {
        {"native_Init",                      "()V",       (void *)(native_Init)},
        {"native_UnInit",                    "()V",       (void *)(native_UnInit)},
        {"native_SetImageData",              "(III[B)V",  (void *)(native_SetImageData)},
        {"native_SetImageDataWithIndex",     "(IIII[B)V", (void *)(native_SetImageDataWithIndex)},
        {"native_SetParamsInt",              "(III)V",    (void *)(native_SetParamsInt)},
        {"native_SetParamsFloat",            "(IFF)V",    (void *)(native_SetParamsFloat)},
        {"native_SetAudioData",              "([S)V",     (void *)(native_SetAudioData)},
        {"native_UpdateTransformMatrix",     "(FFFF)V",   (void *)(native_UpdateTransformMatrix)},
        {"native_OnSurfaceCreated",          "()V",       (void *)(native_OnSurfaceCreated)},
        {"native_OnSurfaceChanged",          "(II)V",     (void *)(native_OnSurfaceChanged)},
        {"native_OnDrawFrame",               "()V",       (void *)(native_OnDrawFrame)},
};

static JNINativeMethod g_BgRenderMethods[] = {
        {"native_EglRenderInit",          "()V",       (void *)(native_EglRenderInit)},
        {"native_EglRenderSetImageData",  "([BII)V",   (void *)(native_EglRenderSetImageData)},
        {"native_EglRenderSetIntParams",  "(II)V",     (void *)(native_EglRenderSetIntParams)},
        {"native_EglRenderDraw",          "()V",       (void *)(native_EglRenderDraw)},
        {"native_EglRenderUnInit",        "()V",       (void *)(natuve_BgRenderUnInit)},
};

static int RegisterNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int methodNum)
{
    LOGCATE("RegisterNativeMethods");
    jclass clazz = env->FindClass(className);
    if (clazz == NULL)
    {
        LOGCATE("RegisterNativeMethods fail. clazz == NULL");
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, methods, methodNum) < 0)
    {
        LOGCATE("RegisterNativeMethods fail");
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

static void UnregisterNativeMethods(JNIEnv *env, const char *className)
{
    LOGCATE("UnregisterNativeMethods");
    jclass clazz = env->FindClass(className);
    if (clazz == NULL)
    {
        LOGCATE("UnregisterNativeMethods fail. clazz == NULL");
        return;
    }
    if (env != NULL)
    {
        env->UnregisterNatives(clazz);
    }
}

// call this func when loading lib
extern "C" jint JNI_OnLoad(JavaVM *jvm, void *p)
{
    LOGCATE("===== JNI_OnLoad =====");
    jint jniRet = JNI_ERR;
    JNIEnv *env = NULL;
    if (jvm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK)
    {
        return jniRet;
    }

    jint regRet = RegisterNativeMethods(env, NATIVE_RENDER_CLASS_NAME, g_RenderMethods,
                                        sizeof(g_RenderMethods) /
                                        sizeof(g_RenderMethods[0]));
    if (regRet != JNI_TRUE)
    {
        return JNI_ERR;
    }

    regRet = RegisterNativeMethods(env, NATIVE_BG_RENDER_CLASS_NAME, g_BgRenderMethods,
                                        sizeof(g_BgRenderMethods) /
                                        sizeof(g_BgRenderMethods[0]));
    if (regRet != JNI_TRUE)
    {
        return JNI_ERR;
    }

    return JNI_VERSION_1_6;
}

extern "C" void JNI_OnUnload(JavaVM *jvm, void *p)
{
    JNIEnv *env = NULL;
    if (jvm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK)
    {
        return;
    }

    UnregisterNativeMethods(env, NATIVE_RENDER_CLASS_NAME);

    UnregisterNativeMethods(env, NATIVE_BG_RENDER_CLASS_NAME);
}




最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!


推荐阅读:

Android Framework 一学就"废"
Gradle常用的几十个命令!
这些技能非常重要!


扫一扫 关注我的公众号

如果你想要跟大家分享你的文章,欢迎投稿~


┏(^0^)┛明天见!

相关阅读

  • 祖传代码,业务代码参数透传满天飞?

  • 本文作者作者:唐子玄链接:https://juejin.cn/post/7165427212911378445本文由作者授权发布。1引子项目中参数多级透传满天飞的情况很常见,增加了开发的复杂度、出错的可能、及
  • 量化策略实战:构建交易方法论

  • 大家对量化交易或量化投资的方式都不陌生了,我们已接触和学习的包含但不限于量化投资门类,诸如程序化交易、算法交易、高频交易、套利交易、量化选股、量化择时等种类繁多的量
  • 千万别在有WiFi的房间里摆这种姿势,只因……

  • 量子位(ID:QbitAI)明敏 丰色 发自 凹非寺现在,只用WiFi就能“看”到你在房间里干啥了……(你…干…嘛……啊啊啊啊)多人追踪也是so easy:过程中完全不需要拍下图像、不需要摄像头。
  • 一文读懂函数编程及其工作原理

  • 点击上方蓝字关注我们(本文阅读时间:8分钟)微软MVP实验室研究员马洪喜-微软 MVP19年研发经验云计算咨询顾问专家容器云及基础架构云技术专家DevOps 及微服务咨询专家什么是函数
  • 实践:使用 Python 开发一款迷你游戏

  • 点击上方蓝字 ● 关注Linux公社 本文与大家一起学习如何在 Python 中使用 Tkinter 构建颜色游戏Battlefield2、World of Tanks、The Sims4 和 Freedom Force 是使用 Pyt
  • Bash 初学者系列 #1:在 Bash 中使用函数

  • 点击上方蓝字 ● 关注Linux公社 当您的 bash 脚本变得越来越大时,事情会变得非常混乱!您可能会发现自己在 bash 脚本的不同部分一次又一次地重写相同的代码片段。幸运的是
  • 免费开源Windows系统错误代码查询工具!

  • “设为星标”第一时间获取各类实用干货!前言大家在使用Windows系统过程中,或多或少都遇到过一些错误,有些错误系统会给出解释,有些错误则只给出对应的错误代码,而这些错误代码代
  • 2023 年的 Web Worker 项目实践

  • 前言—Web Workers 是 2009 年就已经提案的老技术,但是在很多项目中的应用相对较少,常见一些文章讨论如何写 demo ,但很少有工程化和项目级别的实践,本文会结合 Web Workers 在

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • 分享两种方式:如何开启JNI的“大门”?

  • ‍‍1要介绍本篇博客的原因前段时间学习OpenGL ES相关技术,下载了一个Github项目学习,项目地址在:https://github.com/githubhaohao/NDK_OpenGLES_3_0项目的关键代码都是C++实
  • 祖传代码,业务代码参数透传满天飞?

  • 本文作者作者:唐子玄链接:https://juejin.cn/post/7165427212911378445本文由作者授权发布。1引子项目中参数多级透传满天飞的情况很常见,增加了开发的复杂度、出错的可能、及
  • 封神之作:极致包体优化,解决 17 个业务痛点

  • ‍本文作者:小木箱,原文发布于:小木箱成长营。1引言Hello,我是小木箱,欢迎来到小木箱成长营系列教程,今天将分享包体积优化 · 实战论 · 怎么做包体优化? 做好能晋升吗? 能涨多少
  • 勒索软件系列报告之四丨三重勒索软件BlackCat

  • 世界经济论坛发布的《2022年全球网络安全展望》报告显示,勒索软件攻击在全球网络领导者网络威胁关心问题中排名第一,成为全球广泛关注的网络安全难题。天际友盟双子座实验室推