Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲

關於JNI

編輯:關於Android編程

JNI

NDK開發環境的搭建

將NDK的路徑拷貝到環境變量path中 cmd中運行ndk-build可驗證是否添加成功

JNI_HelloWorld

步驟

1.創建Android工程 2.java代碼中聲明native方法 3.在工程根目錄下創建jni文件夾,編寫c代碼,名字要對應 4.編寫Android.mk文件 5.NDK編譯生成動態鏈接庫 6.java代碼load動態庫,調用native代碼

實現

通過一個點擊事件來調用C代碼 在Activity中添加方法public native String hello(); 在該項目的根目錄下添加文件夾 名為jni 在jni文件夾下添加文件 hello.c(命名與Activity中的native方法名一樣) hello.c文件中的代碼:
\ 在項目的根目錄下啟動cmd ndk-build,此時會出現錯誤Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: ./jni/Android.mk。缺少Android.mk文件 在jni目錄下創建Android.mk文件並添加

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE:= hello-jni//要生成的動態庫
LOCAL_SRC_FILES := hello-jni.c//源文件

include $(BUILD_SHARED_LIBRARY)

修改LOCAL_MODULE和LOCAL_SRC_FILES然後在 ndk-build 編譯

[armeabi] Compile thumb : helloJNI <= hello.c

[armeabi] SharedLibrary : libhelloJNI.so<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPlthcm1lYWJpXSBJbnN0YWxsOiBsaWJoZWxsb0pOSS5zbyA9Jmd0OyBsaWJzL2FybWVhYmkvbGliaGVsbG9KTkkuc288L3A+DQo8cD7J+rPJtq/MrL/iPC9wPg0KamF2YbT6wuu147v3ysK8/tbQIMztvNMgU3lzdGVtLmxvYWRMaWJyYXJ5KCZsZHF1bztoZWxsb0pOSSZyZHF1bzspOzxiciAvPg0Kv+K1xMP7s8bKxyBsaWJoZWxsb0pOSS5zbyC9q2xpYrrNLnNvyKW19CC+zb/J0tQNCjxoMSBpZD0="javah生成頭文件">Javah生成頭文件 如果將native方法名改為public native String hello_from_jni();那麼在c代碼中的方法名是這樣的:

jstring Java_com_example_jnihello_MainActivity_hello_from_jni(JNIEnv* env,jobject jobj)

ndk-build編譯也不會報錯,但是運行的時候會崩潰

No implementation found for native Lcom/example/jnihello/MainActivity;.hello_from_jni:()Ljava/lang/String;

在c代碼中將Java的全類名中的.變為_ ,原來有下劃線的地方需要在後面加個1,改成這樣的:

jstring Java_com_example_jnihello_MainActivity_hello_1from_1jni(JNIEnv* env,jobject jobj)

但是如果native方法中有很多下劃線和數字的話,這樣修改起來就會顯得很麻煩,需要一個便捷的方法來實現–javah

JDK1.6在工程的bin/classes目錄下使用 JDK1.7在工程的src目錄下使用

使用方法:javah+空格+java全類名

例如:native方法名為public native String hello_1_1_23_from_jni(); 在src目錄下啟動cmd,輸入Javah com.example.jnihello.MainActivity就行了。將生成的.h文件移動到jni目錄下。 在c代碼中原來的#include修改為#include "com_example_jnihello_MainActivity.h"並把c代碼中的方法名修改為com_example_jnihello_MainActivity.h中的方法名

常見錯誤

缺少Android.mk文件報錯 c文件沒有include導入jni.h的頭文件 方法的形參沒有指定名稱 沒有動態加載.so文件 加載.so時名字寫錯 不同cpu需要編譯不用的.so文件
針對不同cup編譯需要Application.mk文件(jni目錄下)

For example, to support hardware FPU instructions on ARMv7 based devices, use:

      APP_ABI := armeabi-v7a

Or to support ARMv8 AArch64 instruction set, use:

      APP_ABI := arm64-v8a

Or to support the IA-32 instruction set, use:

      APP_ABI := x86

Or to support the Intel64 instruction set (r1), use:

      APP_ABI := x86_64

Or to support the MIPS32 instruction set, use:

      APP_ABI := mips

Or to support the MIPS64 instruction set (r6), use:

      APP_ABI := mips64

Or to support all at the same time, use:

      APP_ABI := armeabi armeabi-v7a x86 mips arm64-v8a x86_64 mips64

