安卓NDK开发入门手册

NDK是让开发者能在Android应用中使用C/C++代码的一套开发工具。

  • 运行效率高
    将C/C++源代码直接编译成机器码

  • 代码安全性好
    APK的Java层代码很容易被反编译,而C/C++库 反编译难度大

  • 跨平台
    用C/C++写得库可以方便在其他的嵌入式平台上再次使用

核心概念

  • 本地共享库:以扩展名.so构建的库,在运行时是共享和动态链接的
  • 本地静态库:以扩展名.a构建的库,这类库实际上是在编译时静态链接的
  • JNI:将Java中对native方法的调用引导到C/C++编写的本地库中
  • ABI:定义二进制接口交互规则,允许编译的机器码运行在不同的CPU架构上
  • Android.mk:告诉ndk-build脚本模块及名称的定义,以及需要编译的源文件还有需要链接的库
  • Application.mk:可选文件。用于列出应用程序所需的模块。包含abi,工具链和标准库等信息

JNI

连接Java世界和C/C++世界的桥梁

注册

将Java层的native函数与JNI层对应的实现函数关联起来,这样在调用java层的native函数时,就能顺利转到JNI层对应的函数执行。

  • 静态注册
    根据函数名来找对应的JNI函数,需要javah参与,必须遵循方法命名规则
#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_demo_jni_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
  • 动态注册
    使用数据结构JNINativeMethod来记录Java native函数和JNI函数的对应关系。必须实现JNI_OnLoad方法,并在里面完成注册
#include <jni.h>
#include <string>

jstring stringFromJNI(JNIEnv *env, jobject) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

static const JNINativeMethod gMethods[] = {
        {"stringFromJNI", "()Ljava/lang/String;", (jstring *) stringFromJNI}
};

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    jclass clazz = env->FindClass("com/demo/jni/MainActivity");
    if (!clazz) {
        return -1;
    }
    if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) {
        return -1;
    }
    return JNI_VERSION_1_6;
}

JNIEnv

JNIEnv是线程相关的结构体,该结构体代表了Java在本线程的执行环境。JNIEnv可以用来调用Java中的函数以及操作Java中的变量/对象,但是只能在本线程中生效,不能传递到另一个线程。那么,如果在其他线程中也需要使用呢?

JavaVM

JavaVM是虚拟机在JNI层的代理,全进程唯一。JNI_OnLoad方法传入,可以通过调用其方法AttachCurrentThread关联线程获取JNIEnv对象。可以在线程的任何地方通过方法GetEnv获取JNIEnv,前提是先调用AttachCurrentThread进行关联。最后可以调用DetachCurrentThread解除关联,释放资源。

在这里插入图片描述

特性应用

参数

可以传入任何类型的参数到native方法中

extern "C" JNIEXPORT jstring JNICALL
Java_com_demo_jni_MainActivity_concat(JNIEnv *env, jobject thiz, jstring words) {
    char *buf = NULL;
    jclass strclass = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("GB2312");
    jmethodID mid = env->GetMethodID(strclass, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barray = (jbyteArray) env->CallObjectMethod(words, mid, strencode);
    jsize len = env->GetArrayLength(barray);
    jbyte *bp = env->GetByteArrayElements(barray, JNI_FALSE);
    if (len > 0) {
        buf = (char *) malloc(len + 1); //"\0"
        memcpy(buf, bp, len);
        buf[len] = 0;
    }
    char *hello = "Hello";
    strcat(hello, buf);
    env->ReleaseByteArrayElements(barray, bp, 0);
    return env->NewStringUTF(hello);
}
方法

JNIEnv提供了很多方法调用

extern "C" JNIEXPORT jobject JNICALL
Java_com_demo_jni_MainActivity_createUser(JNIEnv *env, jobject thiz, jstring name, jint age) {
    jclass userclass = env->FindClass("com/demo/jni/User");
    jobject newUser = env->AllocObject(userclass);

    jfieldID nameField = env->GetFieldID(userclass, "name", "Ljava/lang/String;");
    jfieldID ageField = env->GetFieldID(userclass, "age", "I");

    env->SetObjectField(newUser, nameField, name);
    env->SetIntField(newUser, ageField, age);

    return newUser;
}

基本类型映射

Java TypeNative TypeDescriptionType Signature
booleanjbooleanunsigned 8 bitsZ
bytejbytesigned 8 bitsB
charjcharunsigned 16 bitsC
shortjshortsigned 16 bitsS
intjintsigned 32 bitsI
longjlongsigned 64 bitsJ
floatjfloat32 bitsF
doublejdouble64 bitsD
voidvoidN/AV

引用类型映射

Java TypeNative TypeType Signature
java.lang.ObjectjobjectLfully-qualified-class ;
java.lang.ClassjclassLjava/lang/Class;
java.lang.StringjstringLjava/lang/String;
java.lang.Object[]jobjectArray[Lfully-qualified-class ;
boolean[]jbooleanArray[Z
byte[]jbyteArray[B
char[]jcharArray[C
short[]jshortArray[S
int[]jintArray[I
long[]jlongArray[J
float[]jfloatArray[F
double[]jdoubleArray[D
java.lang.ThrowablejthrowableLjava/lang/Throwable;

引用

局部引用
  • 通过NewLocalRef和各种JNI方法创建(如FindClass、NewObject等)
  • 阻止GC回收
  • 本地方法返回会自动回收所引用的对象或者通过手动调用DeleteLocalRef回收
  • 线程相关,只能在创建它的线程里使用,通过全局变量缓存并使用在其他线程是不合法的
  • 避免使用静态变量缓存局部引用
全局引用
  • 通过NewGlobalRef创建
  • 阻止GC回收
  • 必须手动调用DeleteGlobalRef释放,否则一直有效
  • 全局引用可以跨方法、跨线程使用
  • 可以使用静态变量来缓存
弱全局引用
  • 通过NewWeakGlobalRef创建
  • 使用DeleteGlobalWeakRef来释放
  • 失效之前可以跨方法多次使用,也可以在多线程中使用
  • 无法保证所引用的对象不被GC回收
  • 使用IsSameObject检查
  • 可缓存

图解原理

在这里插入图片描述

感谢大家的支持,如有错误请指正,如需转载请标明原文出处!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值