JNI实例演示

作者: 码上述Andy | 来源:发表于2019-07-06 17:11 被阅读10次

1.前言

为了更好的理解并应用前面所分享的内容,下面将实例演示JNI开发步骤。

2.Native工程准备

2.1SDK Manager 安装相应组件组件

AS菜单栏中选择 Tools >SDK Manager,点击 SDK Tools 选项卡,勾选 LLDB,CMake 和 NDK。如下图:


image.png

2.2新建工程

打开Android Studio--->File菜单--->New--->New Project然后打开如下对话框



选择Native C++然后Next指定Name,PackageName等Next



c++Standard 标准的C++,默认就好,最后Finish即可创建Native工程。

3.开发步骤

3.1JNI函数注册

3.1.1静态注册方式生成对应的.h头文件
java层编写native方法
  

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
    public native String stringFromJNI2();
    public native String fromJaveToNative(String s);
    public native String signture(String sig);
    public native int add(int a, int b);
    public native void throwException(String msg);
    public String getPackageN() {
        return getPackageName();
    }
    public void fromNativeJNI() {
        Logger.getLogger("MainActivity").severe("fromNativeJNI str>>>>>>>");
    }
    public void fromNativeJNI2(String str) {
        Logger.getLogger("MainActivity").severe("fromNativeJNI str=" + str);
    }
    public static void fromNativeJNI3(String str) {
        Logger.getLogger("MainActivity").severe("fromNativeJNI str=" + str);
    }
    public static void fromNativeJNI4() {
        Logger.getLogger("MainActivity").severe("fromNativeJNI str4444444");
    }
    public int getValue() {
        Logger.getLogger("MainActivity").severe("fromNativeJNI getValue");
        return i;
    }
    public void modifyValue(int j) {
        Logger.getLogger("MainActivity").severe("fromNativeJNI str55555>>j=" + j);
    }

通过javah生成jni对应的函数格式

/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/javah -classpath . -jni -d /Users/zhouwen/work/NativeApp/app/src/main/jni jni.chowen.com.nativeapp.MainActivity

执行完命令之后,在jni目录下会自动生成jni_chowen_com_nativeapp_MainActivity.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class jni_chowen_com_nativeapp_MainActivity */

#ifndef _Included_jni_chowen_com_nativeapp_MainActivity
#define _Included_jni_chowen_com_nativeapp_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     jni_chowen_com_nativeapp_MainActivity
 * Method:    passBitmap
 * Signature: (Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_jni_chowen_com_nativeapp_MainActivity_passBitmap
  (JNIEnv *, jobject, jobject);

/*
 * Class:     jni_chowen_com_nativeapp_MainActivity
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_jni_chowen_com_nativeapp_MainActivity_add
  (JNIEnv *, jobject, jint, jint);

/*
 * Class:     jni_chowen_com_nativeapp_MainActivity
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_jni_chowen_com_nativeapp_MainActivity_stringFromJNI
  (JNIEnv *, jobject);

/*
 * Class:     jni_chowen_com_nativeapp_MainActivity
 * Method:    stringFromJNI2
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_jni_chowen_com_nativeapp_MainActivity_stringFromJNI2
  (JNIEnv *, jobject);

/*
 * Class:     jni_chowen_com_nativeapp_MainActivity
 * Method:    fromJaveToNative
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_jni_chowen_com_nativeapp_MainActivity_fromJaveToNative
  (JNIEnv *, jobject, jstring);

/*
 * Class:     jni_chowen_com_nativeapp_MainActivity
 * Method:    signture
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_jni_chowen_com_nativeapp_MainActivity_signture
  (JNIEnv *, jobject, jstring);

/*
 * Class:     jni_chowen_com_nativeapp_MainActivity
 * Method:    throwException
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_jni_chowen_com_nativeapp_MainActivity_throwException
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif
JNI层实现

新建native-lib.cpp c++文件,并实现javah生成的jni_chowen_com_nativeapp_MainActivity.h以上.h文件函数

#include <jni.h>
#include <string>
#include "jni_chowen_com_nativeapp_MainActivity.h"
#include "jni_chowen_com_nativeapp_CInterface.h"
#include "MD5.h"

#include <android/log.h>

#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__))

using namespace std;

jstring getPackname(JNIEnv *env, jobject clazz, jobject obj);

jstring getSignature(JNIEnv *env, jobject clazz, jobject jobject1);

int RegisterNatives(JNIEnv *env);

//静态注册
//extern "C" JNIEXPORT jstring JNICALL
//Java_jni_chowen_com_nativeapp_MainActivity_stringFromJNI(
//        JNIEnv *env,
//        jobject /* this */) {
//    string hello = "Hello from C++";
//    jint jint4 = env->GetVersion();
//    LOGE("JNI version: %d", jint4);
//
//    return env->NewStringUTF(hello.c_str());
//}

//动态注册
static jstring stringFromJNI(
        JNIEnv *env,
        jobject jobject1/* this */) {
    string hello = "Hello from C++";
    jint jint4 = env->GetVersion();
    LOGE("JNI version: %d", jint4);

    return env->NewStringUTF(hello.c_str());
}

jint add(JNIEnv *env, jclass clazz, jint a, jint b) {
    return a + b;
}

JNIEXPORT void JNICALL Java_jni_chowen_com_nativeapp_MainActivity_throwException
        (JNIEnv *env, jobject jobject1, jstring jstring1) {
    jclass  jclass1 = env->FindClass("java/io/IOException");
    const char* c = env->GetStringUTFChars(jstring1, 0);

//    env->FatalError(c); // 抛出一个致命异常

    jthrowable jthrowable1 = env->ExceptionOccurred();

    if (env->ThrowNew(jclass1, c) == JNI_OK){
        if(jthrowable1){
            LOGE("JNI throw suc ExceptionOccurred");
        }
        env->ExceptionDescribe();//打印exception msg
        env->ExceptionClear();

        LOGE("JNI throw suc");
    } else {
        LOGE("JNI throw failed");
    }
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    LOGE("JNI_OnLoad RegisterNatives JNI_OnLoad");
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }
    jint result = RegisterNatives(env);
    LOGE("RegisterNatives result: %d", result);
    return JNI_VERSION_1_6;
}


