下面介绍NDK相关基础性的知识。

打印log

一般调试ndk都需要log。那怎么打印log呢?
下面是打印log的方法。

#include <jni.h>
#include<android/log.h>

#define LOG_TAG "System.out.c"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)

这里用宏定义定义了android系统下几个log打印。__VA_ARGS__代表宏参数...,括号内 ... 的内容原样抄写在__VA_ARGS__的位置。
使用方法如下,在适合的地方调用该方法。

LOGD("这里打印log");
LOGI("这是来自info信息");

java调用c的函数

这里介绍的是静态注册的方法。

extern "C" {

// Data callback stuff
JavaVM* theJvm;
jobject dataCallbackObj;
jmethodID midDataCallback;

/**
 * Initializes JNI interface stuff, specifically the info needed to call back into the Java
 * layer when MIDI data is received.
 */
JNICALL void Java_com_slzr_ndk_NDKManager_initNative(JNIEnv * env, jobject instance) {
    env->GetJavaVM(&theJvm);

    // Setup the receive data callback (into Java)
    jclass clsNDKManager = env->FindClass("com/slzr/ndk/NDKManager");
    dataCallbackObj = env->NewGlobalRef(instance);
    midDataCallback = env->GetMethodID(clsNDKManager, "onNativeMessageReceive", "([B)V");//[B
}

} // extern "C"

当JVM调用这些函数,就传递一个JNIEnv指针,一个jobject的指针,你说看到是输入参数里面有(JNIEnv * env, jobject instance)就是这个东西了。
Java_com_slzr_ndk_NDKManager_initNative包含有了包名,类名和方法名,中间用下划线分开。由于该方法是没有返回值,所以直接用了JNICALL,如果有返回值比如说是String返回,用JNIEXPORT jstring JNICALL这样的形式。JNIEXPORT和JNICALL中间夹着返回值。

对应到java里面的方法是。

    public native void initNative();

native 是java里面的关键字,表示地调用c语言里面的函数。

c调用java的方法

在NDK里面c调用java的方法类似于反射。
需要先获取到对应的方法名称,虚拟机等。

这个回去相关的信息的c代码。

#include <jni.h>

extern "C" {

// Data callback stuff
JavaVM* theJvm;
jobject dataCallbackObj;
jmethodID midDataCallback;

/**
 * Initializes JNI interface stuff, specifically the info needed to call back into the Java
 * layer when MIDI data is received.
 */
JNICALL void Java_com_slzr_ndk_NDKManager_initNative(JNIEnv * env, jobject instance) {
    env->GetJavaVM(&theJvm);

    // Setup the receive data callback (into Java)
    jclass clsNDKManager = env->FindClass("com/slzr/ndk/NDKManager");
    dataCallbackObj = env->NewGlobalRef(instance);
    midDataCallback = env->GetMethodID(clsNDKManager, "onNativeMessageReceive", "([B)V");//[B
}

} // extern "C"

initNative是初始化函数,对应java里面的public native void initNative();调用。env是java虚拟机对象,instance是Object。在这里,先获取到对应的class对象、Object对象和对应的方法。

下面的c调用java里面的方法。

// The Data Callback
extern JavaVM *theJvm;              // Need this for allocating data buffer for...
extern jobject dataCallbackObj;     // This is the (Java) object that implements...
extern jmethodID midDataCallback;   // ...this callback routine

int soso(char *data) {
    ......
    JNIEnv *env;
    theJvm->AttachCurrentThread(&env, NULL);
    if (env == NULL) {
        LOGI("Error retrieving JNI Env");
    }

    // Allocate the Java array and fill with received data
    jbyteArray ret = env->NewByteArray(strlen(data));
    env->SetByteArrayRegion(ret, 0, strlen(data), (jbyte *) data);

    // send it to the (Java) callback
    env->CallVoidMethod(dataCallbackObj, midDataCallback, ret);


    return 1;
}

这里是上面保存下来的相关参数再调用一次。jbyteArray 是转换成java里面的数组。可以在java里面写相对应的实现。
这是调用java里面的onNativeMessageReceive方法。

    public void onNativeMessageReceive(final byte[] message) {
        String str=null;
        try {
            str = new String(message, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        Log.i("NDKManager","这是来自ndkmanger的信息" + str);
    }

附件:
demo源码地址

打赏 赞(0)
微信
支付宝
微信二维码图片

微信扫描二维码打赏

支付宝二维码图片

支付宝扫描二维码打赏

分类: Android

0 条评论

发表评论

电子邮件地址不会被公开。