Or even better, since NDK r7, you can also use the special value ‘all’ which means “all ABIs supported by this NDK release”:

      APP_ABI := all

Android.mk文件

#$:調用系統的工具鏈函數,當前的作用:調用當前目錄
LOCAL_PATH := $(call my-dir)
#清除LOCAL環境變量並且初始化工具鏈工具,但是不會清除LOCAL_PATH
include $(CLEAR_VARS)
#要生成的.so文件名稱,前面省略lib可以不寫,但是後綴不需要寫
LOCAL_MODULE    := hello-jni
#指向源文件,多個源文件用空格連接
LOCAL_SRC_FILES := hello-jni.c
#編譯成動態鏈接庫BUILD_SHARED_LIBRARY,後綴.so ,文件比較小
#BUILD_STATIC_LIBRARY編譯成靜態鏈接庫,後綴.a,文件比較大
include $(BUILD_SHARED_LIBRARY)

NDK簡便開發流程

Windows ->Preferences->NDK->添加NDK路徑

\

一開始我的Eclipse沒有NDK這個選項,百度之後解決。

新建Android Progect

右鍵Android Tools ->Add Native Support 填上庫的名字,這時候會生成jni文件夾,其中有Android.mk文件和一個cpp文件,關聯源碼
右鍵->Properties->C/C++ General->Paths and Sysbols->includes->add->File system->NDK安裝路徑->platforms->選擇版本->arm->include ->確定 在項目工程的src文件夾下 javah 生成頭文件,移動到jni目錄下 在c文件中導入這個頭文件並實現native方法

Java與C之間的數據傳遞

在C代碼中打印LOG信息,需要在C代碼中添加頭文件#include並使用宏定義#define LOGD(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
還需要在Android.mk文件中增加LOCAL_LDLIBS += -llog

在C代碼中直接使用定義好的LOGD,並將要打印的信息傳入即可。

將Java中的String類型轉換成C中的char*類型

char* jstringTostring(JNIEnv* env, jstring jstr) {
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("utf-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes",
        "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0) {
    rtn = (char*) malloc(alen + 1);
    memcpy(rtn, ba, alen);
    rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
return rtn;

JNI中對數組的操作

獲取數組的長度
int size = (*env)->GetArrayLength(env, jarray); 獲取數組中每個元素

jint* arrayElement = (*env)->GetIntArrayElements(env, jarray, JNI_FALSE);

for (i = 0; i < size;i++){
jint s = *(arrayElement + i);
}

C調用Java

反射(Reflection)

動態獲取類的信息以及動態調用對象的方法

Java反射機制主要提供的功能:

1.在運行時判斷任意一個對象所屬的類 2.在運行時構造任意一個類的對象 3.在運行是判斷任意一個類所具有的成員變量和方法 4.在運行是調用任意一個對象的方法

Java Reflection API簡介

Class類:代表一個類,位於java.lang包下。 Field類:代表類的成員變量(成員變量也稱為類的屬性)。 Method類:代表類的方法。 Constructor類:代表類的構造方法。 Array類:提供了動態創建數組,以及訪問數組的元素的靜態方法。

反射步驟:

1.得到字節碼文件 2.得到字節碼對應的方法 3.得到這個類的實例 4.執行方法

MainActivity.java

package com.example.ccalljavademo;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends Activity {

    private JNI jni;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        jni = new JNI();
    }

    public void helloFromJava(View v) {
        jni.cMethods_call_helloFromJava();
    }

    public void add(View v) {
        jni.cMethods_call_add();
    }

    public void printString(View v) {
        jni.cMethods_call_printString("sad");
    }

    public void callStatic(View cv) {
        jni.cMethods_call_sayHello();
    }
}
JNI.java
package com.example.ccalljavademo;

public class JNI {


    {
        System.loadLibrary("CCallJavaDemo");
    }
    /**
     * C調用Java空方法
     */
    public void helloFromJJava() {
        System.out.println("Hello Form Java");
    }

    /**
     * C調用Java兩個int參數方法
     * @param x
     * @param y
     * @return
     */
    public int add(int x, int y) {
        int result = x + y;
        System.out.println("被c調用" + result);
        return result;

    }

    /**
     * C調用Java參數為String方法
     * @param s
     */
    public void printString(String s) {
        System.out.println(s);
    }

    /**
     * 靜態方法
     */
    public static void sayHello (){
        System.out.println("Hello");
    }

    /**
     * 讓C語言調用JNI.java中的 helloFromJava
     */
    public native void cMethods_call_helloFromJava();

    /**
     * 調用JNI.java中的add方法
     */
    public native void cMethods_call_add();

    /**
     * 調用JNI.java中的printString方法
     * @param s
     */
    public native void cMethods_call_printString(String s);
    /**
     * 調用靜態方法
     */
    public native void cMethods_call_sayHello();
}
CCallJavaDemo.c
#include "com_example_ccalljavademo_JNI.h"
#include 
#include

/**
 * 讓c語言調用Java中的HelloFromJava
 */

JNIEXPORT void JNICALL Java_com_example_ccalljavademo_JNI_cMethods_1call_1helloFromJava(
        JNIEnv *env, jobject jobj) {
//  1.得到字節碼文件
//     jclass      (*FindClass)(JNIEnv*, const char*);
//  第二個參數 是全類名 com.example.ccalljavademo.JNI 將.換成/

    jclass jclazz = (*env)->FindClass(env, "com/example/ccalljavademo/JNI");

//  2.得到字節碼對應的方法
//      jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
//  第三個參數:方法名
//  第四個參數:方法簽名
    jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "helloFromJJava",
            "()V");
//  3.得到類的實例
//      jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject obj = (*env)->AllocObject(env, jclazz);
//  4.執行方法
//      void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env, obj, jmethodid); //成功調用JNI.java中的helloFromJava方法

}

