JNI 编程上手指南之 JNI 调用性能优化

为什么要做性能优化

  • Java 程序中,调用一个 Native 方法相比调用一个 Java 方法要耗时很多,我们应该减少 JNI 方法的调用,同时一次 JNI 调用尽量完成更多的事情。对于过于耗时的 JNI 调用,应该放到后台线程调用。
  • Native 程序要访问 Java 对象的字段或调用它们的方法时,本机代码必须调用 FindClass()、GetFieldID()、GetStaticFieldID、GetMethodID() 和 GetStaticMethodID() 等方法,返回的 ID 不会在 JVM 进程的生存期内发生变化。但是,获取字段或方法的调用有时会需要在 JVM 中完成大量工作,因为字段和方法可能是从超类中继承而来的,这会让 JVM 向上遍历类层次结构来找到它们。为了提高性能,我们可以把这些 ID 缓存起来,用内存换性能。

使用时缓存

Java 层:

public class TestJavaClass {

    //......
    private void myMethod() {
        Log.i("JNI", "this is java myMethod");
    }
    //......
}

public native void cacheTest();

Natice 层

extern "C"
JNIEXPORT void JNICALL
Java_com_yuandaima_myjnidemo_MainActivity_cacheTest(JNIEnv *env, jobject thiz) {

    jclass clazz = env->FindClass("com/yuandaima/myjnidemo/TestJavaClass");
    if (clazz == NULL) {
        return;
    }

    static jmethodID java_construct_method_id = NULL;
    static jmethodID java_method_id = NULL;

    //实现缓存的目的,下次调用不用再获取 methodid 了
    if (java_construct_method_id == NULL) {
        //构造函数 id
        java_construct_method_id = env->GetMethodID(clazz, "<init>", "()V");
        if (java_construct_method_id == NULL) {
            return;
        }
    }

    //调用构造函数,创建一个对象
    jobject object_test = env->NewObject(clazz, java_construct_method_id);
    if (object_test == NULL) {
        return;
    }
    //相同的手法,缓存 methodid
    if (java_method_id == NULL) {
        java_method_id = env->GetMethodID(clazz, "myMethod", "()V");
        if (java_method_id == NULL) {
            return;
        }
    }

    //调用 myMethod 方法
    env->CallVoidMethod(object_test, java_method_id);

    env->DeleteLocalRef(clazz);
    env->DeleteLocalRef(object_test);
}

手法还是比较简单的,主要是通过一个全局变量保存 methodid,这样只有第一次调用 native 函数时,才会调用 GetMethodID 去获取,后面的调用都使用缓存起来的值了。这样就避免了不必要的调用,提升了性能。

静态初始化缓存

Java 层:

static {
    System.loadLibrary("myjnidemo");
    initIDs();
}

public static native void initIDs();

C++ 层:

//定义用于缓存的全局变量
static jmethodID java_construct_method_id2 = NULL;
static jmethodID java_method_id2 = NULL;

extern "C"
JNIEXPORT void JNICALL
Java_com_yuandaima_myjnidemo_MainActivity_initIDs(JNIEnv *env, jclass clazz) {

    jclass clazz2 = env->FindClass("com/yuandaima/myjnidemo/TestJavaClass");

    if (clazz == NULL) {
        return;
    }

    //实现缓存的目的,下次调用不用再获取 methodid 了
    if (java_construct_method_id2 == NULL) {
        //构造函数 id
        java_construct_method_id2 = env->GetMethodID(clazz2, "<init>", "()V");
        if (java_construct_method_id2 == NULL) {
            return;
        }
    }

    if (java_method_id2 == NULL) {
        java_method_id2 = env->GetMethodID(clazz2, "myMethod", "()V");
        if (java_method_id2 == NULL) {
            return;
        }
    }
}

手法和使用时缓存是一样的,只是缓存的时机变了。如果是动态注册的 JNI 还可以在 Onload 函数中来执行缓存操作。

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/20410,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?