很多人想學習JNI和NDK,但又不敢學習,覺得這一塊內容太難,其實難的不是JNI和NDK,而是C/C++語言,JNI和NDK只是個工具,很容易學習的。
學習JNI之前,首先得先知道JNI、NDK、Java和C/C++之間的關係。在Android開發中,有時爲了性能和安全性(反編譯),需要使用C/C++語言,但是Android APP層用的是Java語言,怎麼才能讓這兩種語言進行交流呢,因爲他們的編碼方式是不一樣的,這是就需要JNI了。JNI可以被看作是代理模式,Java使用JVM加載並調用JNI來間接調用C/C++代碼,也就是Java讓JNI代其與C/C++溝通。NDK呢其實主要就是用來將C/C++代碼打包編譯成.so庫的。
搭建NDK環境我就不講了,其實就是添加NDK的路徑而已,下面開始講解一個簡單的jni例子,從native層獲取字符串。
第一步,編寫native方法
比如在MainActivity中聲明如下:
public native String stringFromJNI();
第二步,根據此native方法編寫C文件
如果不知道jni中的函數聲明怎麼寫,可以利用javah工具幫我們
在終端中切換到java目錄下,然後執行javah命令
F:\Apps\jniDemo\JNIDemo>cd app/src/main/java/
F:\Apps\jniDemo\JNIDemo\app\src\main\java>javah com.lb6905.jnidemo.MainActivity
之後會在java目錄下自動生成一個com_lb6905_jnidemo_MainActivity.h文件,如下,stringFromJNI方法名會變成Java_com_lb6905_jnidemo_MainActivity_stringFromJNI。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_lb6905_jnidemo_MainActivity */
#ifndef _Included_com_lb6905_jnidemo_MainActivity
#define _Included_com_lb6905_jnidemo_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_lb6905_jnidemo_MainActivity
* Method: stringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_lb6905_jnidemo_MainActivity_stringFromJNI
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
這裏我們可以注意到jni的取名規則,一般都是包名 + 類名,jni方法只是在前面加上了Java_,並把包名和類名之間的.換成了_
有了這個.h文件文件後,在main目錄下新建jni文件夾,把此.h文件拷到jni目錄下,我重命名成了hello-jni.c文件(文件名可隨意取),看着方便。我們把Java_com_lb6905_jnidemo_MainActivity_stringFromJNI實現一下,如下,C/C++中是沒有Java的String類型的,所有我們需要把C/C++的UTF8編碼的字符串轉換成jstring,這時就需要用到jni的NewStringUTF方法了,至於jni的方法可以在jni.h文件(android-ndk-r13b\platforms\android-24\arch-arm\usr\include\jni.h)中查詢。之後JVM會將jstring轉成Java的String類型的。
JNIEXPORT jstring Java_com_lb6905_jnidemo_MainActivity_stringFromJNI( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI !");
}
第三步,使用NDK打包成.so庫
到這裏,jni文件已編寫完成,在Android中想使用native方法,需要通過.so庫來實現,所有我們需要將jni和C代碼打包成.so文件。
這裏需要兩步:
1,編寫Android.mk文件,此文件用來告知NDK打包.so庫的規則
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)
至於每個變量的定義方式可以參考谷歌文檔(https://developer.android.google.cn/ndk/guides/android_mk.html),那裏講的很清楚,還是中文的。
2,使用ndk-build打包.so庫
在終端中切換到jni目錄下,執行ndk-build命令,如下,我們可以看到這裏講所以平臺對應的.so庫都打包了,如果我們想只打包特定平臺的.so庫,還需要編寫Application.mk文件,詳細參考(https://developer.android.google.cn/ndk/guides/application_mk.html)
F:\Apps\jniDemo\JNIDemo>cd app/src/main/jni
F:\Apps\jniDemo\JNIDemo\app\src\main\jni>ndk-build
[arm64-v8a] Compile : hello-jni <= hello-jni.c
[arm64-v8a] SharedLibrary : libhello-jni.so
[arm64-v8a] Install : libhello-jni.so => libs/arm64-v8a/libhello-jni.so
[x86_64] Compile : hello-jni <= hello-jni.c
[x86_64] SharedLibrary : libhello-jni.so
[x86_64] Install : libhello-jni.so => libs/x86_64/libhello-jni.so
[mips64] Compile : hello-jni <= hello-jni.c
[mips64] SharedLibrary : libhello-jni.so
[mips64] Install : libhello-jni.so => libs/mips64/libhello-jni.so
[armeabi-v7a] Compile thumb : hello-jni <= hello-jni.c
[armeabi-v7a] SharedLibrary : libhello-jni.so
[armeabi-v7a] Install : libhello-jni.so => libs/armeabi-v7a/libhello-jni.so
[armeabi] Compile thumb : hello-jni <= hello-jni.c
[armeabi] SharedLibrary : libhello-jni.so
[armeabi] Install : libhello-jni.so => libs/armeabi/libhello-jni.so
[x86] Compile : hello-jni <= hello-jni.c
[x86] SharedLibrary : libhello-jni.so
[x86] Install : libhello-jni.so => libs/x86/libhello-jni.so
[mips] Compile : hello-jni <= hello-jni.c
[mips] SharedLibrary : libhello-jni.so
[mips] Install : libhello-jni.so => libs/mips/libhello-jni.so
第四步,在Android中使用.so庫
現在我們在main/libs目錄下會有所有平臺的.so文件,因爲Android Studio默認的.so庫目錄是src/main/jniLibs,所以在不更改.so文件目錄的情況下,只需要在build.gradle文件中指向main/libs即可,添加如下代碼。
android {
......
defaultConfig {
......
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = []
}
}
下面在MainActivity中加載.so庫,需要添加如下代碼
static {
System.loadLibrary("hello-jni");
}
現在我們可以調用stringFromJNI的native方法了,比如將返回值顯示到TextView上,使用如下代碼:
mTextView.setText(stringFromJNI());
此時運行程序即可正確顯示字符串,如下圖所示:
代碼地址(順手給個Star啊):點擊查看源碼
作者:lb377463323
出處:http://blog.csdn.net/lb377463323
原文鏈接:http://blog.csdn.net/lb377463323/article/details/75112049
轉載請註明出處!