爲什麼選擇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 :=
LOCAL_LDLIBS := -L
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語言的數據類型之間的轉換關係。