NDK開發—基礎知識實戰Demo

簡介

前面寫了幾篇NDK相關的文章:java

這個demo就是對這幾篇文章中涉及內容的一個應用。其功能只是對數組進行排序。若是單純使用Java來作十分簡單,只需寫個排序方法,或者將數組轉化爲集合,而後調用sort函數就ok了。
但咱們的目的是練習JNI因此須要使勁的折騰一下,其中主要涉及了:android

  • Java調用JNI函數
  • JNI調用Java函數
  • JNI中打印Android Log
  • JNI中調用c++函數庫
  • JNI調用普通方法/靜態方法
  • JNI全局變量使用
  • JNI數組的操做
  • JNI異常處理

演示圖

點擊按鈕對數組排序:

這裏寫圖片描述

排序成功:

這裏寫圖片描述

Java代碼:

Activity代碼:

public class MainActivity extends AppCompatActivity {

    // 導入動態庫
    static {
        System.loadLibrary("test");
    }

    // 本地排序方法
    public native boolean doSort(int [] array);

    private Button mOrderBtn;
    private TextView mResultText;

    // 須要排序的數組
    int[] orderArrays = {6,2,5,9,1,3,4,7,8,0,10};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 調用jni調用靜態方法返回一個字符串(hello world?)
        Log.d("TestNDK", JNIHelper.getStringFromNative());

        mResultText = (TextView) this.findViewById(R.id.result);
        setResultText(orderArrays);

        mOrderBtn = (Button) this.findViewById(R.id.plus_btn);
        mOrderBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 調用本地排序方法, 根據是否有異常打印success/failed
                boolean isSuccess = doSort(orderArrays);
                mOrderBtn.setText(isSuccess?"success":"failed");
            }
        });

    }

    public void setResultText(String resultText) {
        mResultText.setText(resultText);
    }

    public void setResultText(int[] array) {
        mResultText.setText(Arrays.toString(array));
    }

}

JNI工具類:

public class JNIHelper {

    @Keep
    private void updateStatus(String msg) {
        if (msg.toLowerCase().contains("error")) {
            Log.e("JniHandler", "Native Err: " + msg);
        } else {
            Log.i("JniHandler", "Native Msg: " + msg);
        }
    }

    public static native String getStringFromNative();
}

JNI代碼

頭文件

#include <jni.h>
/* Header for class com_dean_testndk_JNIHelper */

#ifndef _Included_com_dean_testndk_JNIHelper
#define _Included_com_dean_testndk_JNIHelper

#ifdef __cplusplus
extern "C" {
#endif
/* * Class: com_dean_testndk_JNIHelper * Method: getStringFromNative * Signature: ()Ljava/lang/String; */
JNIEXPORT jstring JNICALL
        Java_com_dean_testndk_JNIHelper_getStringFromNative(JNIEnv *, jclass);

JNIEXPORT jboolean JNICALL
        Java_com_dean_testndk_MainActivity_doSort(JNIEnv *, jobject, jintArray);
#ifdef __cplusplus
}
#endif
#endif

c++代碼

//
// Created by Dean Guo on 7/23/16.
//
#include "com_dean_testndk_JNIHelper.h"
#include "string.h"
#include "iostream"
#include "vector"
#include "algorithm"
#include <android/log.h>

using namespace std;
// Android log function wrappers
static const char* kTAG = "testNDK";
#define LOGI(...) \
  ((void)__android_log_print(ANDROID_LOG_INFO, kTAG, __VA_ARGS__))
#define LOGW(...) \
  ((void)__android_log_print(ANDROID_LOG_WARN, kTAG, __VA_ARGS__))
#define LOGE(...) \
  ((void)__android_log_print(ANDROID_LOG_ERROR, kTAG, __VA_ARGS__))

struct GLOBAL_CONTEXT {
    JavaVM* javaVM;
    jclass mainActivityClz;
    jobject mainActivityObj;

    jclass jniHelperClz;
    jobject jniHelperObj;
};

/** * 全局引用 */
GLOBAL_CONTEXT mContext;

void sendJavaMsg(JNIEnv *env, jobject instance, jmethodID func, const char *msg) {

    jstring javaMsg = env->NewStringUTF(msg);
    env->CallVoidMethod(instance, func, javaMsg);
}

