關於
NDK
的使用,首先須要瞭解一個概念:
JNI
。什麼是
JNI
?
java
2.1 Hello-jni
android
這個是
NDK
自帶的例子程序,安裝官方網站的說明,一步步來,應該沒有什麼問題,這裏就不細說了。
express
2.2 My God I did it
apache
學習的第一步,就是模仿。咱們依照上面
Hello-jni
的例子,在建立本身的
NDK
程序。在此過程當中,對相關的內容和概念進行分析和說明。
app
首先,
less在工程的
src
夾子下用來放置
Java
文件。咱們打開
Eclipse
,而後新建一個
Android
工程,工程名就叫
MyJNI
,工程路徑選擇咱們建立的
NDK
的路徑。這裏須要注意的是,工程名,包名等,須要和上面的
c
文件中的保持一致。
(Java
_com_jpf_myjni _MyJNI _stringFromJNI)
ide
工程創建好後,編輯
src/com/jpf/myjni/MyJNI.java
文件,內容以下:
函數
package com.jpf.myjni;學習
import android.app.Activity;網站
import android.widget.TextView;
import android.os.Bundle;
public class MyJNI extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
TextView tv = new TextView( this );
tv.setText( stringFromJNI() );
System. out .println( "Here we go ..." );
setContentView(tv);
System. out .println( "Done!" );
}
public native String stringFromJNI();
static {
System.loadLibrary ( "myjni" );
}
}
須要說明的幾點:
public native String stringFromJNI(); 這句申明,帶有 native 關鍵字,說明該方法是本地方法。
System.loadLibrary ( "myjni" ); 這句就是用來加載咱們的 c 動態庫的。上面聲明的方法,具體實現,就在咱們加載的庫中。
其次,這裏纔是要學習的內容,建立本身的
NDK
工程。作eclips建立到project的目錄下建立jni文件夾,用於放.c文件。
|
這個程序,惟一和
hello-jni
不一樣的就是引用了
<android/log.h>
這個頭文件。在該頭文件中,聲明瞭函數
__android_log_print(),
能夠根據不一樣的
log
級別,輸出
log
,方便代碼的調試。在
NDK
中,
printf()
無法輸出,因此咱們須要藉助
log
庫來將咱們
c
代碼庫中須要輸出的內容,經過
java
控制檯輸出。調用函數
__android_log_print(),
就能夠在
Eclipse
中,查看
LogCat
來查看相關的輸出信息了。
注意:
在
c
文件中,函數名這樣定義:
Java_com_jpf_myjni_MyJNI_stringFromJNI
,有什麼講究麼?這個是
JNI
的標準,定義須要按照以下格式:
Java _packagename _classname _methodname ,
例如:
Java
_com_jpf_myjni _MyJNI _stringFromJNI
接着建立文件
jni/Android.mk.
這個文件是咱們本地
c
代碼的
Makefile
。文件內容以下:
|
分別對上述
Makefile
的語句進行說明。
LOCAL_PATH := $(call my-dir)
這句用來指定編譯的路徑。經過調用宏
my-dir
,獲取到當前工做的路徑。
include $(CLEAR_VARS) CLEAR_VARS
這個變量是編譯系統提供的,用來指明一個
GNU makefile
文件,添加這句,主要的目的是清理全部的
LOCAL_XXX.
,好比
LOCAL_MODULE
,
LOCAL_LDLIBS
。在每一個新模塊的開始處,須要添加這句。
LOCAL_MODULE := myjni
這句定義了模塊名稱,未來編譯的庫就以此命名。若果編譯的是動態庫,那麼庫名就是
libmyjni.so.
須要注意的是,若是你定義
module
爲
libmyjni
,那麼系統在生成動態庫的時候,就不要再爲你添加
lib
的前綴了,生成德動態庫名字仍是
libmyjni.so.
LOCAL_LDLIBS += -llog
這句指定了須要另外連接的庫。咱們在代碼中,用到了
log
庫,因此這裏加上這句。
include $(BUILD_SHARED_LIBRARY)
這句說明未來生產的庫是共享庫,及動態連接庫。若須要生產靜態庫,能夠這樣寫:
include $(BUILD_STATIC_LIBRARY)
。
若是沒有創建工程,
寫完了
c
文件和
Makefile
文件,是否能夠編譯了呢?咱們試一下。在
cygwin
中,進入工程目錄,運行
ndk-build
,獲得下面的結果:
|
看到這個錯誤的意思是,缺乏
manifest
文件。老版本的
NDk
,工程中有一個
apps
,裏面包含了應用的程序文件和
Application.mk
。如今的版本,不須要咱們本身編寫
Application.mk,
,不過仍須要工程相關的配置信息。那麼如何作到呢?須要手工去寫
manifest
文件麼?不須要。咱們只須要在
Eclipse
中,建立工程就能夠了,這些配置文件會自動生成。
創建好工程,再次編譯,在
cygwin
中運行
ndk-build
,結果
OK
。
|
咱們看到,須要的共享庫已經生成,而且安裝好了。下面就能夠生成
apk
了。
而後,就能夠運行了,用eclisps模擬器仍是生成APK文件,都是能夠的。
在jni目錄下(即hello-jni.c 同級目錄下)新建一個Android.mk文件,Android.mk 文件是Android 的 makefile文件,內容以下:
# Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS) LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello-jni.cinclude $(BUILD_SHARED_LIBRARY)
LOCAL_PATH := $(call my-dir)
一個Android.mk 文件首先必須定義好LOCAL_PATH變量。它用於在開發樹中查找源文件。在這個例子中,宏函數’my-dir’, 由編譯系統提供,用於返回當前路徑(即包含Android.mk file文件的目錄)。
include $( CLEAR_VARS)
CLEAR_VARS由編譯系統提供,指定讓GNU MAKEFILE爲你清除許多LOCAL_XXX變量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),
除LOCAL_PATH 。這是必要的,由於全部的編譯控制文件都在同一個GNU MAKE執行環境中,全部的變量都是全局的。
LOCAL_MODULE := hello-jni
編譯的目標對象,LOCAL_MODULE變量必須定義,以標識你在Android.mk文件中描述的每一個模塊。名稱必須是惟一的,並且不包含任何空格。
注意:編譯系統會自動產生合適的前綴和後綴,換句話說,一個被命名爲'hello-jni'的共享庫模塊,將會生成'libhello-jni.so'文件。
重要注意事項:
若是你把庫命名爲‘libhello-jni’,編譯系統將不會添加任何的lib前綴,也會生成 'libhello-jni.so',這是爲了支持來源於Android平臺的源代碼的Android.mk文件,若是你確實須要這麼作的話。
LOCAL_SRC_FILES := hello-jni.c
LOCAL_SRC_FILES變量必須包含將要編譯打包進模塊中的C或C++源代碼文件。注意,你不用在這裏列出頭文件和包含文件,由於編譯系統將會自動爲你找出依賴型的文件;僅僅列出直接傳遞給編譯器的源代碼文件就好。
注意,默認的C++源碼文件的擴展名是’.cpp’. 指定一個不一樣的擴展名也是可能的,只要定義LOCAL_DEFAULT_CPP_EXTENSION變量,不要忘記開始的小圓點(也就是’.cxx’,而不是’cxx’)
include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY表示編譯生成共享庫,是編譯系統提供的變量,指向一個GNU Makefile腳本,負責收集自從上次調用'include $(CLEAR_VARS)'以來,定義在LOCAL_XXX變量中的全部信息,而且決定編譯什麼,如何正確地去作。還有 BUILD_STATIC_LIBRARY變量表示生成靜態庫:lib$(LOCAL_MODULE).a, BUILD_EXECUTABLE 表示生成可執行文件。