学习在Android Studio中使用JNI以及编译

Ted 80a1505ae0 file 6 lat temu
.idea 80a1505ae0 file 6 lat temu
app 80a1505ae0 file 6 lat temu
gradle 80a1505ae0 file 6 lat temu
.gitignore a0431057f8 init file 7 lat temu
README.md 5f69308af6 add hello world 7 lat temu
build.gradle 80a1505ae0 file 6 lat temu
gradle.properties a0431057f8 init file 7 lat temu
gradlew a0431057f8 init file 7 lat temu
gradlew.bat a0431057f8 init file 7 lat temu
push.sh c5aad092fe TeddeMacBook-Pro.local push @ 2017-08-09_17-17-41 7 lat temu
settings.gradle a0431057f8 init file 7 lat temu
version.gradle 80a1505ae0 file 6 lat temu

README.md

JNI in Java

参考向您的项目添加 C 和 C++ 代码

源码解析

extern "C"
JNIEXPORT jstring JNICALL
Java_com_tedxiong_android_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
  1. extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按c语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名。这个功能十分有用处,因为在C++出现以前,很多代码都是C语言写的,而且很底层的库也是C语言写的,为了更好的支持原来的C代码和已经写好的C语言库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。

  2. JNIEXPORTJNICALL两个都是JNI关键字,表示此函数要被JNI调用,具体定义什么意思有待细究。

  3. jstring表示该函数的返回值类型,即为String

  4. Java_com_tedxiong_android_MainActivity_stringFromJNI函数命名规则,Java_类全路径_方法名称

  5. JNIEnv *env可以看做是Jni接口本身的一个对象,在上一篇中提到的jni.h头文件中存在着大量被封装好的函数,这些函数也是Jni编程中经常被使用到的,要想调用这些函数就需要使用JNIEnv这个对象。例如:env->GetObjectClass()。(详情请查看jni.h)

  6. jobject obj这个jobject需要分两种情况看待,如果在Java中声明*stringFromJNI*时是一个静态方法,那么jobject指的是方法所在的类class。如果是非静态方法,想要调用该方法,就必须要实例化对象,这里这个jobject指的就是实例化的对象。

在JNI中使用Android Log替代printf

  1. find_library() 命令添加到您的 CMake 构建脚本中以定位 NDK 库,并将其路径存储为一个变量。您可以使用此变量在构建脚本的其他部分引用 NDK 库。以下示例可以定位 Android 特定的日志支持库并将其路径存储在 log-lib 中:

    find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib
    
              # Specifies the name of the NDK library that
              # CMake needs to locate.
              log )
    
  2. 为了确保您的原生库可以在 log 库中调用函数,您需要使用 CMake 构建脚本中的 target_link_libraries() 命令关联库:

    find_library(...)
    
    # Links your native library against one or more other native libraries.
    target_link_libraries( # Specifies the target library.
                       # 这里需要修改为so库名称
                       native-lib
    
                       # Links the log library to the target library.
                       ${log-lib} )
    
  3. 在cpp文件或者h文件中,加入以下部分:

    #include <android/log.h>
    #define TAG "Ted_Jni_TAG" // 这个是自定义的LOG的标识
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
    #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
    #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
    #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)
    
  4. 如何使用:

    JNIEXPORT void JNICALL Java_com_tedxiong_android_MainActivity_helloJNI
    (JNIEnv *env, jobject, jstring what) {
        char *str = (char *) (*env).GetStringUTFChars(what, false);
        LOGD("hello,%s", str);//直接调用定义好的LOGD方法
        (*env).ReleaseStringUTFChars(what, str);
    }