上一篇文章主要講解有關JNI和NDK開發過程中需要理解的基本的知識,想要了解的,請移步https://blog.csdn.net/qq_36451275/article/details/95347000
開發:
一、JNI原生開發流程(Eclipse)
(1)基本流程
1.在java類中定義native方法
2.生成包含對應JNI函數聲明的頭文件
3.實現生成的JNI的函數
4.藉助NDK編譯生成動態鏈接庫文件(.so)
5. 在Java類中加載動態連接庫並調用native的方法
6. 運行安裝到ARM模擬器上
(2)詳細流程
1. 在Java類中定義native方法
在Activity類中聲明: public native String helloJNI();
2. 生成包含對應JNI函數聲明的頭文件
1). 在命令行窗口中執行: javah Activity全類名(在src下執行)
說明有的電腦可能會提示GBK的不可映射字符: 添加 -encoding utf-8
2). 在src下會生成一個頭文件: com_atguigu_hellojni_MainActivity.h
3). 頭文件中包含一個native方法對應的JNI函數聲明(需要後面實現):
JNIEXPORT jstring JNICALL Java_com_atguigu_hellojni_MainActivity_helloJNI(JNIEnv *, jobject)
3. 實現生成的JNI函數
1). 在應用下創建一個文件夾: jni
2). 將剛纔生成的頭文件複製到此文件夾下
3). 創建一個c文件來實現生成的JNI函數聲明: test.c
#include "com_atguigu_hellojni_MainActivity.h"
JNIEXPORT jstring JNICALL Java_com_atguigu_hellojni_MainActivity_helloJNI
(JNIEnv * env, jobject jobj) {
return (*env)->NewStringUTF(env, "Hello from C");
}
4. 藉助NDK編譯生成動態鏈接庫文件(.so)
1). 解壓NDK包, 配置NDK文件夾到path(不能包含空格)
2). 藉助NDK下的文檔(ANDROID-MK.html)編寫用於編譯的文件(jni/Android.mk)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni #指定生成的so文件的唯一標識
LOCAL_SRC_FILES := test.c #指定包含JNI函數的c文件名
include $(BUILD_SHARED_LIBRARY)
3). 在命令行窗口中進入應用根目錄, 執行ndk-build命令, 生成so動態鏈接庫文件
so文件路徑: /libs/armeabi/libhello-jni.so
5. 在Java類中加載動態連接庫並調用native的方法
1). 在靜態代碼塊中加載so文件:
static {
System.loadLibrary("hello-jni");
}
2). 調用native方法:
String result = helloJNI();
6. 運行安裝到ARM模擬器上
(3)補充說明
JNIEXPORT :
在Jni編程中所有本地語言實現Jni接口的方法前面都有一個"JNIEXPORT",這個可以看做是Jni的一個標誌,至今爲止沒發現它有什麼特殊的用處。
JNICALL :
這個可以理解爲Jni 和Call兩個部分,和起來的意思就是 Jni調用XXX(後面的XXX就是JAVA的方法名)。
二、NDK集成開發流程(Eclipse)
(1)基本流程
1. 安裝配置NDK
2. 將NDK關聯到eclipse
3. 爲當前應用自動生成c文件和mk文件
4. 定義naitve方法
5. 實現native方法對應的JNI函數
6. 一錘編譯生成so文件
7. 加載動態庫, 並調用native方法
8. 運行應用安裝到ARM模擬器上
(2)詳細流程
1. 安裝配置NDK
1). 解壓NDK的zip包到非中文目錄
2). 配置path : 解壓後NDK的根目錄----->ndk-build
2. 將NDK關聯到eclipse
Window-->Preferences-->Android-->NDK-->配置NDK的根目錄
3. 爲當前應用自動生成c文件和mk文件
1). 選中當前應用右鍵-->Android Tools-->Add Native Support
2). 將生成的.cpp文件改爲.c文件
3). 修改mk文件: 將.cpp改爲.c
4. 配置關聯jni.h (見"配置關聯jni.h文件"截圖)
1). 選中當前應用右鍵-->Properties-->C/C++ General-->Paths and Symbols
2). 選擇add-->選擇File System-->選擇文件夾android-ndk-r9\platforms\android-18\arch-arm\usr\include
3). 點擊ok-->點擊apply
5. 定義naitve方法
public native String helloNDK();
6. 實現native方法對應的JNI函數
1). 使用javah命令, 生成JNI頭文件, 將其複製到jni文件下
2). 在c文件中實現h文件中的函數聲明
3). 利用* 的NewStringUTF()函數, 返回字符串
7. 一錘編譯生成so文件
1). 使用工具欄中的"Build"工具點擊錘子按鈕生成
2). 文件路徑: /libs/armeabi/libNDKTest.so
8. 加載動態庫, 並調用native方法
static {
System.loadLibrary("NDKTest");
}
String result = helloNDK();
9. 運行應用安裝到ARM模擬器上
(3)補充說明
1.修改C函數, 不需要再單獨編譯生成so文件, 可直接運行安裝
2.APK只是將so打包了, 本質並不需要jni文件夾下的相關文件
3.多平臺交叉編譯:arm、intel、mips
1>. 參照NDK文檔: APPLICATION-MK.html
2>. 在jni文件夾下創建: Application.mk
APP_ABI := all
3>. 編譯時會生成多個so文件來支持不同類型的CPU
4>. 不同CPU的說明
armeabi: 是指的該so庫用於ARM的通用CPU,armeabi通用性強,但速度
armeabi-v7a: CPU支持硬件浮點運算, 能充分發揮v7a CPU的能力,
x86: 用於Intel的CPU
mips : 另一種CPU, 用得特別少
4.Android.mk詳細介紹
#必須定義好LOCAL_PATH變量。它用於在開發樹中查找源文件
#宏函數’my-dir’,由編譯系統提供,用於返回當前路徑(包含c/c++文件)
LOCAL_PATH := $(call my-dir)
#清除除LOCAL_PATH之外的LOCAL_XXX變量
#CLEAR_VARS由編譯系統提供
include $(CLEAR_VARS)
#以標識你在Android.mk文件中描述的每個模塊。名稱必須是唯一的
#編譯會產生的鏈接庫文件(lib+名稱+.so): libtest2-jni.so
LOCAL_MODULE := test2-jni
#指定將要編譯打包進模塊中的C或C++源代碼文件
#如果有多個, 以空格隔開
LOCAL_SRC_FILES := test2.c
#編譯生成動態庫(也就是.so文件)
include $(BUILD_SHARED_LIBRARY)
#編譯生成靜態庫(也就是.a文件)
#include $(BUILD_STATIC_LIBRARY)
(4)重要配置
1.編寫Android.mk
2.配置關聯jni.h文件
3.編寫Application.mk
三、NDK集成開發流程(Android Studio)
1. 安裝配置NDK
1). 解壓NDK的zip包到非中文目錄
2). 配置path : 解壓後NDK的根目錄----->ndk-build
2. 給AS配置關聯NDK
1). local.properties中添加配置
ndk.dir=G\:\\android-ndk-r10
2). gradle.properties中添加配置
android.useDeprecatedNdk=true
3. 編寫native方法:
public class JNIS {
public native String helloJNI();
}
4. 定義對應的JNI
1). 在main下創建jni文件夾
2). 生成native方法對應的JNI函數聲明頭文件: 命令窗口中, 進入java文件夾
執行命令: javah com.atguigu.jnitests2.JNIS
生成頭文件: com_atguigu_jnitests2_JNIS.h
函數聲明: JNIEXPORT jstring JNICALL Java_com_atguigu_jnitests2_JNIS_helloJNI(JNIEnv *, jobject);
3). 將生成的頭文件轉移到jni文件夾下
4). 在jni下定義對應的函數文件: test.c
#include "com_atguigu_jnitests2_JNIS.h"
JNIEXPORT jstring JNICALL Java_com_atguigu_jnitests2_JNIS_helloJNI
(JNIEnv * env, jobject jobj) {
return (*env)->NewStringUTF(env, "Hello from C");
}
5). 在jni文件夾下創建一個空的C文件: empty.c
說明: 這是AS的bug, 必須至少2個C文件才能通過編譯
5. 指定編譯的不同CPU
defaultConfig {
ndk{
moduleName "HelloJni" //so文件: lib+moduleName+.so
abiFilters "armeabi", "armeabi-v7a", "x86" //cpu的類型
}
}
6. 編譯生成不同平臺下的動態鏈接文件
1). 執行rebuild, 生成so文件
2). so文件目錄: build\intermediates\ndk\debug\lib\.....
7. 調用native方法:
1). 在native方法所在的類中加載so文件
static {
System.loadLibrary("HelloJni");
}
2). 在Activity中調用native方法:
String result = new JNIS().helloJNI();
Log.e("TAG", "result="+result);