Android Studio下使用NDK的流程

jopen 8年前發布 | 14K 次閱讀 Android開發 移動開發

我要重新拿回持之以恒徽章!!

老規矩,先說看能學會什么:ANDROID STUDIO下NDK的使用方法。JNI的基本使用方法,C語言調用JAVA的方法。


首先要下載NDK,如果你沒有V*N可以來http://www.androiddevtools.cn/進行下載。下載后解壓到任意目錄。

其次,新建一個安卓項目。在MainActivity里添加一個Native方法。

public native void showDialog();

這里不以HelloWorld舉例了。來使用Java來調用C語言的方法,然后C語言的方法里再次調用java的方法來show一個dialog。這里在MainActivity寫show()方法

public void show(String message){
        AlertDialog.Builder bulider = new AlertDialog.Builder(this);
        bulider.setTitle("title");
        bulider.setMessage(message);
        bulider.show();
    }


寫完了之后 點擊Builde里面的 MakeProject


然后打開終端 進入app/src/main/java目錄,鍵入以下命令來編譯頭文件

cd app/src/main/java

javah -d ../jni com.wingsofts.jniii.MainActivity

之后再android studio下可以看到jni文件夾以及頭文件,這個頭文件的命名是 包名+類名


然后我們再來配置我們的gradle,編輯app目錄下的build.gradle文件,在defaultConfig函數內加入以下函數。

 ndk {
            moduleName "JniTest"
            abiFilters "armeabi", "armeabi-v7a", "x86"
        }

然后再編輯local.properties。加入你的NDK路徑(即剛解壓的ndk)

sdk.dir=/Users/wing/android-sdk
ndk.dir=/Users/wing/android-ndk-r10e

此時,ndk的配置以及完成。我們將要用C語言實現函數的內容。

在jni文件夾下新建一個C文件。這里我起名叫做Hello。

具體怎么實現呢 首先看一下我們編譯好的頭文件是什么內容

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_wingsofts_jniii_MainActivity */

#ifndef _Included_com_wingsofts_jniii_MainActivity
#define _Included_com_wingsofts_jniii_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_wingsofts_jniii_MainActivity
 * Method:    showDialog
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_wingsofts_jniii_MainActivity_showDialog
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

看到其中有一個函數 返回值為void 名稱叫做 Java_com_wingsofts_jniii_MainActivity_showDialog,哇塞,這個函數名有點長。仔細觀察,發現函數名是由 包名加類名加函數名命名這也就解釋了java和c方法是怎么對應的。然后看他的參數。(JNIEnv *,jobject),這是啥。。怎么看不懂呢。。 沒關系,我們再來看看他include的一個頭,jni.h

這里不全部貼出來了。只進行摘錄

typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
找到了以上語句。 原來這個JNIEnv 是一個JNINativieterface的指針,也就是說是一個環境。再來看

typedef void*           jobject;
typedef jobject         jclass;
typedef jobject         jstring;
typedef jobject         jarray;
typedef jarray          jobjectArray;
typedef jarray          jbooleanArray;
typedef jarray          jbyteArray;
typedef jarray          jcharArray;
typedef jarray          jshortArray;
typedef jarray          jintArray;
typedef jarray          jlongArray;
typedef jarray          jfloatArray;
typedef jarray          jdoubleArray;
typedef jobject         jthrowable;
typedef jobject         jweak;

因為java的數據類型和C不一樣,所以現在要用別名實現。這里是對應表。 可以看到這個函數第二個參數其實是jobject 也就是一個函數指針。

現在大概了解了。快來實現我的c函數吧。

#include "com_wingsofts_jniii_MainActivity.h"


JNIEXPORT void JNICALL Java_com_wingsofts_jniii_MainActivity_showDialog(JNIEnv* env,jobject jobj){
    jclass clazz = (*env)->FindClass(env,"com/wingsofts/jniii/MainActivity");
    jmethodID method = (*env)->GetMethodID(env,clazz,"show","(Ljava/lang/String;)V");
    (*env)->CallVoidMethod(env,jobj,method,(*env)->NewStringUTF(env,"c調用java"));




}
這里首先導入編譯好的頭文件。

然后實現方法名和頭文件里的一樣。 這個函數的目的是:調用java的方法,這里選擇show一個dialog。所以需要用c語言來調用showDialog方法。于是要用到反射。跟java內的反射一樣,先需要獲取class然后放到類加載器中 在獲得方法,才可以調用。

通過jni.h看到  jclass 就是java的 class    jmethodID就是java的Method 。

1.獲取class

首先使用(*env)->的 findClass方法 傳入環境變量 env 第二個參數是要尋找的類名  以路徑的形式寫。

2.獲取method

獲取到clazz之后 在獲取其中的方法  仍然是使用(*env)->的GetMethodID方法。傳入環境env Class clazz 然后是實例的方法名"show",最后一個是方法的簽名。看起來樣子很奇怪。其實也不奇怪,首先()里面的是參數的類型,因為java里傳入的是一個String 所以這里是(Ljava/lang/String;) 后面跟著是返回值,因為是VOID所以這里是V。如果你不確定你寫的方法對不對呢。還可以使用javap -s來查看方法的簽名。

3.調用方法

這里因為返回值為void所以使用CallVoidMethod 如果是返回obj就是CallObjectMethod, 這些都可以從jni.h看到。前三個參數不做解釋了,大家都明白。這里解釋一下最后一個參數。這個參數就是show(String message)的參數。一看是String的花 如果直接寫"c調用Java"是錯誤的。因為Java的字符串和C的字符串不是同一個類型。所以要調用NewStringUTF來轉換一下。

好了這時我們的C語言文件遍實現好了。

快來運行一下試試。得到效果圖


看起來也許不是很炫酷。但是想想這時由java調用C 在用C調用java來實現的。是不是立馬變得高大上了?







來自: http://blog.csdn.net/wingichoy/article/details/49337595

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!