07-NDK开发提升性能

NDK开发提升性能

NDK开发的优点

  • 运行效率高:Android开发的原生代码不需要在Dalvik/ART虚拟机下运行
  • 代码安全性高:Java层代码很容易被反编译,而C/C++库反编译难度较大;同时,相对于针对Java代码的混淆保护来说,可以运用当前一切C/C++代码保护技术,如控制流混淆、加壳等
  • 易于移植、可复用海量代码库:当使用第三方C/C++开源库时便于移植,可以充分复用C/C++海量代码库
  • 等其他优点

NDK开发的缺点

  • 开发效率低:和Android的Java世界(Dalvik/ART)交互繁琐,无法直接使用Android系统框架提供的丰富的API
  • 开发难度大:对于当前市面的APP开发人员来说熟练掌握JNI开发存在一定困难
  • 稳定性难以保证:对开发人员要求高,如存在内存泄露出现频率高、修正bug困难等问题
  • 等其他缺点

java函数运行模式

  • 纯解释模式下执行
  • JIT模式
  • 经过dex2oat编译后在quick模式下运行

注意:Android7.0开始结合使用AOT、即时(JIT)编译和配置文件引导型编译。因此一个java函数可能运行在解释模式、JIT或者quick模式。

啥意思:在7.0之前,apk在安装时,会将dex进行优化,将dex中的smali指令转换为原生汇编指令,然后将dex和生成的原生汇编指令存储到oat文件中,具体细节可以使用oatdump命令进行查看odex文件。

ART的运作模式 https://source.android.com/devices/tech/dalvik/configure#how_art_works

分别比较相同代码逻辑的java函数和JNI函数的时间代价

java函数运行模式:

  • 在4.4以前的dalvik下运行或者在ART下利用hook禁用掉dex2oat过程强制让其运行在解释模式下
  • 使用Android6.0测试java函数在quick模式(运行dex2oat编译以后的汇编代码)

JNI函数实现:

  • 和java函数相同的逻辑,纯C/C++实现
  • 和java函数相同的逻辑,经过JNI提供的接口频繁调用java函数
public int java_add(int num){
        int result = 0;
        for(int i=0;i<=num;i++){
            result += i;
        }
        return result;
    }
extern "C" JNIEXPORT jint JNICALL
Java_com_showme_myapplication_MainActivity_StringFromJNI2(
        JNIEnv* env,
        jobject /* this */,jint num) {
        int result = 0;
        for(int i=0;i<=num;i++){
            result = +=i;
        }
        return result;
}

在纯解释模式下进行测试:在android4.4之前、在android7.0之后第一次运行。JNI是java效率的5倍

在quick模式下进行测试:使用android6进行测试。JNI是java效率的1.5倍左右

java引用数据类型(只有4种)

JNI数据类型java数据类型
jobjectjava.lang.Object
jclassjava.lang.Class
jstringjava.lang.String
jthrowablejava.lang.Throwable

jni引用数据的继承关系

  • jobject
    • jclass
    • jstring
    • jarray
      • jobjectArray
      • jbooleanArray
      • jbyteArray
      • jcharArray
      • jshortArray
      • jintArry
      • jlongArray
      • jfloatArray
      • jdoubleArray
    • jthrowable

JNIEnv

JNIEnv:指Java Native Interface Environment,是一个JNI接口指针,指向了本地方法的一个函数表,该函数表中的每一个成员指向了一个JNI函数

JNIEnv与JavaVM

JNIEnv 概念 : 是一个线程相关的结构体, 该结构体代表了 Java 在本线程的运行环境 ;

JNIEnv 与 JavaVM : 注意区分这两个概念;
– JavaVM : JavaVM 是 Java虚拟机在 JNI 层的代表, JNI 全局只有一个;
– JNIEnv : JavaVM 在线程中的代表, 每个线程都有一个, JNI 中可能有很多个 JNIEnv;

