編輯:關於Android編程
JNI是Java Native Interface的縮寫,中文為JAVA本地調用。從Java1.1開始,Java Native Interface(JNI)標准成為java平台的一部分,它允許Java代碼和其他語言寫的代碼進行交互。JNI一開始是為了本地已編譯語言,尤其是C和C++而設計的,但是它並不妨礙你使用其他語言,只要調用約定受支持就可以了。
a、可以使用JNI來實現“本地方法”(native methods),並在JAVA程序中調用它們,一般是在java中調用C的函數;相反的也可以用C來調用Java中的方法,這樣可以復用很多以前寫過的代碼。
b、JNI支持一個“調用接口”(invocation interface),它允許你把一個JVM嵌入到本地程序中。本地程序可以鏈接一個實現了JVM的本地庫,然後使用“調用接口”執行JAVA語言編寫的軟件模塊。
a、眾所周知的Java的可移植性在使用JNI之後可能會被破壞,程序不再跨平台,原因很簡單,一個操作系統上的本地方法很可能不能在另一個平台上正常運行,那麼導致使用了這些本地方法的Java代碼也同樣無法在別的平台上運行。
b、程序不再是絕對安全的,本地代碼的不當使用可能導致整個程序崩潰。
一個比較好的做法是,讓調用的本地方法只集中在你寫的工程的少數幾個類中,這樣可以降低Java和C代碼的耦合性,也提高了代碼的可維護性。
好吧,又是HelloWorld!
這裡我們以Android工程為例。
在真正開始做之前,還是先來了解一下基本流程:
public native void helloworld();
這裡我默認已經搭好了環境,如果沒有可以參看:http://www.cnblogs.com/baronzhao/archive/2012/07/10/2585181.html
打開cygwin,我們首先進入到對應工程的src目錄下,在我這裡是使用的cygwin打開的目 錄:/cygdrive/d/Android_Workspace/JNI_day19/Exercise/src
然後執行:javah -jni com.example.exercise.MainActivity,其中com.example.exercise.MainActivity為MainActivity.java的全類名
正常的話就會在src目錄下生成一個.h的頭文件:com_example_exercise_MainActivity.h,其內容為:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include/* Header for class com_example_exercise_MainActivity */ #ifndef _Included_com_example_exercise_MainActivity #define _Included_com_example_exercise_MainActivity #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_exercise_MainActivity * Method: getStr * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_exercise_MainActivity_getStr( JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
實際上上面那些endif之類的宏定義都不是必須的,關鍵的部分只有一個include
其中,jni.h是jni定義的對應java中的類型和方法的c中的對應類型和函數的頭文件,這個是必須有的。
其次,JNIEXPORT jstring JNICALL Java_com_example_exercise_MainActivity_getStr(JNIEnv *, jobject); 這一句除了JNIEXPORT可以去掉之外,其他的必須一致保留,大概寫法就是:返回類型(這裡是void) java_全類名(.換成_)_方法名(JNIEnv* env,jobject ojb);
這裡有一個小細節就是,有時候eclipse會在#include
點擊完之後會彈出一個對話框:
這裡我們可以直接寫入我們之後想生成的so文件名,這個文件名跟.c文件關聯,這裡我們選擇hello,點擊Finish之後,我們發現eclipse自動在jni文件夾中生成了兩個新的文件:Android.mk和hello.cpp<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPs7EvP48L3A+CjxwPsbk1tBBbmRyb2lkLm1rzsS8/s6qo7o8L3A+CjxwPiA8L3A+CjxwcmUgY2xhc3M9"brush:java;">LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#對應的是打包成函數庫的名字
LOCAL_MODULE := hello
#src目錄,對應的是c代碼的文件
LOCAL_SRC_FILES := hello.cpp
include $(BUILD_SHARED_LIBRARY) 這個文件的作用是指定生成.so文件的規則。 而hello.cpp文件則是我們需要實際寫c代碼的文件,在這裡面,我們將實現剛才頭文件中的jstring
JNICALL Java_com_example_exercise_MainActivity_getStr(JNIEnv *, jobject);這個函數。 實際上這裡要做的也很簡單,我們的目的就是想在這個函數裡返回一個字符串:"hello from C!",實際上這裡就需要用到jni.h中定義的一個方法了:jstring
(*NewStringUTF)(JNIEnv*, const char*);它的作用就是返回一個jstring字符串 實際上這個文件會出現在工程文件夾的:obj/local/armeabi/文件夾下。 至此,函數庫就已經生成好了,接下來的工作就是調用這個函數 我們可以再MainActiviy.java文件下,定義一個Button,對應點擊事件cilck,每當點擊的時候,就調用這個方法,將其返回的字符串"hello
from c!"以Toast的形式打印出來,在這之前需要注意的是,我們需要先加載剛才生成的.so庫文件,這裡使用一個static塊來加載,System.loadLibrary("hello");,注意這裡我們不需要寫libhello.so,而只需要寫hello即可: 效果:
#include
4、之後我們需要做的就是生成.so庫文件,在系統環境變量配置好了的情況下,直接打開cmd,進入到對應的工程目錄下,執行ndk-build即可生成對應的庫文件:libhello.so。
5、在MainActiviy.java中調用這個.so文件。
public class MainActivity extends Activity {
public native String helloWorld();
static {
System.loadLibrary("hello");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View view) {
String helloWorld = helloWorld();
System.out.println(helloWorld);
Toast.makeText(getApplicationContext(), helloWorld, Toast.LENGTH_LONG)
.show();
}
}
前面幾篇博文介紹了Android如何自定義控件,其實就是講一下如何“從無到有”的自定義一個全新的控件,繼承View或者繼承ViewG
每當viewpager上一個可見或依附的頁面發生了滾動事件就會調用PageTransformer,這讓應用可以使用自定義transformation讓viewpager某
Android自帶的對話框標題不好看,如果我們需要給彈出的對話框設置一個自己定義的標題,可以使用AlertDialog.Builder的setCustomTitle()方
Android手勢密碼LockPatternView、LockPasswordUtils、LockPatternUtils在使用別人寫的這個手勢密碼的時候,我們通常是有自