編輯:關於Android編程
在第零篇文章簡單地介紹了JNI編程的模式之後,後面兩三篇文章,我們又針對JNI中的一些概念做了一些簡單的介紹,也不知道我到底說的清楚沒有,但相信很多童鞋跟我一樣,在剛開始學習一個東西的時候,入門最好的方式就是一個現成的例子來參考,慢慢研究,再學習概念,再回過來研究代碼,加深印象,從而開始慢慢掌握。
今天我們就再來做一個小Demo,這個例子會比前面稍微復雜一點,但是如果閱讀過前面幾篇文章的話,理解起來也還是很簡單的。很多東西就是這樣,未知的時候很可怕,理解了就很簡單了。
1)我們首先定義一個Java類,裡面包含幾個native方法,如下:
public class ParamTransferTest { public static int testval = 1; public native void changeTestVal(); public native int add(int x, int y); public native String addTail(String tail); public native int[] changeArray(int[] arr); }
/* DO NOT EDIT THIS FILE - it is machine generated */ #include/* Header for class com_lms_jni_ParamTransferTest */ #ifndef _Included_com_lms_jni_ParamTransferTest #define _Included_com_lms_jni_ParamTransferTest #ifdef __cplusplus extern "C" { #endif /* * Class: com_lms_jni_ParamTransferTest * Method: changeTestVal * Signature: ()V */ JNIEXPORT void JNICALL Java_com_lms_jni_ParamTransferTest_changeTestVal (JNIEnv *, jobject); /* * Class: com_lms_jni_ParamTransferTest * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_lms_jni_ParamTransferTest_add (JNIEnv *, jobject, jint, jint); /* * Class: com_lms_jni_ParamTransferTest * Method: addTail * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_lms_jni_ParamTransferTest_addTail (JNIEnv *, jobject, jstring); /* * Class: com_lms_jni_ParamTransferTest * Method: changeArray * Signature: ([I)[I */ JNIEXPORT jintArray JNICALL Java_com_lms_jni_ParamTransferTest_changeArray (JNIEnv *, jobject, jintArray); #ifdef __cplusplus } #endif #endif
3)編寫 C 文件,如下:
#include#include #include "com_lms_jni_ParamTransferTest.h" #include #include #include #define LOG_TAG "System.out" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) char* Jstring2CStr(JNIEnv * env, jstring str){ char * rtn = NULL; jclass clsstring = (*env)->FindClass(env, "java/lang/String");//通過FindClass方法獲得Java的String類 jstring strencode = (*env)->NewStringUTF(env, "UTF-8");//調用NewStringUTF方法,獲得"UTF-8"字符串,作為編碼 jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");//獲取String類的getBytes方法 jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, str, mid, strencode);//調用String類的getBytes方法 jsize alen = (*env)->GetArrayLength(env, barr);//獲得數組長度 jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);//獲得數組的首地址,C/C++中數組的首元素就是一個指針 if(alen > 0){ rtn = (char*) malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; }//上面這一步是將數組的值復制到一個char*的數組中,也就是C/C++的char數組,因為C/C++沒有字符串概念,最後以0結尾。 (*env)->ReleaseByteArrayElements(env, barr, ba, 0);//釋放內存 return rtn; } /* * Class: com_lms_jni_ParamTransferTest * Method: changeTestVal * Signature: ()V */ JNIEXPORT void JNICALL Java_com_lms_jni_ParamTransferTest_changeTestVal (JNIEnv * env, jobject obj){ jclass clazz = (*env)->GetObjectClass(env,obj);//獲得obj對應的類,也就是ParamTransferTest jint val = (*env)->GetStaticIntField(env, clazz, (*env)->GetStaticFieldID(env, clazz,"testval","I"));//獲取字段testval的值 LOGI("before change testval = %d", val);//添加Log信息 val = val + 1; LOGI("after change testval = %d", val); (*env)->SetStaticIntField(env, clazz,(*env)->GetStaticFieldID(env, clazz,"testval","I"),val);//設置字段testval的值 } /* * Class: com_lms_jni_ParamTransferTest * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_lms_jni_ParamTransferTest_add (JNIEnv * env, jobject obj, jint x, jint y){ LOGI("x = %d", x); LOGI("y = %d", y); return x + y; //返回參數x和y的和 } /* * Class: com_lms_jni_ParamTransferTest * Method: addTail * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_lms_jni_ParamTransferTest_addTail (JNIEnv * env, jobject obj, jstring str){ char* p = Jstring2CStr(env,str);//將Java中的string轉化為C/C++中的char數組 LOGI("str = %s", p); char* newStr = " Tail "; return (*env)->NewStringUTF(env, strcat(p, newStr));//調用strcat函數連接兩個char數組,將通過NewStringUTF返回。 } /* * Class: com_lms_jni_ParamTransferTest * Method: changeArray * Signature: ([I)[I */ JNIEXPORT jintArray JNICALL Java_com_lms_jni_ParamTransferTest_changeArray (JNIEnv * env, jobject obj, jintArray ja){ int len = (*env)->GetArrayLength(env, ja);/獲取數組長度 LOGI("len = %d", len); LOGI("address = %#x", &ja); jint* arr = (*env)->GetIntArrayElements(env, ja, 0);//將數組中的所有元素存入以jint*為首地址的數組中(數組的首地址就是數組名) int i = 0; for(;i < len; i++){ LOGI("arr[%d] = %d", i, *(arr + i)); *(arr + i) += 10; } //數組中每個元素的值加上10return ja;由於GetIntArrayElements的最後一個參數是0,即表明獲取的數組是不復制的,即它們操作同一塊內存,所以返回哪個數組都是ok的 return ja; }
對應這四個方法,在上面都添加了一些注釋,大家看著注釋,自己研究一下邏輯,也就清楚了這幾個方法實現的功能是什麼,很簡單的。
4)編寫Android.mk文件,如下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := com_lms_jni_HwDemo LOCAL_SRC_FILES := \ HwDemo.c \ JniTest.c \ ParamTransferTest.c LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY)
而這裡LOCAL_LDLIBS是JNI中運用log所需要添加的動態包,下一篇文章會講。
5)編寫好Android.mk文件之後,就利用ndk-build文件進行編譯,文件結構如下:
在jni目錄下運行ndk-build,如下:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+PGltZyBzcmM9"/uploadfile/Collfiles/20140523/20140523091221129.jpg" alt="\">
到這裡,關於JNI層的實現就結事了,下面就是Java端的事情。
6)Activity中調用,如下:
public class HwDemo extends Activity { static { System.loadLibrary("com_lms_jni_HwDemo");//加載動態包,名稱就是Android.mk中的Module名稱。 } public native String printHello(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ... TextView tvAdd = (TextView)findViewById(R.id.tvAdd); TextView tvString = (TextView)findViewById(R.id.tvString); TextView tvArray = (TextView)findViewById(R.id.tvArray); TextView tvChangeTestVal = (TextView)findViewById(R.id.tvChangeTestVal); ParamTransferTest ptt = new ParamTransferTest(); //調用changeTestVa()方法 ptt.changeTestVal(); tvChangeTestVal.setText("" + ptt.testval); //調用add方法 int sum = ptt.add(1, 2); tvAdd.setText(String.valueOf(sum)); //調用addTail方法 tvString.setText(ptt.addTail("lms")); //調用changeArray方法 int[] newArr = ptt.changeArray(new int[]{1,2}); StringBuilder sb = new StringBuilder(); sb.append("["); sb.append(newArr[0]).append(",").append(newArr[1]); sb.append("]"); tvArray.setText(sb.toString()); } }
7)結果顯示:
系統核心服務,或者啟動Zygote虛擬機的時候,用到了大量的JNI層面的框架,大家如果對JNI熟悉了解了,再去了解這些框架的東西,會有很大的幫助的。
結束。
今天再來介紹該作者的另一個開源項目circular-progress-button,效果更酷炫。項目地址:https://github.com/dmytrodanylyk
Widget引入 我們可以把Widget理解成放置在桌面上的小組件(掛件),有了Widget,我們可以很方便地直接在桌面上進行各種操作,例如播放音樂。 
我們的qq有時候會有異地登陸的提示,這是怎麼回事呢?難道我們的qq號被盜了?如何才能加強手機qq的安全管理?下面我們就一起來看看吧!1、大家登錄自己的QQ賬
本文實例講述了Android編程實現ViewPager多頁面滑動切換及動畫效果的方法。分享給大家供大家參考,具體如下:一、首先,我們來看一下效果圖,這是新浪微博的Tab滑