JNIEnv 作用 :
– 调用 Java 函数 : JNIEnv 代表 Java 运行环境, 可以使用 JNIEnv 调用 Java 中的代码;
– 操作 Java 对象 : Java 对象传入 JNI 层就是 Jobject 对象, 需要使用 JNIEnv 来操作这个 Java 对象;

JNIEnv 体系结构

线程相关 : JNIEnv 是线程相关的, 即 在 每个线程中 都有一个 JNIEnv 指针, 每个JNIEnv 都是线程专有的, 其它线程不能使用本线程中的 JNIEnv, 线程 A 不能调用 线程 B 的 JNIEnv;

JNIEnv 不能跨线程 :
– 当前线程有效 : JNIEnv 只在当前线程有效, JNIEnv 不能在 线程之间进行传递, 在同一个线程中, 多次调用 JNI层方法, 传入的 JNIEnv 是相同的;
– 本地方法匹配多JNIEnv : 在 Java 层定义的本地方法, 可以在不同的线程调用, 因此 可以接受不同的 JNIEnv;

JNIEnv 结构 : 由上面的代码可以得出, JNIEnv 是一个指针, 指向一个线程相关的结构, 线程相关结构指向 JNI 函数指针 数组, 这个数组中存放了大量的 JNI 函数指针, 这些指针指向了具体的 JNI 函数;

注意:JNIEnv只在当前线程中有效。本地方法不能将JNIEnv从一个线程传递到另一个线程中。相同的 Java 线程中对本地方法多次调用时,传递给该本地方法的JNIEnv是相同的。但是,一个本地方法可被不同的 Java 线程所调用,因此可以接受不同的 JNIEnv。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vJHnNtTT-1614360457624)(pic/002_001.png)]

jni中的字符串操作

  • jstring NewStringUTF(const char* bytes):函数使用给定的C字符串创建一个新的JNI字符串(jstring),不能为构造的java.lang.String分配足够的内存,NewStringUTF会抛出一个OutOfMemoryError异常,并返回一个NULL
  • const char* GetStringUTFChars(jstring string , jboolean* isCopy):函数可用于从给定的Java的jstring创建新的C字符串(char*)。如果无法分配内存,在该函数返回NULL。在内存不够时会抛出一个OutOfMemoryError异常,因此每次调用该函数时,都需要对返回值进行检查。在不使用分配的字符串时,需要调用ReleaseStringUTFChars()函数进行内存回收
  • jsize GetStringUTFLength(jstring string):用于获取jstring的长度

基本上字符串函数分为两类:char(8字节),jchar(16字节)

* jstring (*NewString)(JNIENv * ,const jchar* jsize); // 创建一个unicode编码字符串,传递的jchar是java中的char类型,占两个字节
* jsize (*GetStringLength)(JNIENv *, jstring); // 返回unicode编码的字符串的长度
* const jchar* (*GetStringChars)(JNIEN *,jstring ,jboolean *);//返回一个unicode编码的jstring的jchar指针
* void (*ReleaseStringChars)(JNIEnv *, jstring, const jchar *);// 释放使用NewString时分配的内存
* jstring (*NewStringUTF)(JNIENv *,const char *);//char为1个字节,utf8编码字符串
* jsize (*GetStringUTFLength)(JNIENv *, jstring);// 返回utf8编码的字符串的字节长度,字符个数
* const char* (*GetStringUTFChars)(JNIENv *,jstring, jboolean *);
* void (*ReleaseStringUTFChars)(JNIENv *,jstring,const char * ); // 释放使用NewStringUTFChars时分配的内存
extern "C" JNIEXPORT jstring JNICALL
Java_com_showme_performancetest01_MainActivity_testjstringapis(
        JNIEnv *env,
        jobject /* this */,jstring content) {

     const char* a=env->GetStringUTFChars(content,nullptr);
     int jstring_size=env->GetStringUTFLength(content);
     if(a!= nullptr){
         __android_log_print(ANDROID_LOG_INFO,"showme", "char content:%s,size:%d", a,jstring_size);
     }
     env->ReleaseStringUTFChars(content,a);
     jstring result=env->NewStringUTF("Hello from jni");
     return result;

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值