Jimmy Chen

A Programmer

(原创)在JNI(Native)层调用Android的Log系统

  最近在用Android Studio写JNI程序,发现在写Native层C++代码时貌似没有特别好的打印Log的方法,所以就有了这一篇,这一篇主要是通过查找、记录Android层面的Log方法,然后在Native层进行调用,达到打印Log的效果。下面直接将源码贴上,源码也比较简单,供大家参考

ThrowException.h

  这个类是一个辅助类,当找不到对应类、方法或者字段的时候可以直接调用这个这个方法的类来抛出异常。后续如果可以扩充这个类来完善异常抛出

//
// Created by Jimmy on 10/25/18.
// 当JNI找不到对应类、方法或者字段的时候抛出异常
//

#ifndef TESTAPP_THROWEXCEPTION_H
#define TESTAPP_THROWEXCEPTION_H

#include <jni.h>


class ThrowException {
public:
    static jint ClassNotFound(JNIEnv *env, char *className);
    static jint NoSuchMethod(JNIEnv *env, char *methodName);
    static jint NoSuchField(JNIEnv *env, char *fieldName);
};


#endif //TESTAPP_THROWEXCEPTION_H

ThrowException.cpp

  异常抛出类的具体实现如下:

//
// Created by Jimmy on 10/25/18.
//

#include "ThrowException.h"
#include <string.h>

jint ThrowException::ClassNotFound(JNIEnv *env, char *className) {
    env->ExceptionDescribe();
    env->ExceptionClear();
    jclass jclassClassNotFound = env->FindClass("java/lang/ClassNotFoundException");
    return env->ThrowNew(jclassClassNotFound, className);
}

jint ThrowException::NoSuchMethod(JNIEnv *env, char *methodName) {
    env->ExceptionDescribe();
    env->ExceptionClear();
    jclass jclassNotSuchMethod = env->FindClass("java/lang/NoSuchMethodException");
    return env->ThrowNew(jclassNotSuchMethod, methodName);
}

jint ThrowException::NoSuchField(JNIEnv *env, char *fieldName) {
    env->ExceptionDescribe();
    env->ExceptionClear();
    jclass jclassNoSuchField = env->FindClass("java/lang/NoSuchFieldException");
    return env->ThrowNew(jclassNoSuchField, fieldName);
}

NativeLog.h

  NativeLog类的头文件

//
// Created by Jimmy on 10/25/18.
// 再Native层调用Logcat打印log信息
//

#ifndef TESTAPP_LOG_H
#define TESTAPP_LOG_H

#include <jni.h>
#include <stdio.h>

class NativeLog {
private:
    // 记录对应的方法ID
    jmethodID jmethodIDLogV;
    jmethodID jmethodIDLogD;
    jmethodID jmethodIDLogI;
    jmethodID jmethodIDLogW;
    jmethodID jmethodIDLogE;
    // 记录android.util.Log类的ID
    jclass jclassLog;

public:
    NativeLog(JNIEnv *env);
    NativeLog() {}
    // Log方法定义,名字和上次Log系统相同
    int v(JNIEnv *env, const char *LOG_TAG, const char *Msg);
    int d(JNIEnv *env, const char *LOG_TAG, const char *Msg);
    int i(JNIEnv *env, const char *LOG_TAG, const char *Msg);
    int w(JNIEnv *env, const char *LOG_TAG, const char *Msg);
    int e(JNIEnv *env, const char *LOG_TAG, const char *Msg);
};

#endif //TESTAPP_LOG_H

NativeLog.cpp

//
// Created by Jimmy on 10/25/18.
//

#include "NativeLog.h"
#include "ThrowException.h"

// NativeLog类初始化的时候通过查找android.util.log类以及其中的方法进行初始化
NativeLog::NativeLog(JNIEnv *env) {
    // 查找android.util.log类
    jclass jclass1 = env->FindClass("android/util/Log");
    if (jclass1 == nullptr && env->ExceptionCheck()) {
        ThrowException::ClassNotFound(env, "android/util/Log");
    }
    jclassLog = (jclass)env->NewGlobalRef(jclass1);

    // 查找v、i、d、w、e等方法的方法ID
    jmethodIDLogV = env->GetStaticMethodID(jclassLog, "v", "(Ljava/lang/String;Ljava/lang/String;)I");
    if (jmethodIDLogV == nullptr && env->ExceptionCheck()) {
        ThrowException::NoSuchMethod(env, "v");
    }

    jmethodIDLogI = env->GetStaticMethodID(jclassLog, "i", "(Ljava/lang/String;Ljava/lang/String;)I");
    if (jmethodIDLogI == nullptr && env->ExceptionCheck()) {
        ThrowException::NoSuchMethod(env, "i");
    }

    jmethodIDLogD = env->GetStaticMethodID(jclassLog, "d", "(Ljava/lang/String;Ljava/lang/String;)I");
    if (jmethodIDLogD == nullptr && env->ExceptionCheck()) {
        ThrowException::NoSuchMethod(env, "d");
    }

    jmethodIDLogE = env->GetStaticMethodID(jclassLog, "e", "(Ljava/lang/String;Ljava/lang/String;)I");
    if (jmethodIDLogE == nullptr && env->ExceptionCheck()) {
        ThrowException::NoSuchMethod(env, "e");
    }

    jmethodIDLogW = env->GetStaticMethodID(jclassLog, "w", "(Ljava/lang/String;Ljava/lang/String;)I");
    if (jmethodIDLogW == nullptr && env->ExceptionCheck()) {
        ThrowException::NoSuchMethod(env, "w");
    }
}