int RegisterNatives(JNIEnv *env) {
    jclass clazz = env->FindClass("jni/chowen/com/nativeapp/MainActivity");
    if (clazz == NULL) {
        LOGE("con't find class: jni/chowen/com/nativeapp/MainActivity");
        return JNI_ERR;
    }
    JNINativeMethod methods_MainActivity[] = {
            {"stringFromJNI", "()Ljava/lang/String;", (void *) stringFromJNI},
            {"add",           "(II)I",                (void *) add}
    };
    LOGE("can find class: jni/chowen/com/nativeapp/MainActivity %d >>> %d", sizeof(methods_MainActivity), sizeof(methods_MainActivity[0]));
    // int len = sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]);
    return env->RegisterNatives(clazz, methods_MainActivity,
                                sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]));
}

JNIEXPORT jstring JNICALL Java_jni_chowen_com_nativeapp_MainActivity_signture
        (JNIEnv *env, jobject jobject1, jstring jstring1) {
    //md5加密
    const char *jcstr = (env)->GetStringUTFChars(jstring1, 0);
    MD5 md5;
    md5.update(jcstr);
    string jstring2 = md5.toString();

    //package_name
    jstring packName = getPackname(env, jobject1, jobject1);
    const char *c = env->GetStringUTFChars(packName, 0);
    LOGE("getPackageName: %s", c);

    //signature
    jstring signatures = getSignature(env, jobject1, jobject1);
    const char *signaturesc = env->GetStringUTFChars(signatures, 0);
    LOGE("signatures: %s", signaturesc);

//    jstring js3 = (jstring) "jni.chowen.com.nativeapp";
//    const char* c33 = env->GetStringUTFChars(js3, 0);
//    if (strcmp(c, c33)) {
//        LOGE("getPackageName: %s", "is cmp!!!!");
//    }


//    return (*env)->NewStringUTF(env, jstring2.c_str());

    return env->NewStringUTF(jstring2.c_str());
}

jstring getPackname(JNIEnv *env, jobject clazz, jobject obj) {
    jclass native_class = env->GetObjectClass(obj);
    jmethodID mId = env->GetMethodID(native_class, "getPackageName", "()Ljava/lang/String;");
    jstring packName = static_cast<jstring>(env->CallObjectMethod(obj, mId));

    return packName;
}


