JNI,是用於開發本地C函數庫的技術。用於鏈接JAVA和C或者C++語言的橋樑。在部分android項目開發中,我們是需要用到這項技術的。在升級APP的時候,我們有時間需要用到增量更新技術,這個也是基於JNI技術實現的,詳情請點擊:基於JNI技術實現增量更新
那麼廢話不多說,進入正題。
開發JNI,需要用到NDK,這個大家應該都知道了。還需要一個linux的開發環境。一般而言,可以使用虛擬機裝一個ubantu,博主以前就是搞linux開發的,這點還是比較熟悉。但是對於大部分android開發者而言,弄一個虛擬機成本太高。那麼,我們需要搭建一個模擬linux的開發環境。這個博主就不說了,直接上鍊接
以上博文其實只需要做完第三步即可,如果是下載安裝谷歌官方集成的eclipse,第三步都可以不用做了。
好,當一切東西都準備好了之後,我們以一個例子來講解如何開發一個JNI項目。
一、新建一個Android項目
二、C語言方法實現。
1、新建本地native方法
public class DataProvider {
public native int add(int x, int y); //
public native String sayHelloInC(String s);
public native int[] intMethod(int[] iNum);
}
2、編譯native方法
3、創建JNI目錄
4、編寫.c文件
#include <stdio.h>
#include "com_example_ndkpassdata_DataProvider.h"
#include <android/log.h>
#include <string.h>
#define LOG_TAG "clog"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
//將java語言中的字符串格式轉換爲C語言中的字符串格式。
char* Jstring2CStr(JNIEnv* env, jstring jstr) {
char* rtn = NULL;
jclass clsstring = (*env)->FindClass(env, "java/lang/String");
jstring strencode = (*env)->NewStringUTF(env, "GB2312");
jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",
"(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
strencode); // String .getByte("GB2312");
jsize alen = (*env)->GetArrayLength(env, barr);
jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
if (alen > 0) {
rtn = (char*) malloc(alen + 1); //"\0"
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
(*env)->ReleaseByteArrayElements(env, barr, ba, 0); //
return rtn;
}
JNIEXPORT jint JNICALL Java_com_example_ndkpassdata_DataProvider_add(
JNIEnv * env, jobject jobject, jint x, jint y) {
// 想在logcat控制檯上 打印日誌
LOGD("x=%d", x);
LOGI("y=%d", y);
// log.i(TAG,"sss");
return x + y;
}
JNIEXPORT jstring JNICALL Java_com_example_ndkpassdata_DataProvider_sayHelloInC(
JNIEnv * env, jobject jobject, jstring str) {
char* c = "hello";
// 在C語言中不能直接操作java中的字符串
// 把java中的字符串轉換成c語言中 char數組
char* cstr = Jstring2CStr(env, str);
strcat(cstr, c);
LOGD("%s", cstr);
return (*env)->NewStringUTF(env, cstr);
}
JNIEXPORT jintArray JNICALL Java_com_example_ndkpassdata_DataProvider_intMethod(
JNIEnv * env, jobject jobject, jintArray jarray) {
// jArray 遍歷數組 jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
// 數組的長度 jsize (*GetArrayLength)(JNIEnv*, jarray);
// 對數組中每個元素 +5
int length = (*env)->GetArrayLength(env, jarray);
//拿到指針初始位置
int* array = (*env)->GetIntArrayElements(env, jarray, 0);
int i = 0;
for (; i < length; i++) {
*(array + i) += 5;
}
return jarray;
}
5、編寫android.mk文件
LOCAL_PATH := $(call my-dir) // 返回當前c代碼目錄
include $(CLEAR_VARS) // 清楚了所有 已local 開頭的配置文件 唯獨不清楚LOCAL_PATH
LOCAL_MODULE := hello // 庫函數的名字 嚴格遵守makefile 格式 lib .so 如果前面加lib 不會自動生成了
LOCAL_SRC_FILES := Hello.c //源文件名稱,就是剛剛新建的那個.c文件的名稱。
include $(BUILD_SHARED_LIBRARY) // 加入庫函數
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libhello
LOCAL_SRC_FILES := Hello.c
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
6、編譯C文件
當android文件寫好之後,一切的準備工作都已經就緒,這個時候我們只需要調用NDK去編譯該項目即可上次.so動態庫文件。這個時候需要啓動Cygwin,然後來到該項目的目錄下,調用ndk-build命令即可。過程如圖所示:三、C語言函數庫方法調用
public class MainActivity extends Activity {
DataProvider provider;
static {
System.loadLibrary("hello");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
provider = new DataProvider();
}
public void click1(View view) {
int result = provider.add(6, 8);
System.out.println(result);
}
public void click2(View view) {
String str = provider.sayHelloInC("freedom");
Toast.makeText(getApplicationContext(), str, 0).show();
}
public void click3(View view) {
int[] arr = new int[] { 5, 6, 7, 8, 9 };
provider.intMethod(arr);
for (int i : arr) {
System.out.println(i);
}
}
}