win10 AndroidStudio 3.1.2 NDK 版本:R16java
和配置 Java JDK 環境變量相同,不會的能夠自行百度,配置 NDK 環境變量有不少種方式;android
即:在 Java 類中建立帶有 native 的方法;c++
項目或者應用的包名:com.ang.ndkdemomarkdown
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//建立的本地方法,具體功能在C或者C++中實現
public native String fromJNIString();
}
複製代碼
1,在電腦的 cmd 或者 AndroidStudio 的 Terminal 中輸入 javah -d D:\Demo\NDKDemo\app\src\main\jni -classpath D:\Demo\NDKDemo\app\src\main\java com.ang.ndkdemo.MainActivity數據結構
javah -d D:\Demo\NDKDemo\app\src\main\jni -classpath D:\Demo\NDKDemo\app\src\main\java
com.ang.ndkdemo.MainActivity
複製代碼
-classpath :類搜索路徑,這裏表示從當前的 D:\Demo\NDKDemo\app\src\main\java 目錄下查找架構
-d :將生成的頭文件放到當前的 jni 目錄下app
-o : 指定生成的頭文件名稱,默認以類全路徑名生成(包名 + 類名. h)ide
注意:-d 和 - o 只能使用其中一個參數。函數
注意: -d D:\Demo\NDKDemo\app\src\main\jni 和 -classpath D:\Demo\NDKDemo\app\src\main\java 位置能夠互換;一下寫法和等價於上面的寫法;工具
javah -classpath D:\Demo\NDKDemo\app\src\main\java -d D:\Demo\NDKDemo\app\src\main\jni com.ang.ndkdemo.MainActivity
複製代碼
補充:能夠經過 - o 指定生成的頭文件名稱,若是不指定,默認以類全路徑名生成(包名 + 類名. h)
javah -classpath E:\Demo\JNIDemo\app\src\main\java -o E:\Demo\JNIDemo\app\src\main\java\jni\JNITest.h com.ang.MainActivity
複製代碼
2,執行以上命令以後:就在項目的 main 文件夾下建立了 jni 文件夾,而且在 jni 文件夾下自動建立了. h 頭文件;頭文件名也是自動生成的,命名規則是 com_ang_ndkdemo_MainActivity.h(包名 + 類名. h)
3,自動生成的 com_ang_ndkdemo_MainActivity.h 頭文件代碼
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_ang_ndkdemo_MainActivity */
#ifndef _Included_com_ang_ndkdemo_MainActivity
#define _Included_com_ang_ndkdemo_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_ang_ndkdemo_MainActivity
* Method: fromJNIString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_ang_ndkdemo_MainActivity_fromJNIString
(JNIEnv *, jobject);
/*JNIEnv* 是定義任意native函數的第一個參數(包括調用JNI的RegisterNatives函數註冊的函數),指向JVM函數表的指針,函數表中的每個入口指向一個JNI函數,每一個函數用於訪問JVM中特定的數據結構。*/
#ifdef __cplusplus
}
#endif
#endif
複製代碼
4,生成. h 頭文件時候,若是出現 「找不到類文件」 的錯誤請參考 blog.csdn.net/ezconn/arti… 這篇文章
注意:
a. 包名或類名或方法名中含下劃線 _ 要用 _1 鏈接;
b. 重載的本地方法命名要用雙下劃線 __ 鏈接;
c. 參數簽名的斜槓 「/」 改成下劃線 「_」 鏈接,分號 「;」 改成 「_2」 鏈接,左方括號 「[」 改成 「_3」 鏈接;
另外,對於 Java 的 native 方法,static 和非 static 方法的區別在於第二個參數,static 的爲 jclass,非 static 的 爲 jobject;JNI 函數中是沒有修飾符的。
文件名能夠隨意寫,但須要注意文件類型;Hello.c 文件(.c 後綴的文件爲 C)表明內容是 C 代碼;Hello.cpp(.cpp 後綴的文件爲 C++)文件表明內容是 C++ 代碼;
C++ 代碼(注意 C 和 C++ 代碼是有區別),如下分別給出 C 和 C++ 兩種實現方式:
#include <jni.h>
#include "com_ang_ndkdemo_MainActivity.h"
JNIEXPORT jstring JNICALL
Java_com_ang_ndkdemo_MainActivity_fromJNIString(JNIEnv* env, jobject obj) {
return (*env)->NewStringUTF(env,"I am From Native C");
}
複製代碼
#include <com_ang_ndkdemo_MainActivity.h>
#include <stdio.h>
JNIEXPORT jstring JNICALL
Java_com_ang_ndkdemo_MainActivity_fromJNIString(JNIEnv *env, jobject obj)
{
return env->NewStringUTF("I am From Native C");
}
複製代碼
Java 的 native 方法是如何連接 C/C++ 中的函數的呢?能夠經過靜態和動態的方式註冊 JNI。 以上是經過靜態註冊的方式。
靜態註冊:根據函數名創建 Java 本地方法和 JNI 函數的一一對應關係。
動態註冊:直接告訴 Java native 方法其在 JNI 中對應函數的指針。
也能夠不配置 ndk{}, 這裏只是指定編譯出哪幾種對應的 abi 架構的. so 庫,若是不配置,會根據 ndk-build 默認輸出對應的 abi 架構的. so 庫;最好配置,否則不能編譯出本身想要的對應 ABI 架構的. so,若是本身的項目中已經引用其餘的. so 庫還要作適配;
defaultConfig {
applicationId "com.ang.demo"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//ndk編譯生成.so文件
ndk{
moduleName "Java2c" //生成的so名字,Android.mk文件中已經指定了,這裏能夠不寫
abiFilters "armeabi", "armeabi-v7a", "x86" //輸出指定三種abi體系結構下的so庫。
}
}
複製代碼
Android.mk 文件通常包含以下信息就夠了,差很少可算得上一個模板;根據本身的. so 庫名和 C 或者 C++ 文件名修改一下就能夠用了;
LOCAL_PATH:= $(call my-dir)#不用修改
include $(CLEAR_VARS)#不用修改
LOCAL_MODULE:= hello #動態庫名稱
LOCAL_SRC_FILES:= hello.c #C文件,裏面就是咱們寫的C代碼
include $(BUILD_SHARED_LIBRARY)#生成.so動態庫
#include $(BUILD_STATIC_LIBRARY) 編譯出.a的靜態庫
複製代碼
還有一種方式,就是讓 androidstudio 自動生成;以下是我獲取自動生成的 Android.mk 文件的方式:
a, 緊接着步驟六以後,點擊 Androidstudio 菜單欄 Build ->ReBuildProject
報錯:
b, 在 app ——> build ——>intermediater——>ndk(自動建立) 目錄下自動建立了一個 Android.mk 文件
Android.mk 文件以下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Java2c
LOCAL_LDFLAGS := -Wl,--build-id
LOCAL_SRC_FILES := \
D:\Demo\NDKDemo\app\src\main\jni\Hello.cpp \
LOCAL_C_INCLUDES += D:\Demo\NDKDemo\app\src\debug\jni
LOCAL_C_INCLUDES += D:\Demo\NDKDemo\app\src\main\jni
include $(BUILD_SHARED_LIBRARY)
複製代碼
鼠標右鍵項目,點擊 Link C++ Project with Gradle 修改 Androidstudio 默認編譯工具, 在 BuildSystem 欄選擇 ndk—build, 在 ProjectPath 選項欄,找到剛纔建立的 Android.mk 文件(其實就是 Android.mk 文件路徑),點擊 OK 以後就在 build.gradle(Model:App)的 android{} 中自動生成了 externalNativeBuild { ndkBuild { path 'src/main/jni/Android.mk'} }
//增長以後以下信息以後,右鍵項目的時候Link C++ Project with Gradle選項再也不顯示;
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
}
}
複製代碼
注意:有的時候須要再次顯示 Link C++ Project with Gradle 選項,刪掉 externalNativeBuild {ndkBuild { path 'src/main/jni/Android.mk'} } 點擊 sync now 同步一下;再次右鍵項目就能夠出現了;
**相關知識:**要將 Gradle 關聯到原生庫,須要提供一個指向 CMake 或 ndk-build 腳本文件的路徑。在構建應用時,Gradle 會以依賴項的形式運 CMake 或 ndk-build,並將共享的. so 庫打包到 APK 中。externalNativeBuild 就是配置的腳本路徑;
注意:加載生成的動態庫指定的文件名(System.loadLibrary("Java2c");)和生成. so 時指定的名字(buil.gradle 中的 ndk{moduleName "Java2c" }),還有 Android.mk 中 LOCAL_MODULE := Java2c 三者是否一致;
例如: 下圖加載生成的動態庫指定的文件名爲:Java2JNI 和上面生成. so 時指定的名字爲:Java2c 還有 Android.mk 中 LOCAL_MODULE := Java2c 三者不一致,就會出現 UnsatisfiedLinkError 異常;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(this,new Java2C().fromJNIString(),Toast.LENGTH_LONG).show();
}
//加載.so庫 Java2c爲庫名
static {
System.loadLibrary("Java2c");
}
public native String fromJNIString();
}
複製代碼
**總結:**以上不用編譯成. so 庫放到指定的路徑下;若是須要. so 庫(給其餘項目使用,例如使用百度地圖服務,就要使用其提供的. so 庫)以下手動編譯並使用. so 庫;
從步驟八開始的第二種方式,不指定 AndroidStudio 編譯工具(Cmake 或者 ndk-build),直接手動生成. so 庫
a, cmd 或者 Android studio 的 Terminal 中進入 jni 的上一級目錄
b, 輸入 ndk-build 命令,在 jni 同級的目錄中生成了一個 libs 文件夾,裏面生成了各個 cup 架構對應的. so 文件,
c, 應用. so 動態庫
// gradle高版本新寫法
sourceSets {
main {
jniLibs.srcDirs = ['src/main/libs']
}
}
複製代碼
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
複製代碼
d, 注意:這種手動生成. so 庫的方式,使用 ndk17 生成失敗,以上都是應用的 ndk16,因爲 ndk17 已不在支持 mips、armeabi CPU 架構,
使用 NDK 編譯代碼主要有三種方法:
1.基於 Make 的 ndk-build。
2.CMake。
3.獨立工具鏈,用於與其餘編譯系統集成,或與基於 configure 的項目搭配使用。
若是對您有所幫助的話
不妨加個關注,點個贊哈,您的每一個小小舉動都是對我莫大的支持!