Android Studio NDK基礎使用
NDK是什嗎?
Android平台是基於java實現,運行於虛擬機器Dalvik;故而使用Android SDK建立應用程式需要使用java語言來編寫實現。不過並不僅限於使用java,google在發布android之初就宣稱支援持JNI編程方式,也就是第三方應用完全可以通過JNI調用自己的C動態庫,即在Android平台上,“Java+C”的編程方式是一直都可以實現的。NDK即為了方便你建立應用時調用本地C/C++的一系列工具集合。
The NDK is a toolset that allows you to implement parts of your app using native-code languages such as C and C++. For certain types of apps, this can be helpful so you can reuse existing code libraries written in these languages, but most apps do not need the Android NDK.
官方NDK文檔:http://wear.techbrood.com/tools/sdk/ndk/
什麼時候使用NDK?
Before downloading the NDK, you should understand that the NDK will not benefit most apps. As a developer, you need to balance its benefits against its drawbacks. Notably, using native code on Android generally does not result in a noticable performance improvement, but it always increases your app complexity. In general, you should only use the NDK if it is essential to your app—never because you simply prefer to program in C/C++.
官方清楚的描述了大部分應用並不會因為使用NDK而獲得明顯的效能收益,反而有可能導致應用的複雜度增加,相容性變差等。Google建議在需要在處理需要大量計算/高CPU消耗的工作時使用NDK來提高運算處理效率,例如:遊戲引擎、訊號處理、影像處理等。
而實際使用NDK時往往還會考慮到:
1. 代碼的保護,由於apk的java層代碼很容易被反編譯,而C/C++庫反匯難度較大。
2. 為了方便地使用現存的開源庫,大部分現存的開源庫都是用C/C++代碼編寫的。
3. 跨平台考慮,用C/C++寫得庫可以方便在其他的嵌入式平台上再次使用。
AS 上使用NDK 樣本
使用NDK,當然要先下載最新NDK工具:http://developer.android.com/ndk/downloads/index.html
設定項目的NDK路徑:
查看local.properties是否定義好NDK路徑:
Project Gradle NDK配置
更改project的gradle.properties檔案,加入android.useDeprecatedNdk=true,使得項目支援NDK:
修改app下build.gradle的defaultConfig,配置native c/c++編譯本地so庫:
申明JNI函數,在java代碼內編寫需要實現JNI的native函數:
package com.example.jokerlee.myapplication;/** * Created by jokerlee on 16-3-31. */public class JniTest { //使用jni需要載入本地編譯出來的so庫,名稱即為上一步配置的ndk moduleName static { System.loadLibrary("jniTest"); } public static native String getStringFromJni();}
Rebuild Project之後NDK工具會在app的build/intermediates目錄下產生一個classses檔案夾:
開啟AS的terminal執行使用javah命令將java class聲明的JNI方法轉化成C\C++標頭檔
關於javah命令詳解可參考:http://blog.csdn.net/zzhays/article/details/10514767
在當前debug目錄產生com_example_jokerlee_myapplication_JniTest.h:<喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">/* DO NOT EDIT THIS FILE - it is machine generated */#include /* Header for class com_example_jokerlee_myapplication_JniTest */#ifndef _Included_com_example_jokerlee_myapplication_JniTest#define _Included_com_example_jokerlee_myapplication_JniTest#ifdef __cplusplusextern "C" {#endif/** Class: com_example_jokerlee_myapplication_JniTest* Method: getStringFromJni* Signature: ()Ljava/lang/String;*/JNIEXPORT jstring JNICALL Java_com_example_jokerlee_myapplication_JniTest_getStringFromJni(JNIEnv *, jclass);#ifdef __cplusplus}#endif#endif
然後就是根據該標頭檔建立c/cpp檔案TestJni.cpp實現標頭檔中的函式宣告,並將這些標頭檔和c/cpp檔案移動到main/src目錄下:
//// Created by jokerlee on 16-3-31.//#include "com_example_jokerlee_myapplication_JniTest.h"JNIEXPORT jstring JNICALL Java_com_example_jokerlee_myapplication_JniTest_getStringFromJni (JNIEnv * env, jclass ){ return env->NewStringUTF("這裡是來自c的string"); }
使用JNI函數
前面完成各種配置以及c/c++檔案的編寫之後,在應用內若需要實現JNI調用native函數,則需要先載入native code編譯出來的so庫:
//jniTest為庫的名稱,即為配置在app build.gradle defaultConfig的moduleNameSystem.loadLibrary("jniTest");
package com.example.jokerlee.myapplication;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d("test", JniTest.getStringFromJni() ); }}