/***
 *C調用Java中帶有兩個int參數的方法
 *
 */JNIEXPORT void JNICALL Java_com_example_ccalljavademo_JNI_cMethods_1call_1add(
        JNIEnv *env, jobject jobj) {
    //  1.得到字節碼文件
    //     jclass      (*FindClass)(JNIEnv*, const char*);
    //  第二個參數 是全類名 com.example.ccalljavademo.JNI 將.換成/

    jclass jclazz = (*env)->FindClass(env, "com/example/ccalljavademo/JNI");

    //  2.得到字節碼對應的方法
    //      jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    //  第三個參數:方法名
    //  第四個參數:方法簽名
    jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "add", "(II)I");
    //  3.得到類的實例
    //      jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject obj = (*env)->AllocObject(env, jclazz);
    //  4.執行方法
    //          jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallIntMethod(env, obj, jmethodid, 99, 12); //成功調用JNI.java中的add方法
}

JNIEXPORT void JNICALL Java_com_example_ccalljavademo_JNI_cMethods_1call_1printString(
        JNIEnv *env, jobject jobj, jstring jstr) {
    //  1.得到字節碼文件
    //     jclass      (*FindClass)(JNIEnv*, const char*);
    //  第二個參數 是全類名 com.example.ccalljavademo.JNI 將.換成/

    jclass jclazz = (*env)->FindClass(env, "com/example/ccalljavademo/JNI");

    //  2.得到字節碼對應的方法
    //      jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    //  第三個參數:方法名
    //  第四個參數:方法簽名
    jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "printString",
            "(Ljava/lang/String;)V");
    //  3.得到類的實例
    //      jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject obj = (*env)->AllocObject(env, jclazz);
    //  4.執行方法
    //    void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env, obj, jmethodid, jstr); //成功調用JNI.java中的add方法
}

JNIEXPORT void JNICALL Java_com_example_ccalljavademo_JNI_cMethods_1call_1sayHello(
        JNIEnv *env, jobject jobj) {
    //  1.得到字節碼文件
    //     jclass      (*FindClass)(JNIEnv*, const char*);
    //  第二個參數 是全類名 com.example.ccalljavademo.JNI 將.換成/

    jclass jclazz = (*env)->FindClass(env, "com/example/ccalljavademo/JNI");

    //  2.得到字節碼對應的方法
    //      jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    //  第三個參數:方法名
    //  第四個參數:方法簽名
    jmethodID jmethodid = (*env)->GetStaticMethodID(env, jclazz, "sayHello",
            "()V");
    //  3.得到類的實例
    //      jobject     (*AllocObject)(JNIEnv*, jclass);
//          jobject obj = (*env)->AllocObject(env, jclazz);
    //  4.執行方法
//              void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
    (*env)->CallStaticVoidMethod(env, jclazz, jmethodid);
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved