android ndk 入门学习2

本文详细介绍了Java与C/C++之间的互操作性技术JNI,包括如何处理字符串转换、访问Java对象域和方法的细节,以及性能优化策略。通过实例解析,帮助开发者更好地理解并应用JNI技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java Native Interface
更多有关JNI的信息
1)字符串
在Java和C/C++使用String,常会导致性能问题。Java的Strng使用16位的Unicode字符(UTF-16),而许多C/C++函数使用char *作字符串用(C/C++中的字符串大部分情况下用ASCII或UTF-8)。也就是说,Java字符串必须转换成C/C++字符串才可以使用。

//Java (Myclass.java文件中)
public class MyClass {
      public static native void doSomethingWithString(String s);
}
//JNI 粘合层(C文件中)
void JNICALL Java_com_apress_proandroid_MyClass_doSomethingWithString
 (JNIEnv *env,jclass clazz, jstring s)
{
    const char* str = (*env)->GetStringUTFChars(env, s, NULL);
    if (str != NULL) {
        // 用str字符串在这里做些事情

        // todo

        // 记得释放字符串,不要犯内存泄漏这个常见的错误。
        (*env)->ReleaseStringUTFChars(env, s, str);
    }
}

JNI提供了多种方法来处理字符串,它们都用相同的方式完成大量工作,在使用过程中注意以下事项:
I) Java字符串必须被转换成C/C++字符串;
II) C/C++字符串必须释放。

内存分配从来不是免费午餐,应该在代码中尽可能使用GetStringRegion和GetStringUTFRegion。这样做,可以得到以下好处:
a)避免可能的内存分配;
b)复制String要用的一部分到预先分配的缓冲区(可能在栈上);
c)不需要释放字符串,避免了忘了释放字符串的风险。

2、访问域或方法
可以从JNI粘合层内访问Java对象或类的域和方法,但它不像访问域或调用C++对象或类的函数那样简单,要通过id访问Java或类的域和方法。访问域或调用方法时需要:
a)得到这一域或方法的id;
b)使用JNI函数设置/获取域或调用方法。

public class MyClass {

    static {
        System.loadLibrary("mylib");
        getIds();//加载类时,只需加载一次id
    }
    public static int someInteger = 0;

    public static native void sayHelloToJNI();

    public static void helloFromJNI() {
        Log.i("MyClass", "Greetings! someInteger=" + someInteger);
    }

    private static native void getIds();

}

对应的C文件

#include "com_wll_util_ndk_MyClass.h" 

static jfieldID someIntegerId;
static jmethodID helloFromJNIId;

/*
 * Class:     com_wll_util_ndk_MyClass
 * Method:    sayHelloToJNI
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_wll_util_ndk_MyClass_sayHelloToJNI
  (JNIEnv *env, jclass clazz) {
    // 增加someInteger 
    jint value = (*env)->GetStaticIntField(env, clazz, someIntegerId);
    (*env)->SetStaticIntField(env, clazz, someIntegerId, value + 1);

    // 调用helloFromJNI
    (*env)->CallStaticVoidMethod(env, clazz, helloFromJNIId);
  }

/*
 * Class:     com_wll_util_ndk_MyClass
 * Method:    getIds
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_wll_util_ndk_MyClass_getIds
  (JNIEnv *env, jclass clazz) { 
    // 获取someInteger域和helloFromJNI方法的id 
    someIntegerId = (*env)->GetStaticFieldID(env, clazz, "someInteger", "I");
    helloFromJNIId = (*env)->GetStaticMethodID(env, clazz, "helloFromJNI", "()V");
  }

出于性能方面的考量,不想每次访问域或调用方法时都去获取一次域或方法的id。虚拟机加载类时域和方法的id就被设置,只要类被加载,该id就有效。高效的方式是在类被加载时获取id,即在静态初始化块中获取。

JNI定义了大量用于访问域和调用方法的函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值