Jni系列1起航例子

爲什麼選擇JNI,我想大家都應該有理解。要麼就是前人寫好了庫,自己懶了不想重寫,要麼就是你們項目這個技術比較核心,不想輕鬆被人給反編譯,也有可能你需要與底層打交道,這時候由於某種理由我們可能就需要選擇JNI開發。

Jni實際上就是用於JAVA與C進行通信的,NDK是用來編譯C語言文件成so給Android進行調用的。所以大家需要區分清楚JNI與NDK不同的功能。C語言中的函數通過jni數據類型接收JAVA的傳入參數,但是一般jni數據類型我們不能直接在C中使用,這時候就需要將jni類型,轉化爲C語言能夠使用的數據類型了。

本文以Android Studio爲例來說明一個非常簡單的JNI demo。個人覺得JNI在Android開發中還是比較重要的,況且最近項目因爲實時性就用到了此塊。打算好好講解一下JNI開發。凡事以基礎爲起點,本文首先是運行起來一個JNI demo。另外我使用的是Android mk文件,Android Studio2.2已經可以使用CMakeList文件了,變得更加方便與智能,不過我覺得你習慣使用哪個,覺得哪個更順手自己決定就好。

1,創建一個Android Studio項目,新建一個類JniUtils

package org.eclipse.paho.android.jnidemo.jniutil;

/**
 * Created by IBM on 2016/11/10.
 */

public class JniUtils {
    static {
        System.loadLibrary("jnilib");
    }
    public native int sum(int a,int b);
    public native String getString(String s1,String s2);

}

System.loadLibrary(“jnilib”);這個就是加載ndk編譯生成的so庫,此處生出的庫名字是libjnilib.so,注意名字要去掉lib和lib.so

2,使用javah命令生成JniUtils的.h文件,名字是org_eclipse_paho_android_jnidemo_jniutil_JniUtils.h
本文生成的.h文件具體內容是:

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

#ifndef _Included_org_eclipse_paho_android_jnidemo_jniutil_JniUtils
#define _Included_org_eclipse_paho_android_jnidemo_jniutil_JniUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     org_eclipse_paho_android_jnidemo_jniutil_JniUtils
 * Method:    sum
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_org_eclipse_paho_android_jnidemo_jniutil_JniUtils_sum
  (JNIEnv *, jobject, jint, jint);

/*
 * Class:     org_eclipse_paho_android_jnidemo_jniutil_JniUtils
 * Method:    getString
 * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_org_eclipse_paho_android_jnidemo_jniutil_JniUtils_getString
  (JNIEnv *, jobject, jstring, jstring);

#ifdef __cplusplus
}
#endif
#endif

3,新建jniUtils.c文件,就是開始寫C代碼實現

//
// Created by IBM on 2016/11/10.
//
#include "org_eclipse_paho_android_jnidemo_jniutil_JniUtils.h"
#include<stdlib.h>
// 引入log頭文件
#include <android/log.h>
// log標籤
#define TAG "JniUtils"
// 定義info信息
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
// 定義debug信息
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
// 定義error信息
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)

JNIEXPORT jint JNICALL Java_org_eclipse_paho_android_jnidemo_jniutil_JniUtils_sum
  (JNIEnv * env, jobject thiz, jint a, jint b){
    int re = a + b;
    return (jint)re;
  }

JNIEXPORT jstring JNICALL Java_org_eclipse_paho_android_jnidemo_jniutil_JniUtils_getString
  (JNIEnv * env, jobject thiz, jstring str1, jstring str2){
       char *str;
       //char result[256];
       str = (*env)->GetStringUTFChars(env,str1,NULL);

       //strcpy(result,str);
       (*env)->ReleaseStringUTFChars(env,str1,str);
       char *re = "jni to java";
       jstring ret = (*env)->NewStringUTF(env,re);
       return ret;
  }

具體的代碼如果你是剛接觸jni可以先不要搞懂,先照着例子做,我一般都是先看現象,再去看如何實現,這樣能一開始放心能出結果。
3,編寫mk文件
在Android Studio的jni目錄下新建Android.mk文件和Application.mk文件
Android.mk:
LOCAL_PATH := (callmydir)include (CLEAR_VARS)
LOCAL_LDLIBS := -L(SYSROOT)/usr/libllogLOCALMODULE:=jnilibLOCALSRCFILES:=jniUtils.cinclude (BUILD_SHARED_LIBRARY)
Application.mk文件
APP_ABI := armeabi

4,然後你就可以使用ndk進行編譯了。
現在回到MainActivity文件裏,加入幾行代碼:

JniUtils jniUtils = new JniUtils();
        int re = jniUtils.sum(2,3);
        Log.i(TAG, "onCreate: Sum result is "+re);
        String r  = jniUtils.getString("hehe","haha");
        Log.i(TAG, "onCreate: getString result is"+r);

運行虛擬機即可看到控制檯打印輸出結果:
11-10 09:54:54.509 2546-2546/? I/MainActivity: onCreate: Sum result is 5
11-10 09:54:54.509 2546-2546/? I/MainActivity: onCreate: getString result isjni to java
說明我們的JNI例子是跑通的。

下一篇明天再寫基本JNI數據類型與C語言的數據類型之間的轉換關係。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章