void callSetResult(JNIEnv *env, vector<int> vector_) {

    char buff[1000] = "";

    vector<int>::iterator it;

    for (it = vector_.begin(); it != vector_.end(); it++) {
        char numChar[100] = "";
        sprintf(numChar, "%d", *it);
        strcat(numChar, ",");
        strcat(buff, numChar);
    }

    jstring javaMsg = env->NewStringUTF(buff);
    jthrowable ex = env->ExceptionOccurred();
    if (0 != ex) {
        env->ExceptionClear();
        env->DeleteLocalRef(javaMsg);
        LOGE("Exception-sendJavaMsg!");
    } else {
        jmethodID methodID = env->GetMethodID(mContext.mainActivityClz, "setResultText", "(Ljava/lang/String;)V");
        env->CallVoidMethod(mContext.mainActivityObj, methodID, javaMsg);
        env->DeleteLocalRef(javaMsg);
    }
}

/** * 根據排序好的vector集合, 建立排序數組返回 */
void callSetArrayResult(JNIEnv *env, vector<int> vector_) {

    int size = vector_.size();

    // 排序後須要返回的數組
    jintArray array = env->NewIntArray(size);

    // 爲jni數組賦值
    jint *num = new jint[size];
    for (int i = 0; i < size; i++)
    {
        num[i] = vector_[i];
    }
    env->SetIntArrayRegion(array, 0, size, num);

    // 調用java方法傳遞排序好的數組
    jmethodID methodID = env->GetMethodID(mContext.mainActivityClz, "setResultText", "([I)V");
    env->CallVoidMethod(mContext.mainActivityObj, methodID, array);

    // 釋放數組
    env->ReleaseIntArrayElements(array, num, 0);
}

/** * 系統函數, JNI初始化時調用。 * 可在這個函數這裏作一些初始化賦值 */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {

    LOGI("JNI_OnLoad");

    JNIEnv* env;

    mContext.javaVM = vm;

    if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
        LOGE("JNI_ERR");
        return JNI_ERR; // 不支持該版本
    }

    // 對JNIHelper設置全局引用
    jclass clz = env->FindClass("com/dean/testndk/JNIHelper");
    mContext.jniHelperClz = (jclass) env->NewGlobalRef(clz);

    jmethodID jniHelperCtor = env->GetMethodID(mContext.jniHelperClz, "<init>", "()V");
    jobject handler = env->NewObject(mContext.jniHelperClz, jniHelperCtor);
    mContext.jniHelperObj = env->NewGlobalRef(handler);

    // 調用JNIHelper中updateStatus方法打印信息
    jmethodID statusId = env->GetMethodID(mContext.jniHelperClz, "updateStatus", "(Ljava/lang/String;)V");
    sendJavaMsg(env, mContext.jniHelperObj, statusId, "JNI: initializing...");

    return JNI_VERSION_1_6;
}

/** * 簡單返回一個字符串 */
JNIEXPORT jstring JNICALL Java_com_dean_testndk_JNIHelper_getStringFromNative
        (JNIEnv *env, jclass jclz) {
    return env->NewStringUTF("from Native");
}

/** * 排序方法, 成功返回true, 不然false */
JNIEXPORT jboolean JNICALL
Java_com_dean_testndk_MainActivity_doSort(JNIEnv *env, jobject instance, jintArray arr_) {
    // 設置全局引用
    mContext.mainActivityClz = env->GetObjectClass(instance);
    mContext.mainActivityObj = env->NewGlobalRef(instance);

    // 獲取傳遞過來的數組指針
    jint *nums = env->GetIntArrayElements(arr_, NULL);
    // 獲取數組長度
    jsize len = env->GetArrayLength(arr_);

    // 使用標準庫中的vector排序
    vector<int> mVector;
    for (int i=0; i< len; i++) {
        mVector.push_back(nums[i]);
    }
    sort(mVector.begin(), mVector.end());

    // 調用返回結果函數
    callSetArrayResult(env, mVector);

    // 釋放數組
    env->ReleaseIntArrayElements(arr_, nums, 0);

    // 判斷是否有異常, 沒有表示成功返回true, 不然false
    jthrowable ex = env->ExceptionOccurred();
    if (0 != ex) {
        env->ExceptionClear();
        LOGE("Exception!");
        return false;
    } else {
        return true;
    }
}

最後

下載

https://github.com/a396901990/TestNDKios

總結

demo中全部詳細的知識點均可以在NDK開發-零散知識點整理中找到。c++

若是想深刻NDK推薦Google NDK的官方demo:
https://github.com/googlesamples/android-ndkgit

相關文章
相關標籤/搜索