在前面的學習中,咱們已經講解了關於NDK編程的環境搭建流程,簡單的使用咱們也經過官網自己自帶的例子進行說明了。但是相信你們必定還存在這麼的一個疑惑:「若是我要本身利用NDK編寫一個Android應用,具體應該怎麼作?有什麼要求」。OK,別擔憂,下面就讓咱們一塊兒來利用NDK來編寫一個簡單的Android應用。html
1) 經過如下命令建立一個新的Android Project (詳細的使用方法,你們能夠回去從新參考博文《Android學習次日-android經常使用命令》)java
android create project -n myfirstndk -t 1 -p ./myfirstndk -k cn.uc.myjni -a MainActivity
當Project建立成功後,咱們能夠經過查看文件夾發現它的大致架構以下圖:android
1) 進入該項目的MainActivity.java所在的目錄下,新建一個定義本地方法的類 NumberSum.java ,輸入如下代碼:編程
1 package cn.uc.myjni; 2 3 public class NumberSum { 4 5 // 聲明一個本地方法 6 public native int add (int a, int b); 7 8 // 加載名爲 libnumber_sum.so的庫 9 // 根據Unix的規則,系統爲自動爲number_sum 10 // 添加上lib前綴和.so後綴 11 static { 12 System.loadLibrary("number_sum"); 13 } 14 }
2) 調用如下命令,編譯NumberSum.javawindows
javac -encoding UTF-8 NumberSum.java
編譯成功後,控制檯並無特別的輸出,同時,咱們能夠在目錄下發現Number.class文件架構
這裏須要注意的是,個人命令中之因此指定源文件使用的編碼是由於我使用的是UTF-8編碼,在windows中直接經過javac NumberSum.java 進行編譯的話,會出現如下錯誤:app
3) 返回項目源代碼文件夾下,好比個人就是返回到src目錄下,經過javah命令,生成頭文件:ide
javah cn.uc.myjni.NumberSum
執行成功後,控制檯沒有輸出什麼特別的內容,同時,咱們能夠發如今src目錄下多了一個頭文件:函數
javah生成的頭文件知識講解:學習
① 從頭文件的名字咱們能夠看出,它是由編譯的調用本地方法的類名及其所在的包名經過下劃線"_"分割組成。如咱們的程序中是編譯cn.uc.myjni.NumberSum生成的,因此就算cn_uc_myjni_NumberSum
② 頭文件中的內容可能不少,可是咱們只關注這個方法的聲明:
JNIEXPORT jint JNICALL Java_cn_uc_myjni_NumberSum_add (JNIEnv *, jobject, jint, jint);
在這個聲明中,咱們能夠清晰的看到方法名的最後面add就是在NumberSum類中聲明的本地方法add,而前面的是 cn_uc_myjni_NumberSum就算完整的包名和類名,由此咱們能夠知道,一個完整的JNI函數名有3部分組成:Java、定義native方法的類的全名(包名+類名)、實際的函數名。這三部分用"_"進行鏈接。
4) 在Project的根目錄下,新建一個文件夾jni (必須叫作jni),將咱們以前生成的頭文件移動到jni文件夾下,如圖:
5) 新建一個c文件,名字爲 number_sum.c,輸入如下代碼:
1 #include <jni.h> 2 3 // 此處的方法簽名與頭文件中聲明的方法簽名是相同的 4 // 建議直接從頭文件中複製過來 5 JNIEXPORT jint JNICALL Java_cn_uc_myjni_NumberSum_add(JNIEnv *env,jobject obj,jint a,jint b) 6 { 7 return (a+b); 8 }
下面講解下上面的一些相關內容:
① JNIEXPORT jint JNICALL JNIEnv 等都是在jni.h中定義的;
② 其中上面參數中的env表示JNI的調用環境,obj表示定義native方法的Java類的對象自己。
6) 新建一個Android.mk 文件,建議直接從官方文檔給的例子 hello-jni 中複製出來進行修改LOCAL_MODUE 和 LOCAL_SRC_FILES,代碼以下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := number_sum LOCAL_SRC_FILES := number_sum.c include $(BUILD_SHARED_LIBRARY)
下面咱們簡單講解一下上面幾個參數的含義吧:
① LOCAL_PATH:Android.mk 的第一行必須是LOCAL。用於指定參與編譯的C/C++源文件的位置。在上面例子中,宏函數mydir 是由系統提供的,用來返回當前目錄的路徑。也就是包含Android.mk文件的目錄的路徑。
② include ${CLEAR_VARS}:CLEAR_VARS變量是在系統中定義的,用來指定一個特殊的GNU Make文件。該文件用來清空不少以LOCAL_開頭的變量,例如LOCAL_MODULE、LOCAL_SRC_FILES、LOCAL_STATIC_LIBRARIES 等。但這些變量不包括LOCAL_PATH。之因此要清空這些變量,是由於這些都是全局變啦ing。同時這些變量又要在不一樣的GNU Make文件中使用,爲了多個GNU Make文件不相互影響,就須要在執行每個GNU make文件(Android.mk文件)以前先清空這些變量。
③ LOCAL_MODULE := number_sum :在每個模塊中必須定義 LOCAL_MODULE變量,用來指定生成的模塊名。該變量的值必須是惟一的,並且不能包含任何空白分隔符。實際上,LOCAL_MODULE 變量的值就是生產共享庫的文件名(不包括lib和.so),在編譯時,系統會自動在文件名的先後添加上lib 和.so 。若是該模塊名前綴加了lib,在生產共享庫的時候不會進行添加。
④ LOCAL_SRC_FILES := number_sum.c:用來指定一個C/C++源文件列表,這裏不須要指定頭文件,系統會自動計算當前C/C++源文件 include的頭文件。系統就直接將LOCAL_SRC_FILES變量指定的源文件傳給編譯器。C++源文件的默認擴展名是.cpp,但能夠經過LOCAL_DEFAULT_CPP_EXTENSION 變量改變 C++文件默認拓展名,例如將該變量的值設成".cxx",注意不要忘記了" . "
⑤ include ${BUILD_SHARED_LIBRARY} : BUILD_SHARED_LIBRARY是在系統中定義的,用來指定一個GNU Make腳本文件。該腳本文件會根據以LOCAL_開頭的變量來生成共享庫文件。若是想生成靜態庫文件,可使用BUILD_STATIC_LIBRARY變量。
7) 爲了可以把咱們調用共享代碼庫執行的程序結果顯示出來,咱們對生成的代碼界面進行必定的修改:
I. 打開 res/layout/main.xml 文件,給TextView添加上一個Id,方便咱們在後臺經過id獲取組件進行顯示內容的修改:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" 4 android:layout_width="fill_parent" 5 android:layout_height="fill_parent" 6 > 7 <!-- 給TextView 添加了ID --> 8 <TextView 9 android:id="@+id/info_text" 10 android:layout_width="fill_parent" 11 android:layout_height="wrap_content" 12 android:text="Hello World, MainActivity" 13 /> 14 </LinearLayout>
II. 給咱們的主界面MainActivity.java的onCreate() 方法添加上將結果顯示到界面上的邏輯代碼:
1 package cn.uc.myjni; 2 3 import android.app.Activity; 4 import android.widget.TextView; 5 import android.os.Bundle; 6 7 public class MainActivity extends Activity 8 { 9 /** Called when the activity is first created. */ 10 @Override 11 public void onCreate(Bundle savedInstanceState) 12 { 13 super.onCreate(savedInstanceState); 14 setContentView(R.layout.main); 15 16 // 經過Id獲取TextView組件 17 TextView textView=(TextView) findViewById(R.id.info_text); 18 NumberSum sum = new NumberSum(); 19 // 調用本地方法 20 int result=sum.add(10, 20); 21 textView.append("\n 10+20="+result); 22 } 23 }
1) 打開cmd,將路徑轉移到項目的根目錄myfirstndk下,執行命令ndk-build (詳情你們能夠參考博文《Android學習——windows下搭建NDK_r9環境》的 第1節),執行後控制檯輸出以下圖,同時咱們能夠在\obj\local\armeabi路徑下查看到libnumber_sum.so共享庫:
1) 一樣在cmd中,直接執行命令 ant debug 進行打包:
打包成功後,咱們能夠在bin文件夾下看到相應的myfirstndk-debug.apk
2) 經過 emulator 命令打開你的模擬器(命令詳情能夠參考博文《Android學習第三天-打包經常使用命令》 第3節)
3) 經過 adb 命令將apk安裝到模擬器中(命令詳情能夠參考博文《Android學習第一天-adb經常使用命令》 第3節),如圖表示安裝成功。
4) 打開模擬器中的程序MainActivity 顯示如圖則表示安裝成功。