開發環境:windows2007, eclipsehtml
作anroid越深發現用到底層開發的時候越多,可是我之前也沒有搞過,所以如今打算好好學習學習。先從最簡單的作起。正所謂萬事開頭難啊。java
搞了近一天終於把在windows下,用eclipse開發Android JNI給倒騰通了。下面將詳細講解其操做步驟和我在其中遇到的問題android
參考:http://www.cnblogs.com/bastard/archive/2012/05/19/2508913.htmlc++
http://blog.csdn.net/cghs123/article/details/7044826web
1.新建Android工程 JNITestwindows
在HelloWorld的Activity中添加加載c庫的代碼,系統運行後自動執行該段app
static { //加載庫文件 System.loadLibrary("HelloWorldJni"); }
聲明c庫中須要自定義的原生函數框架
private native String printJNI(String inputStr);
HelloWorld整段代碼以下:eclipse
1 package com.sirc.jni; 2 3 import android.os.Bundle; 4 import android.app.Activity; 5 import android.util.Log; 6 import android.view.Menu; 7 8 public class HelloWorld extends Activity { 9 10 @Override 11 protected void onCreate(Bundle savedInstanceState) { 12 super.onCreate(savedInstanceState); 13 setContentView(R.layout.activity_main); 14 15 String jniStr=printJNI("I am HelloWorld Activity"); 16 Log.v("android", jniStr); 17 18 } 19 static 20 { 21 //加載庫文件 22 System.loadLibrary("HelloWorldJni"); 23 } 24 //聲明原生函數 參數爲String類型 返回類型爲String 25 private native String printJNI(String inputStr); 26 27 28 @Override 29 public boolean onCreateOptionsMenu(Menu menu) { 30 // Inflate the menu; this adds items to the action bar if it is present. 31 getMenuInflater().inflate(R.menu.main, menu); 32 return true; 33 } 34 35 }
2.生成共享庫的頭文件ide
詳細步驟見上一文章《Android JNI開發生成.h頭文件問題》http://www.cnblogs.com/gisdream/p/3521090.html
咱們都知道在Eclipse中新建類後,eclipse會幫助咱們在項目下自動創建去對應的class文件,位於項目目錄下的\bin\classes中。共享庫的頭文件能夠根據class文件經過javah編譯生成。
經過命令進入項目根目錄
再輸入命令:javah -classpath bin\classes -d jni com.sirc.jni.HelloWorld
能夠獲得頭文件com_sirc_jni_HelloWorld.h,位於跟目錄/jni/下面
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_sirc_jni_HelloWorld */ #ifndef _Included_com_sirc_jni_HelloWorld #define _Included_com_sirc_jni_HelloWorld #ifdef __cplusplus extern "C" { #endif /* * Class: com_sirc_jni_HelloWorld * Method: printJNI * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_sirc_jni_HelloWorld_printJNI (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif
頭文件中定義了與上層的訪問接口Java_com_sirc_jni_HelloWorld_printJNI,方法名是按照」Java_包名_類名_方法名「來命名的。
3.實現JNI原生函數源文件
在jni文件夾下面新建com_sirc_jni_HelloWorld.c文件,文件代碼以下:
#include <jni.h> #define LOG_TAG "HelloWorld" #include <android/log.h> #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args) #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args) /* Native interface, it will be call in java code */ JNIEXPORT jstring JNICALL Java_com_sirc_jni_HelloWorld_printJNI(JNIEnv *env, jobject obj, jstring inputStr) { LOGI("dufresne Hello World From libhelloworld.so!"); // 從 instring 字符串取得指向字符串 UTF 編碼的指針 const char *str = (const char *) (*env)->GetStringUTFChars(env, inputStr, JNI_FALSE); LOGI("dufresne--->%s", (const char *)str); // 通知虛擬機本地代碼再也不須要經過 str 訪問 Java 字符串。 (*env)->ReleaseStringUTFChars(env, inputStr, (const char *) str); return (*env)->NewStringUTF(env, "Hello World! I am Native interface"); } /* This function will be call when the library first be load. * You can do some init in the libray. return which version jni it support. */ jint JNI_OnLoad(JavaVM* vm, void* reserved) { void *venv; LOGI("dufresne----->JNI_OnLoad!"); if ((*vm)->GetEnv(vm, (void**) &venv, JNI_VERSION_1_4) != JNI_OK) { LOGE("dufresne--->ERROR: GetEnv failed"); return -1; } return JNI_VERSION_1_4; }
其中#include <android/log.h>應用了打印日誌頭文件,並聲明瞭集中打印方法,包括info,debug,error。我想這應該是調用的android的內部系統文件,在這裏對其進行了一個重聲明
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args)
JNI_OnLoad函數JNI規範定義的,當共享庫第一次被加載的時候會被回調。
Java_com_sirc_jni_HelloWorld_printJNI是頭文件中方法的實現,能夠經過這裏獲取上層傳過來的參數,並在這裏返回數據給上層框架使用。
注:這段代碼是從其餘網站上copy過來的,運行時若是出現錯誤stray '/241' in program ,該錯誤是指源程序中有非法字符,須要去掉非法字符。
解決方法:來源於http://blog.sina.com.cn/s/blog_6bc5571a0100zmft.html
(1)把所粘的文字放到記事本里就好了
(2)把出錯行的空格刪掉從新打一下試試。
4.編譯生成so庫
4.1在jni文件夾下面新建Android.mk文件,文件內容以下:
LOCAL_PATH:= $(call my-dir) # 一個完整模塊編譯 include $(CLEAR_VARS) LOCAL_SRC_FILES:=com_sirc_jni_HelloWorld.c LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) LOCAL_MODULE := libHelloWorldJni LOCAL_SHARED_LIBRARIES := libutils LOCAL_PRELINK_MODULE := false LOCAL_MODULE_TAGS :=optional LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog include $(BUILD_SHARED_LIBRARY)
系統變量解析:
LOCAL_PATH - 編譯時的目錄
$(call 目錄,目錄….) 目錄引入操做符
如該目錄下有個文件夾名稱 src,則能夠這樣寫 $(call src),那麼就會獲得 src 目錄的完整路徑
include $(CLEAR_VARS) -清除以前的一些系統變量
LOCAL_MODULE - 編譯生成的目標對象
LOCAL_SRC_FILES - 編譯的源文件
LOCAL_C_INCLUDES - 須要包含的頭文件目錄
LOCAL_SHARED_LIBRARIES - 連接時須要的外部庫
LOCAL_PRELINK_MODULE - 是否須要prelink處理
include$(BUILD_SHARED_LIBRARY) - 指明要編譯成動態庫
android.mk編譯模塊添加具體方法參考:http://blog.csdn.net/yili_xie/article/details/4906865
4.2編譯
我這裏使用NDK來進行編譯,首先要下載安裝NDK。
Android NDK安裝很簡單,直接到http://developer.android.com/tools/sdk/ndk/index.html 下載android-ndk-r8e-windows-x86.zip 解壓便可,接着設置環境變量。右擊個人電腦屬性,切換到高級選項卡,單擊環境變量,在系統變量下單擊編輯在Path變量名下直接變量值;D:\Android\android-ndk-r8e-windows-x86\android-ndk-r8e\ ,也就是你的解壓路徑,其中有個封號與前面的變量值分割。單擊肯定NDK就安裝好了。
在命令行中使用ndk-build編譯,過程以下
編譯成功後會自動生成libs/armeabi/libHelloWorldJni.so文件。
5.運行測試
運行JNITest項目,在logcat中查看運行結果。能夠看到
上層打印狀況:
底層打印狀況:
以上即是一個完整的開發JNI的實例,下一篇將詳細研究參數傳值,java中基本類型和c++基本類型的關聯。