一.安裝配置環境
1.安裝Android Studio,下載路徑https://developer.android.com/studio/index.html?hl=zh-cn。我下載的是Windows 64位集成Android SDK版本https://dl.google.com/dl/android/studio/install/2.3.2.0/android-studio-bundle-162.3934792-windows.exe?hl=zh-cn,不用單獨安裝Android SDK。因為公司支持訪問Google,所以下載很方便。
2.安裝完畢,下載CMake,LLDB和NDK工具
3.如果要使用Git版本控制,需要單獨下載安裝包,官網地址https://git-scm.com/,安裝好后在Android Studio里設置路徑
然后可以指定下載路徑獲取代碼,比如kotlin的代碼倉庫
二.分析新建NDK工程
首先創建一個新工程,選擇Include C++Support
打開工程,里面有一個C++輸出字符串的例子
路徑在D:\Project\MyApplication\app\src\main\cpp\native-lib.cpp,代碼如下
#include <jni.h> #include <string> extern "C" JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
調用的文件是MainActivity.java,路徑是D:\Project\MyApplication\app\src\main\java\com\example\myapplication\MainActivity.java,內容如下
package com.example.myapplication; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Example of a call to a native method TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(stringFromJNI()); } /** * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. */ public native String stringFromJNI(); }
按Shift+F9,調試運行,選擇真機
在手機端輸出如下
默認對這個C++代碼是用CMake編譯的,CMakeLists.txt的路徑在D:\Project\MyApplication\app\CMakeLists.txt,內容精簡如下:
cmake_minimum_required(VERSION 3.4.1) add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). src/main/cpp/native-lib.cpp ) find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log ) target_link_libraries( # Specifies the target library. native-lib # Links the target library to the log library # included in the NDK. ${log-lib} )
其中,#代表行注釋。大概意思是設置要編譯的庫名稱是native-lib,源代碼路徑是src/main/cpp/native-lib.cpp
在模塊的gradle文件(注意不是項目的gradle文件,前者的路徑是D:\Project\MyApplication\app\build.gradle,后者路徑是D:\Project\MyApplication\build.gradle)里默認配置使用cmake文件編譯
三.按照CMake+gradle的方式編寫NDK程序
接下來我嘗試自己模仿用NDK寫一個C++的例子
編寫一個包含native方法的java類,如TestNative.java,路徑在D:\Project\MyApplication\app\src\main\java\com\example\myapplication\TestNative.java
代碼如下:
package com.example.myapplication; public class TestNative { static { System.loadLibrary("NativeLib"); } public static native String GetStr(); }
其中,NativeLib指定的是這個類要加載的庫的名稱,稍后我會生成這個庫,方法一定要用native修飾,而且為了不定義對象,把這個方法聲明為static類型,這樣可以直接用TestNative.GetStr()這樣的方式訪問。
接下來需要生成對應的庫,首先要編寫C++文件,有兩種方式,一種是使用Javah,生成頭文件,對應頭文件件編寫源代碼;另一種是根據jni生成函數的格式手動寫一個cpp即可,不用頭文件,先在實現第一種:
輸入CMD使用控制台或者在Android Studio下方的Terminal終端,先cd找到當前TestNative.java文件的位置
D:\Project\MyApplication>cd D:\Project\MyApplication\app\src\main\java\com\example\myapplication D:\Project\MyApplication\app\src\main\java\com\example\myapplication>
然后輸入javac TestNative.java編譯生成class文件
D:\Project\MyApplication\app\src\main\java\com\example\myapplication>javac TestNative.java D:\Project\MyApplication\app\src\main\java\com\example\myapplication>
在D:\Project\MyApplication\app\src\main\java\com\example\myapplication下會生成一個TestNative.class文件
然后cd到最上層包的路徑上,這點非常重要,是我反復嘗試帶包名的類執行java或javah的方法
執行javah,參數是帶包名的類
D:\Project\MyApplication\app\src\main\java\com\example\myapplication>cd D:\Project\MyApplication\app\src\main\java D:\Project\MyApplication\app\src\main\java>javah com.example.myapplication.TestNative D:\Project\MyApplication\app\src\main\java>
會在D:\Project\MyApplication\app\src\main\java路徑下生成com_example_myapplication_TestNative.h文件,內容如下
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_myapplication_TestNative */ #ifndef _Included_com_example_myapplication_TestNative #define _Included_com_example_myapplication_TestNative #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_myapplication_TestNative * Method: GetStr * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_myapplication_TestNative_GetStr (JNIEnv *, jclass); #ifdef __cplusplus } #endif #endif
按照這個聲明編寫一個cpp,如如TestNative.cpp,路徑是在D:\Project\MyApplication\app\src\main\cpp\TestNative.cpp,包含該頭文件,實現即可
#include <string>
#include "../java/com_example_myapplication_TestNative.h"
extern "C" {
JNIEXPORT jstring JNICALL Java_com_example_myapplication_TestNative_GetStr
(JNIEnv * env, jclass) {
std::string str = "My First Native Test";
return env->NewStringUTF(str.c_str());
}
}
第二種就是根據這樣的聲明特征,直接寫一個TestNative.cpp,注意函數命名方式一定要和用Javah生成的頭文件的方法一致,仔細觀察記住就行了。這樣不用使用javac和javah手動生成class文件和頭文件,但要包含jni.h頭文件,代碼如下
#include <string>
#include <jni.h>
extern "C" {
JNIEXPORT jstring JNICALL Java_com_example_myapplication_TestNative_GetStr
(JNIEnv * env, jclass) {
std::string str = "My First Native Test";
return env->NewStringUTF(str.c_str());
}
}
然后修改CMakeLists.txt文件,如下
cmake_minimum_required(VERSION 3.4.1) add_library( NativeLib SHARED src/main/cpp/TestNative.cpp ) find_library( log-lib log ) target_link_libraries( NativeLib ${log-lib} )
修改調用JNI的文件,如下
package com.example.myapplication; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Example of a call to a native method TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(TestNative.GetStr()); } }
運行程序,在手機輸出
用CMake方式,會在路徑D:\Project\MyApplication\app\build\intermediates\cmake\debug\obj下生成各種平台版本的so文件,如armeabi中的libNativeLib.so,注意生成庫的名稱是在設定庫名稱前加了lib
三.按照ndk-build+gradle的方式編寫NDK程序
按照ndk-build的方式編譯,需要生成.mk文件
在之前建立工程的基礎上
1.首先,要在D:\Project\MyApplication\app\src\main下新建jni文件夾,在里面新建Android.mk文件,內容如下
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE = NativeLib LOCAL_SRC_FILES := ../cpp/TestNative.cpp LOCAL_LDLIBS := -landroid -llog -latomic include $(BUILD_SHARED_LIBRARY)
大體意思是指定目標庫的名稱,源代碼的路徑,可以對比CMakeLists.txt。
2.接在,在Android.mk的同級目錄下,建立Application.mk文件,內容如下
APP_STL := c++_static
即說明使用C++的版本,具體可以參考https://developer.android.com/ndk/guides/cpp-support.html#runtimes
注意在使用C++時要新建Application.mk文件,寫上C++版本,直接寫在Android.mk不行,否則會提示STL的庫找不到
3.修改gradle的編譯方式為ndkBuild,內容如下
用ndk-build的方式,會在路徑D:\Project\MyApplication\app\build\intermediates\ndkBuild\debug\obj\local\下生成各種平台版本的so文件
四.設置指定平台版本的庫
1.清理掉D:\Project\MyApplication\app\build下的文件夾
2.在模塊的build.gradle文件中添加如下:
就只會生成libNativeLib.so了。