JNI在项开发中会被用到的场景

1、JNI注册
(1)静态注册
// Java
public native String stringFromJNI(String msg);
// JNI native方法实现
extern "C"
JNIEXPORT void JNICALL
Java_com_jni_study_MainActivity_stringFromJNI(JNIEnv* env, jobject thiz,jstring msg) {
    std::string hello = "Hello from C++";
    //do something
    return env->NewStringUTF(hello.c_str());
}
(2)动态注册
jstring stringFromJNI(JNIEnv *env, jobject thiz){
    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){

    __android_log_print(ANDROID_LOG_INFO, "native", "Jni_OnLoad");
    JNIEnv* env = NULL;
    if(vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) //从JavaVM获取JNIEnv,一般使用1.4的版本
        return -1;
    jclass clazz = env->FindClass("com/example/efan/jni_learn2/MainActivity");
    if (!clazz){
        __android_log_print(ANDROID_LOG_INFO, "native", "cannot get class: com/example/efan/jni_learn2/MainActivity");
        return -1;
    }
    if(env->RegisterNatives(clazz, gMethods, sizeof(gMethods)/sizeof(gMethods[0])))
    {
        __android_log_print(ANDROID_LOG_INFO, "native", "register native method failed!\n");
        return -1;
    }
    return JNI_VERSION_1_4;
}

2、Java和C互调?
(1)Java调用C/C++
// Java代码
public class MainActivity extends AppCompatActivity {   
    // 静态加载c/c++文件
    static {
        System.loadLibrary("native-lib");
    }
   // 用native声明c/c++方法
    public native String stringFromJNI();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }
}
// JNI代码
#include <jni.h>
#include <string> 
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_test_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
(2)C/C++调用Java
// Java代码
public class PrintUtils{
    private void printMessage(String message) {
        System.out.print("....");
    }
}
// JNI代码
JNIEXPORT void JNICALL Java_com_jni_study_MainActivity_printMessage(JNIEnv *env, jclass cls){
    // 1、从classpath路径下搜索PrintUtils这个类,并返回该类的Class对象
    jclass clazz = (*env)->FindClass(env, "com/jni/study/PrintUtils");
    // 2、获取类的默认构造方法ID
    jmethodID mid_construct = (*env)->GetMethodID(env,clazz, "<init>","()V");
    // 3、查找实例方法的ID
    jmethodID  mid_instance = (*env)->GetMethodID(env, clazz, "printMessage", "(Ljava/lang/String;I)V");
    // 4、创建该类的实例
    jobject jobj = (*env)->NewObject(env,clazz,mid_construct);
    // 5、调用对象的实例方法
    jstring str_arg = (*env)->NewStringUTF(env,"我是实例方法");
    (*env)->CallVoidMethod(env,jobj,mid_instance,str_arg);
    // 删除局部引用
    (*env)->DeleteLocalRef(env,clazz);
    (*env)->DeleteLocalRef(env,jobj);
    (*env)->DeleteLocalRef(env,str_arg);
}
  • jclass FindClass(const char* name) 根据类名来查找一个类,完整类名。
  • jclass GetObjectClass(jobject obj) 根据一个对象,获取该对象的类。
  • jclass GetSuperClass(jclass obj) 获取一个传入的对象获取他的父类的jclass。
  • jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) 获取非静态方法的ID。
  • jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig) 获取静态方法的ID。
3、JNI在开发中会被用到的场景
(1)加密算法
  • 对图片、文件、音视频进行加密处理。
(2)图片压缩
  • 使用libjpeg,在ubuntu系统下生成Android平台不同cpu架构使用的头文件和.a静态库并导入Android项目中。
  • 然后通过JNI是调用头文件和静态库中的方法去进行图片压缩,比如哈夫曼算法压缩、去掉透明度等一系列方式在保证图片清晰度在Android上相差不大的情况下降低图片文件大小。
  • 最后在编程成apk的时候会生成对应的.so库。
(3)音频压缩
  • 使用FFAC,在ubuntu系统下生成Android平台不同cpu架构使用的头文件和.a静态库并导入Android项目中。
  • 首先通过Android系统提供的AudioRecord进行音频采集。
  • 然后通过FAAC将采集好PCM音频数据编码成AAC。
  • 最终将AAC音频数据通过流媒体协议(rtmp或webrtc)将其发送到流媒体服务。
(4)视频压缩
  • 使用x264,在ubuntu系统下生成Android平台不同cpu架构使用的头文件和.a静态库并导入Android项目中。
  • 首先通过Android系统提供的Camera2进行视频数据采集。
  • 然后通过x264将采集好的YUV/RGB原始视频数据编程h264。
  • 最终h264视频数据通过流媒体协议(rtmp或webrtc)将其发送到流媒体服务。
(5)推流
  • 使用RTMPDump,在ubuntu系统下生成Android平台不同cpu架构使用的头文件和.a静态库并导入Android项目中。
  • 然后使用RTMPDump将压缩后的音频和视频上传大流媒体服务器。
4、JNI Crash定位步骤
(1)找到未strip且符号表完整的so库文件
# 这几行代码表示debug版本的so文件保留so保留符号库,这样会导致so文件很大,
# 如果要让release版本保留符号库文件,就替换成CMAKE_C_FLAGS_RELEASE和CMAKE_CXX_FLAGS_RELEASE
# 但务必在正式对外发布的时候去掉release 配置的-g选项,以免增加文件size
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
# R16之前版本的NDK默认是编译时加-g的,新版本不确定,所以需要不strip的 so文件,最好在CMake里配置一下-g

// strip之后的文件所在目录:
app/build/intermediaters/transforms/stripDebugSymbol/debug
(2)确定发生Crash的设备对应的CPU架构
  • 在JNI Crash的日志里,如果有lib/arm, 则是armeabi-v7a架构; 如果有lib/arm64, 则是arm64-v8a架构;然后根据CPU架构找相应的toolchain:
  • arm64-v8a对应的是aarch64-linux-android-4.9
  • armeabi-v7a对应的是arm-linux-androideabi-4.9
(3)使用add2line和ndk-stack等工具分析JNI Crash的log
  • addr2line:作用是根据内存地址找到对应的报错代码的文件名和行号。
// 所在目录是toolchain的bin文件夹
// 比如aarch64-linux-android-4.9对应的bin文件夹是:
/Android/Sdk/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin

// arm-linux-androideabi-4.9,对应的bin文件夹是:
/media/kyle/a393d005-ebe5-42a0-8c6a-c86fdfb185c1/Android/Sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin

// 用法:
# -f表示显示函数名, -e表示execution,后面是包含符号库的文件 以及报错的内存地址(即Crash log里pc后的字段)
arm-linux-androideabi-addr2line -f -e xxx.so 0x8eb09258
  • ndk-stack:作用是一键生成更可读的Crash日志。
// 所在目录是:
/media/kyle/a393d005-ebe5-42a0-8c6a-c86fdfb185c1/Android/Sdk/ndk-bundle/ndk-stack

// 用法1:
# -sym表示symbols
ndk-stack -sym App/build/intermediates/transforms/mergeJniLibs/release/0/lib/对应的abi目录 -dump jniCrash.log

// 用法2:
adb logcat | ndk-stack -sym  App/build/intermediates/transforms/mergeJniLibs/release/0/lib/对应的abi目录
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值