// 下面的就是具体的Log打印实现
int NativeLog::d(JNIEnv *env, const char *LOG_TAG, const char *Msg) {
    jstring jstringLogTag = env->NewStringUTF(LOG_TAG);
    jstring jstringMsg = env->NewStringUTF(Msg);

    int ret = env->CallStaticIntMethod(jclassLog, jmethodIDLogD, jstringLogTag, jstringMsg);

    env->DeleteLocalRef(jstringLogTag);
    env->DeleteLocalRef(jstringMsg);

    return ret;
}

int NativeLog::v(JNIEnv *env, const char *LOG_TAG, const char *Msg) {
    jstring jstringLogTag = env->NewStringUTF(LOG_TAG);
    jstring jstringMsg = env->NewStringUTF(Msg);

    int ret = env->CallStaticIntMethod(jclassLog, jmethodIDLogV, jstringLogTag, jstringMsg);

    env->DeleteLocalRef(jstringLogTag);
    env->DeleteLocalRef(jstringMsg);
    return ret;
}

int NativeLog::i(JNIEnv *env, const char *LOG_TAG, const char *Msg) {
    jstring jstringLogTag = env->NewStringUTF(LOG_TAG);
    jstring jstringMsg = env->NewStringUTF(Msg);

    int ret = env->CallStaticIntMethod(jclassLog, jmethodIDLogI, jstringLogTag, jstringMsg);

    env->DeleteLocalRef(jstringLogTag);
    env->DeleteLocalRef(jstringMsg);
    return ret;
}

int NativeLog::w(JNIEnv *env, const char *LOG_TAG, const char *Msg) {
    jstring jstringLogTag = env->NewStringUTF(LOG_TAG);
    jstring jstringMsg = env->NewStringUTF(Msg);

    int ret = env->CallStaticIntMethod(jclassLog, jmethodIDLogW, jstringLogTag, jstringMsg);

    env->DeleteLocalRef(jstringLogTag);
    env->DeleteLocalRef(jstringMsg);
    return ret;
}

int NativeLog::e(JNIEnv *env, const char *LOG_TAG, const char *Msg) {
    jstring jstringLogTag = env->NewStringUTF(LOG_TAG);
    jstring jstringMsg = env->NewStringUTF(Msg);

    int ret = env->CallStaticIntMethod(jclassLog, jmethodIDLogE, jstringLogTag, jstringMsg);

    env->DeleteLocalRef(jstringLogTag);
    env->DeleteLocalRef(jstringMsg);
    return ret;
}

How to use

1. 在使用前将上面四个文件包含到自己的源码中

2. 定义一个全局变量Log,定义如下:std::shared_ptr<NativeLog> native_log;

3. 然后再使用前调用类构造函数进行初始化:Log = std::shared_ptr<NativeLog>(new NativeLog(env));

4. 最后通过Log->d()Log->e()等方法进行Log调用即可

下面是一个小的调用示范

#include <jni.h>
#include <string>
#include <iostream>
#include <memory>
#include <unistd.h>

#include <cstdlib>
#include <wait.h>
#include <sstream>
#include "NativeLog.h"

// 定义全局的Log变量
std::shared_ptr<NativeLog> Log;

extern "C"
JNIEXPORT jstring

JNICALL
Java_com_blog4jimmy_logandexception_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

// 上层定义了一个native_init方法,进行一些基本的初始化
extern "C"
JNIEXPORT void JNICALL
Java_com_blog4jimmy_logandexception_MainActivity_native_1init(JNIEnv *env, jobject instance) {

    std::stringstream stringstream;

    // 调用NativeLog的构造函数创建Native层的Log系统
    Log = std::shared_ptr<NativeLog>(new NativeLog(env));
    Log->d(env, "JIMMY", "Init native log success");

    Log->e(env, "Jimmy", "Test Log.e()");
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d 博主赞过: