Android JNI開發流程

很多人想學習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
轉載請註明出處!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章