編輯:關於Android編程
當編寫一個混合有本地C代碼和Java的應用程序時,需要使用Java本地接口(JNI)作為連接橋梁。JNI作為一個軟件層和API,允許使用本地代碼調用Java對象的方法,同時也允許在Java方法中調用本地函數。
在Java端,開發者所需要做的僅僅是在連接本地函數的方法之前加上native關鍵字。這樣VM就會去尋找這個本地函數。
從Java調用本地函數時,需要在類中定義一個帶有native關鍵字的特有方法,作為連接本地代碼的橋梁。通過這個定義,嘗試調用本地方法時JVM會找到一個名字帶有包名,類名和方法名的本地函數。
package com.example.liyuanjing.jniproject; import android.util.Log; public class NativeSorting { static { System.loadLibrary("sorting_jni"); } public NativeSorting() { } public void sortIntegers(int[] ints) { nativeSort(ints); for (int i = 0; i < ints.length-1; i++) { System.out.print(String.valueOf(ints[i])); Log.i("liyuanjinglyj",String.valueOf(ints[i])); } } private native void nativeSort(int[] ints); }
上面是一個簡化的示例,包括一個對int數組進行排序的方法。除構造函數之外還有兩個方法。第一個是sortIntegers(),它是一個常規的Java方法,可以在其他Java類中調用它。第二個是nativeSort(),這個方法指向本地代碼中的函數。雖然可以把本地方法定義為公共的,但更好的做法是把它們作為私有方法包裝在一個Java方法中,以便進行一些錯誤處理。
可以從頭開始寫本地代碼,但也可以借助javah工具來生成部分代碼,該工具在Java SDK中。它會生成一個C語言頭文件,包括本地方法對應的函數定義。首先要編譯Java程序代碼,然後在當前項目的src/main目錄運行如下命令:
javah -classpath ../../build/intermediates/classes/debug/ -d jni/ com.example.liyuanjing.jniproject.NativeSorting
上面命令展示了如何為之前示例代碼中的NativeSorting生成一個頭文件。-classpath參數指定了編譯好的類文件位置,注意不是DEX文件。-d參數指定了生成頭文件的輸出目錄。運行完命令後,會在jni目錄生成com_example_liyuanjing_jniproject_NativeSorting.h文件,它包含了本地函數的定義。
/* DO NOT EDIT THIS FILE - it is machine generated */ #include/* Header for class com_example_liyuanjing_jniproject_NativeSorting */ #ifndef _Included_com_example_liyuanjing_jniproject_NativeSorting #define _Included_com_example_liyuanjing_jniproject_NativeSorting #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_liyuanjing_jniproject_NativeSorting * Method: nativeSort * Signature: ([I)V */ JNIEXPORT void JNICALL Java_com_example_liyuanjing_jniproject_NativeSorting_nativeSort (JNIEnv *, jobject, jintArray); #ifdef __cplusplus } #endif #endif
這段代碼即為生成的頭文件。正如第一行注釋所說,不要修改這個文件。開發者所要做的就是把函數定義復制到實現該函數的.c文件中。
下面的代碼展示了頭文件com_example_liyuanjing_jniproject_NativeSorting.h中的JNI函數實現,本例沒有在JNI_OnLoad函數做太多的操作,只是返回了代表當前JNI版本為1.6的常量,這是Dalvik VM支持的一個版本,下面是array.c代碼:
#include#include #include "com_example_liyuanjing_jniproject_NativeSorting.h" void quicksort(int *arr, int start, int end); JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_VERSION_1_6; } JNIEXPORT void JNICALL Java_com_example_liyuanjing_jniproject_NativeSorting_nativeSort (JNIEnv *env, jobject obj, jintArray data) { jint* array = (*env)->GetIntArrayElements(env, data, 0); jint length = (*env)->GetArrayLength(env, data); quicksort(array, 0, length); (*env)->ReleaseIntArrayElements(env, data, array, 0); } void quicksort(int *arr, int start, int end) { int i, j, temp; for (i = 0; i < end-1; i++) { for (j = 0; j < end - i-1; j++) { if (*(arr+j) < *(arr+j+1)) { temp = *(arr+j); *(arr+j) = *(arr+j+1); *(arr+j+1) = temp; } } } }
這個示例中,函數GetIntArrayElements,GetArrayLength和ReleaseIntArrayElements都是特定的JNI代碼。第一個函數得到一個本地數據指針,以便把數據傳給普通的C函數;第二個函數返回數據的大小;第三個函數告訴JVM本地端的工作已經完成,需要把數組復制回原地。這些函數都是必須的,因為從Java到JNI傳送復雜的數據類型時必須通過JNIEnv對象來完成。
注意:調用GetIntArrayElements返回一個jint指針,指向函數中jintArray裡的數據,接下來就可以把jint指針作為普通int類型指針來使用。
要想Android能運行起來,必須到NDK目錄android-ndk-r10d\samples\native-activity\jni目錄下拷貝Android.mk,到剛才放置com_example_liyuanjing_jniproject_NativeSorting.h和array.c同一目錄下,當然還要更改Android.mk的幾個值。
LOCAL_PATH := $(call my-dir)
$(call import-module,android/native_app_glue)
一個Android.mk file首先必須定義好LOCAL_PATH變量。它用於在開發樹中查找源文件。在這個例子中,宏函數’my-dir’, 由編譯系統提供,用於返回當前路徑(即包含Android.mk file文件的目錄)。
CLEAR_VARS由編譯系統提供,指定讓GNU MAKEFILE為你清除許多LOCAL_XXX變量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),
除LOCAL_PATH 。這是必要的,因為所有的編譯控制文件都在同一個GNU MAKE執行環境中,所有的變量都是全局的。
LOCAL_MODULE變量必須定義,以標識你在Android.mk文件中描述的每個模塊。名稱必須是唯一的,而且不包含任何空格。注意編譯系統會自動產生合適的前綴和後綴,換句話說,一個被命名為'sorting_jni'的共享庫模塊,將會生成'libsorting_jni'文件。
重要注意事項
如果你把庫命名為‘libhelloworld’,編譯系統將不會添加任何的lib前綴,也會生成libhelloworld.so,這是為了支持來源於Android平台的源代碼的Android.mk文件,如果你確實需要這麼做的話。
LOCAL_SRC_FILES變量必須包含將要編譯打包進模塊中的C或C++源代碼文件。注意,你不用在這裡列出頭文件和包含文件,因為編譯系統將會自動為你找出依賴型的文件;僅僅列出直接傳遞給編譯器的源代碼文件就好。【注意,默認的C++源碼文件的擴展名是’.cpp’. 指定一個不同的擴展名也是可能的,只要定義LOCAL_DEFAULT_CPP_EXTENSION變量,不要忘記開始的小圓點(也就是定義為‘.cxx’,而不是‘cxx’)(當然這一步我們一般不會去改它)】
BUILD_SHARED_LIBRARY是編譯系統提供的變量,指向一個GNU Makefile腳本(應該就是在build/core目錄下的shared_library.mk),負責收集自從上次調用'include $(CLEAR_VARS)'以來,定義在LOCAL_XXX變量中的所有信息,並且決定編譯什麼,如何正確地去做。並根據其規則生成靜態庫。同理對於靜態庫。
當配置完上面所說的一個C頭文件,一個.c文件,一個Android.mk文件後,進入CMD到當前目錄中。輸入ndk-build命令:
[armeabi] Compile thumb : sorting_jni <= array.c
[armeabi] SharedLibrary : libsorting_jni.so
[armeabi] Install : libsorting_jni.so => libs/armeabi/libsorting_jni.so
如果沒有意外會顯示上述正確結果。
然後在Android Studio項目的app/src/main/目錄下建立jinLibs目錄將生成的libs目錄中的文件拷貝到JinLibs目錄中。如下圖所示:
然後調用此方法,就可以實現Android使用JNI的功能了。
05-25 20:02:46.720 32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 9
05-25 20:02:46.720 32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 8
05-25 20:02:46.720 32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 7
05-25 20:02:46.720 32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 6
05-25 20:02:46.720 32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 5
05-25 20:02:46.720 32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 4
05-25 20:02:46.720 32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 3
05-25 20:02:46.720 32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 2
05-25 20:02:46.720 32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 1
之前的JNI例子只是演示用的,開發者應該使用Arrays.sort()或Collections.sort()來進行排序。通常不需要在本地進行排序,因為Java實現已經夠快了。
主布局 Popup對話框布局 package com.example.popupwindow; import
前幾天做的一個仿To圈個人資料界面的實現效果下面是To圈的效果Gif圖:做這個東西其實也花了一下午的時間,一開始思路一直沒理清楚,就開始盲目的去做,結果反而事倍功半。以後
前面在學習鴻洋大神的一些自定義的View文章,看到了自定義ViewGroup實現浮動標簽,初步看了下他的思路以及結合自己的思路完成了自己的浮動標簽的自定義ViewGrou
一.相關概念一個對話框一般是一個出現在當前Activity之上的一個小窗口. 處於下面的Activity失去焦點, 對話框接受所有的用戶交互. 對話框一般用於提示信息和與