jstring getSignature(JNIEnv *env, jobject clazz, jobject jobject1) {
    //PackageInfo packageInfo = getPackageManager().getPackageInfo(
//        getPackageName(), PackageManager.GET_SIGNATURES);
//Signature[] signs = packageInfo.signatures;
//Signature sign = signs[0];


    jclass native_class = env->GetObjectClass(clazz);
    jmethodID pm_id = env->GetMethodID(native_class, "getPackageManager",
                                       "()Landroid/content/pm/PackageManager;");
    jobject pm_obj = env->CallObjectMethod(clazz, pm_id);
    jclass pm_clazz = env->GetObjectClass(pm_obj);
// 得到 getPackageInfo 方法的 ID
    jmethodID package_info_id = env->GetMethodID(pm_clazz, "getPackageInfo",
                                                 "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    jstring pkg_str = getPackname(env, clazz, clazz);

// 获得应用包的信息
    jobject pi_obj = env->CallObjectMethod(pm_obj, package_info_id, pkg_str, 64);
// 获得 PackageInfo 类
    jclass pi_clazz = env->GetObjectClass(pi_obj);
// 获得签名数组属性的 ID
    jfieldID signatures_fieldId = env->GetFieldID(pi_clazz, "signatures",
                                                  "[Landroid/content/pm/Signature;");
    jobject signatures_obj = env->GetObjectField(pi_obj, signatures_fieldId);
    jobjectArray signaturesArray = (jobjectArray) signatures_obj;
    jsize size = env->GetArrayLength(signaturesArray);
    jobject signature_obj = env->GetObjectArrayElement(signaturesArray, 0);
    jclass signature_clazz = env->GetObjectClass(signature_obj);
    jmethodID string_id = env->GetMethodID(signature_clazz, "toCharsString",
                                           "()Ljava/lang/String;");
    jstring str = static_cast<jstring>(env->CallObjectMethod(signature_obj, string_id));
    char *c_msg = (char *) env->GetStringUTFChars(str, 0);
    LOGI("signsture: %s", c_msg);
    return str;
}

//JNIEXPORT jstring JNICALL Java_jni_chowen_com_nativeapp_MainActivity_stringFromJNI2
//        (JNIEnv *env, jobject js) {
//    LOGI("No data on command pipe!");
//
//    //抛java层异常
////    jclass newExcCls = env->FindClass("java/lang/IllegalArgumentException");
////    env->ThrowNew(newExcCls, "throw from JNI");
//
//    return env->NewStringUTF("from native str");
//}

JNIEXPORT jstring JNICALL Java_jni_chowen_com_nativeapp_MainActivity_fromJaveToNative
        (JNIEnv *env, jobject jobject1, jstring jstring1) {

    //method 1
//   jclass jclass1 = env->FindClass("jni/chowen/com/nativeapp/MainActivity");
// method 2
    jclass jclass1 = env->GetObjectClass(jobject1);

    jmethodID jmethodID2 = env->GetMethodID(jclass1, "<init>", "()V");
    jobject jobject2 = env->NewObject(jclass1, jmethodID2);

    jmethodID jmethodID1 = env->GetMethodID(jclass1, "fromNativeJNI2", "(Ljava/lang/String;)V");
    jstring message = env->NewStringUTF("call instance method");
    env->CallVoidMethod(jobject2, jmethodID1, message);

    jmethodID jmethodID4 = env->GetMethodID(jclass1, "fromNativeJNI", "()V");
    env->CallVoidMethod(jobject2, jmethodID4);


    jstring message2 = env->NewStringUTF("call instance method33333");
    jmethodID jmethodID3 = env->GetStaticMethodID(jclass1, "fromNativeJNI3",
                                                  "(Ljava/lang/String;)V");
    env->CallStaticVoidMethod(jclass1, jmethodID3, message2);

    jmethodID jmethodID5 = env->GetStaticMethodID(jclass1, "fromNativeJNI4", "()V");
    env->CallStaticVoidMethod(jclass1, jmethodID5);


    jclass jclass2 = env->FindClass("jni/chowen/com/nativeapp/CInterface");
    jmethodID jmethodID6 = env->GetStaticMethodID(jclass2, "setAndGetValue", "(I)I");
    jint jint2 = env->CallStaticIntMethod(jclass2, jmethodID6, 2000);
    LOGE("AndroidBitmap_getInfo failed, jint2: %d", jint2);


//    #修改field
    jfieldID jfieldID2 = env->GetFieldID(jclass1, "i", "I");
    env->SetIntField(jobject2, jfieldID2, 200);

    jfieldID jfieldID1 = env->GetFieldID(jclass1, "i", "I");
    jint jint1 = env->GetIntField(jobject2, jfieldID1);
    LOGE("AndroidBitmap_getInfo failed, result: %d", jint1);


    jmethodID jmethodID7 = env->GetMethodID(jclass1, "getValue", "()I");
    jint jint3 = env->CallIntMethod(jobject2, jmethodID7);
    LOGE("AndroidBitmap_getInfo failed, jint3: %d", jint3);

    env->DeleteLocalRef(jclass1);
    env->DeleteLocalRef(message);
    env->DeleteLocalRef(jobject2);
    env->DeleteLocalRef(message2);

    env->DeleteLocalRef(jclass2);

//    jclass newExcCls = env->FindClass("java/lang/IllegalArgumentException");
//    env->ThrowNew(newExcCls, "throw from JNI");

    return env->NewStringUTF("fromJaveToNative");
}
3.1.2函数动态注册方式

直接在JNI_OnLoad函数里动态注册相关函数,之前讲过JNI_OnLoad的初始化时机,是在System.loadLibrary里加载的,具体请看JNI方法注册及加载原理分析
下面以native-lib.cpp中两个函数为例:

//动态注册
static jstring stringFromJNI(
        JNIEnv *env,
        jobject jobject1/* this */) {
    string hello = "Hello from C++";
    jint jint4 = env->GetVersion();
    LOGE("JNI version: %d", jint4);

    return env->NewStringUTF(hello.c_str());
}

jint add(JNIEnv *env, jclass clazz, jint a, jint b) {
    return a + b;
}
//重载JNI_OnLoad函数,在其中可做初始化相关工作
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    LOGE("JNI_OnLoad RegisterNatives JNI_OnLoad");
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }
    jint result = RegisterNatives(env);
    LOGE("RegisterNatives result: %d", result);
    return JNI_VERSION_1_6;
}
//具体注册函数
int RegisterNatives(JNIEnv *env) {
    jclass clazz = env->FindClass("jni/chowen/com/nativeapp/MainActivity");
    if (clazz == NULL) {
        LOGE("con't find class: jni/chowen/com/nativeapp/MainActivity");
        return JNI_ERR;
    }
    JNINativeMethod methods_MainActivity[] = {
            {"stringFromJNI", "()Ljava/lang/String;", (void *) stringFromJNI},
            {"add",           "(II)I",                (void *) add}
    };
    LOGE("can find class: jni/chowen/com/nativeapp/MainActivity %d >>> %d", sizeof(methods_MainActivity), sizeof(methods_MainActivity[0]));
    // int len = sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]);
   // 进行函数动态注册
    return env->RegisterNatives(clazz, methods_MainActivity,
                                sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]));
}

