https://blog.csdn.net/Hi_Red_Beetle/article/details/78994767
https://blog.csdn.net/zeqiao/article/details/77893167
在AS中進行NDK開發之前,我們先來簡單的介紹幾個大家都容易搞懵的概念:
1. 到底什麼是JNI,什麼是NDK?
2. 何爲“交叉編譯”?
先看什麼是JNI?JNI的全稱就是Java Native Interface,即java本地開發接口。可能大家和我一樣,一聽到接口什麼的就犯懵:“我也知道這是java本地開發接口的意思,但它具體是個什麼意思我還是搞不明白。”其實JNI它就是一種協議,一說協議,那它就是對某種東西的一個規範和約束,說的好聽一點就是標準化。如果你想用我這個東西,那你必須要遵守我這邊的規範。像http協議一樣,http作爲超文本傳輸協議,它規範了我們上網時從客戶端到服務器端等一系列的運作流程。正因爲如此,我們才能暢通無阻的上網。那麼換做JNI也一樣,只不過JNI這個協議是用來溝通java代碼和外部的本地代碼(c/c++)。也就是說有了JNI這個協議,我們才能夠隨意的讓java代碼調用C/C++的代碼,同樣C/C++的代碼也可以調用java的代碼。如果沒有這個協議作爲支撐,那麼java和C/C++代碼想要相互調用是不可能的。下面通過兩個圖簡單看一下JNI協議在系統架構中處於什麼位置:
在上圖中,上層綠色的部分一般都是用Java代碼寫的,下層橘黃色的部分一般都是用C/C++代碼寫的。可以看出,正式由於有了中間JNI的存在我們纔可以在Application層通過JNI調用下層中的一些東西。瞭解了JNI的概念後,我們再看看NDK,NDK(Native Development Kit)就比較好理解了,它就是一個本地開發的“工具包”。Java開發要用到JDK,Android開發要用到SDK,那我們在Android中要進行native開發,也要用到它對應的工具包,即NDK。通俗的來講,NDK就是幫助我們可以在Android應用中使用C/C++來完成特定功能的一套工具。 NDK的作用有很多,我們簡單的列舉兩個,比如:
1.首先NDK可以幫助開發者“快速”開發C(或C++)的動態庫。
2.其次,NDK集成了“交叉編譯器”。使用NDK,我們可以將要求高性能的應用邏輯使用C開發,從而提高應用程序的執行效率。
上面提到了“交叉編譯”,我們最後再解釋一下什麼是交叉編譯。大家都知道編譯器在將中間代碼連接成當前計算機可執行的二進制程序時,連接程序會根據當前計算機的CPU、操作系統的類型來轉換。而根據運行的設備的不同,CPU的架構也是不同,大體有如下三種常見的CUP架構:
- arm結構 :主要在移動手持、嵌入式設備上。我們的手機幾乎都是使用的這種CUP架構。
- x86結構 : 主要在臺式機、筆記本上使用。如Intel和AMD的CPU 。
- MIPS架構:多用在網關、貓、機頂盒等設備。
- ```
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <TextView
- android:id="@+id/textview"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Hello World!"
- />
- <Button
- android:id="@+id/button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="button"/>
- </LinearLayout>
- ```
- ```
- public class MainActivity extends AppCompatActivity {
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- final TextView textview = findViewById(R.id.textview);
- Button button = findViewById(R.id.button);
- button.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- textview.setText(JNIUtils.sayHelloFromJNI());
- }
- });
- }
- }
- ```
上面代碼中的JNIUtils.sayHelloFromeJNI()就是我們在與MainActivity相同的包中新建JNIUtils類後在裏面編寫的native方法。如下所示:
可以看到我們上面的sayHelloFromJNI()方法顯示的是警告紅色。把鼠標放到上面,它會提示我們對應的JNI頭文件沒有查找到。那麼接下來我們要做的就是去生成與這個sayHelloFromJNI()方法所對應的頭文件。
- ```
- /* DO NOT EDIT THIS FILE - it is machine generated */
- /* Header for class com_example_zhangxudong_jnidemo_JNIUtils */
- extern "C" {
- /*
- * Class: com_example_zhangxudong_jnidemo_JNIUtils
- * Method: sayHelloFromJNI
- * Signature: ()Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_com_example_zhangxudong_jnidemo_JNIUtils_sayHelloFromJNI
- (JNIEnv *, jclass);
- }
- ```
輸入要新建的C/C++文件名稱JNIHello,這裏我們用C++來編寫,所以Type爲.cpp,如果你選擇用C來編寫,那麼Type選爲.c,點擊ok。這裏說一下,在我們進行NDK開發的時候,選擇用C還是C++,在編寫代碼的時候除了C和C++基本的語法不同外,還是有許多不同地方需要注意。我們後續會慢慢介紹。這裏先默認跟着我的步驟來。
- JNIEXPORT jstring JNICALL Java_com_example_zhangxudong_jnidemo_JNIUtils_sayHelloFromJNI
- (JNIEnv *env, jclass jclass){
- return env->NewStringUTF("Hello World From JNI!!!!!");
- }
可以看到我們首先需要把原來生成的JNIUtlis對應的頭文件引入進來,下面的代碼基本都是從com_example_zhangxudong_jnidemo_JNIUtils.h中複製粘貼過來的一部分,然後稍加修改。修改的地方主要有sayHelloFromJNI的兩個參數和裏面的簡單實現,參數方面就是加了env和jclass兩個字段。函數裏面的實現呢,就是簡單的返回一個字符串“Hello World From JNI!!!!!”,至於爲什麼這麼寫,我會在下一篇文章進行講解,大家現在就需要知道如果要在這裏返回一個字符串就必須要通過env->NewStringUTF("xxxxxx");這行代碼。
- ```
- public class JNIUtils {
- static {
- System.loadLibrary("JNIHello");
- }
- public static native String sayHelloFromJNI();
- }
- ```
- ```
- Error:Execution failed for task ':app:compileDebugNdk'.
- > Error: Flag android.useDeprecatedNdk is no longer supported and will be removed in the next version of Android Studio. Please switch to a supported build system.
- Consider using CMake or ndk-build integration. For more information, go to:
- https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile
- To get started, you can use the sample ndk-build script the Android
- plugin generated for you at:
- E:\JNIDemo\app\build\intermediates\ndk\debug\Android.mk
- Alternatively, you can use the experimental plugin:
- https://developer.android.com/r/tools/experimental-plugin.html
- To continue using the deprecated NDK compile for another 60 days, set
- android.deprecatedNdkCompileLease=1515317190556 in gradle.properties
- ```
那我們生成的動態庫(.so文件)都在哪裏呢?點開app--->build--->intermediates--->ndk--->debug--->libs,可以看到各個平臺對應的動態庫都已經生成了。