Java层实现
声明对应的native函数

public native int add(int a, int b);
public native String stringFromJNI();

3.2CMake构建脚本文件编写

CMake是一个跨平台的构建系统,会在接下来详细介绍它的语法及使用。
在项目cpp文件夹中新建一个CMakeLists.txt文件,AS新建的时候会自动为大家新建一个,平时JNI开发会又子目录,可能需要新建很多CMakeLists.txt文件。

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.
#指定最小版本
cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

#IF (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Windows")
#    ADD_DEFINITIONS(-DWindows)
#ELSE (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux")
#    ADD_DEFINITIONS(-DLinux)
#ENDIF ()
# 搜索当前目录下的所有.cpp文件
aux_source_directory(. SRC_LIST) 
#将SRC_LIST下的cpp文件生成名为native-lib2的动态库
add_library(native-lib2 SHARED ${SRC_LIST} MD5.cpp)

#FILE(GLOB SRC_LIST_SUB "${PROJECT_SOURCE_DIR}/src/main/cpp/cppp/*.cpp")
#FILE(GLOB SRC_LIST_SUB2 "${PROJECT_SOURCE_DIR}/src/main/cpp/*.cpp")
#add_library(native-lib SHARED ${SRC_LIST} ${SRC_LIST_SUB})

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
#查找指定的log库文件,并将路径存到log-lib中
find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
# 指定动态链接库,native-lib2, jnigraphics,log-lib三个库
target_link_libraries( # Specifies the target library.
        native-lib2
        jnigraphics
        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})
简单说明,后续会详细讲解:

指定最小版本
cmake_minimum_required(VERSION 3.4.1)
指定动态链接库,native-lib2, jnigraphics,log-lib三个库
target_link_libraries( # Specifies the target library.
native-lib2
jnigraphics
# Links the target library to the log library
# included in the NDK.
${log-lib})
查找指定的log库文件,并将路径存到log-lib中
find_library( # Sets the name of the path variable.
log-lib

    # Specifies the name of the NDK library that
    # you want CMake to locate.
    log)

搜索当前目录下的所有.cpp文件
aux_source_directory(. SRC_LIST)
将SRC_LIST下的cpp文件生成名为native-lib2的动态库
add_library(native-lib2 SHARED ${SRC_LIST} MD5.cpp)

4.gradle打包脚本编写

在根目录build.gradle文件中android里配置exteranlNativeBuild

android {
    ...
       
        externalNativeBuild {
            cmake {
                cppFlags ""
                // 配置构建相应的cpu架构平台so库
                abiFilters 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
            }
        }
    }
  
    externalNativeBuild {
       //指定CMakeLists.txt文件目录路径
        cmake {
            path "src/main/cpp/CMakeLists.txt"
        }
    }
}

5.通过CMake构建即可打出SO动态库文件

编译完成后会在项目以下(build/intermediates/cmake/debug/obj/对应的cpu架构平台/native-lib.so)文件中出现一个so文件。
../NativeApp/app/build/intermediates/cmake/debug/obj/armeabi-v7a

6.项目加载so库

引用编译好的so库,拷贝到jniLibs目录下,在项目使用之前需要loadLibrary动态库。说明下编译出来的是libnative-lib2名称,在System.loadLibrary中需要去掉lib。

// Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib2");
    }

That's All

相关文章

网友评论

    本文标题:JNI实例演示

    本文链接:https://www.haomeiwen.com/subject/aaefcctx.html