Android JNI 开发说明和示例

6 篇文章 0 订阅
4 篇文章 0 订阅

Android中开发JNI,把关键业务逻辑的代码使用C/C++的native实现可以隐藏代码逻辑;

一. JNI_OnLoad()

是Android的java层Dalvik虚拟机加载C库时,首先调用JNI_OnLoad()函数,所以, 在JNI_OnLoad()里面进行初始化操作,比如 注册JNI函数,获取Java的类等等。

注意: 初始化时动态注册native函数,运行时可以提高java层调用native函数的效率。

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){

    LOG_I("JNI_OnLoad: vm=%p, reserved=%p", vm, reserved);
     return JNI_VERSION_1_4;
}
返回值是jint 类型,表示Dalvik虚拟机C库使用JNI版本。如果库里面没有写明JNI_OnLoad()函数,VM会, 默认该库使用最老的JNI 1.1版本; 一定要返回版本号,否则会出错.

二. JNI_OnUnload()

当Java虚拟机释放该C库时,则会调用JNI_OnUnload()函数, 可以在此函数进行善后清除动作,通常不用。

————————————————

下面暂时开发逻辑关键代码段:

//JNI 函数声明
jstring udpSendto(JNIEnv *env, jclass clazz, jbyteArray _buf, jlong _len, jstring _host, jint _port);
void setHookVpnProxyAddr(JNIEnv *env, jclass clazz, jstring _proxy_host, jint _proxy_port);


/**
 * 全局java虚拟机
 * 首先, JNIEnv *env指针和 jobject对象都不能跨线程使用
 * 其次, jvm可以多线程共享,但是只有主线程可以销毁虚拟机
 * refer: https://blog.csdn.net/dream2009gd/article/details/8654928
 */
JavaVM *gJavaVM;
jint gJniVersion = JNI_VERSION_1_4;

/**
 * Java层C层回调的接口类
 */
static const char* HULK_JNI_UTIL_CLASS_PATH = "com/hulk/jni/HulkJniUtil";
/**
 * C回调Java层的接口类
 */
static const char *JAVA_JNI_CALLER_CLASS_PATH = "com/hulk/jni/JavaJniCaller";

/**
 * HulkJniUtil的方法列表
 * <p>JNINativeMethod结构体,分别是java层的函数名称,签名,对应的函数指针
 */
static const JNINativeMethod HULK_JNI_UTIL_METHODS[] ={
        //jstring udpSendto(JNIEnv *env, jclass clazz, jbyteArray _buf, jlong _len, jstring _host, jint _port)
        {"udpSendto"/*java层方法名*/, "([BJLjava/lang/String;I)Ljava/lang/String;", (void*)udpSendto/*C层方法名*/},
        {"setVpnUdpProxyAddr", "(Ljava/lang/String;I)V", (void*)setHookVpnProxyAddr},

};

/**
 * Java层 com.hulk.jni.JavaJniCaller 的全局引用.
 * C层可以直接调用里面的方法;
 * 注意: 每次调用JavaJniCaller的方法都需要env在当前线程,即调用ensureJniEnvCreated()返回的env.
 */
jclass gJavaJniCallerClass;

/**
 * Android的Java层加载(启动)Native层模块的入口函数
 * <p>Dalvik虚拟机加载C库时,第一件事是调用JNI_OnLoad()函数,
 * <p>所以在JNI_OnLoad()里面进行一些初始化工作,如注册JNI函数等等。
 * <p>注册本地函数,可以加快java层调用本地函数的效率。
 * <p> 返回值是jint 类型,告诉Dalvik虚拟机此C库使用哪一个JNI版本。如果你的库里面没有写明JNI_OnLoad()函数,VM会默认该库使用最老的JNI 1.1版本;
 * @param vm  Java 虚拟机
 * @param reserved 保留字段
 * @return
 */
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    LOG_I("JNI_OnLoad: vm=%p, reserved=%p", vm, reserved);
    jint result = -1;
    gJavaVM = vm;
    JNIEnv *env = NULL;

    // global_jvm = vm;
    gJavaVM->GetEnv(reinterpret_cast<void **>(&env), gJniVersion);
    LOG_I("JNI_OnLoad: env=%p", env);
    //JavaJniCaller的全局引用
    jclass caller_clazz = env->FindClass(JAVA_JNI_CALLER_CLASS_PATH);
    gJavaJniCallerClass = (jclass)env->NewGlobalRef(caller_clazz);

    //动态注册方法: com/hulk/jni/HulkJniUtil.xxXXX()
    jclass hulk_clazz = (env)->FindClass(HULK_JNI_UTIL_CLASS_PATH);
    if (hulk_clazz == nullptr) {
        return -1;
    }

    int method_count = 0;
    method_count = sizeof(HULK_JNI_UTIL_METHODS) / sizeof(HULK_JNI_UTIL_METHODS[0]);
    if (method_count > 0) {
        LOG_I("JNI_OnLoad: Register natives method count=%d", method_count);
        if((env)->RegisterNatives(hulk_clazz, HULK_JNI_UTIL_METHODS, method_count) < 0) {
            LOG_E("JNI_OnLoad: Failed to register natives");
            return -1;
        }
    }

    //一定要返回版本号,否则会出错:
    // Fatal signal 5 (SIGTRAP), code 1 (TRAP_BRKPT) (native函数缺少返回语句也会抛出此错误)
    result = gJniVersion;//eg JNI_VERSION_1_4
    LOG_I("JNI_OnLoad: Finished result=%d", result);
    return result;
}

/**
 * 当虚拟机释放该C库时,则会调用JNI_OnUnload()函数来进行善后清除动作。
 * @param vm  Java 虚拟机
 * @param reserved 保留字段
 */
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) {
    LOG_W("JNI_OnUnload: vm=%p, reserved=%p", vm, reserved);
}

extern "C" JNIEnv *_getEnv() {
    JNIEnv *env = NULL;
    if (gJavaVM != NULL) {
        gJavaVM->GetEnv(reinterpret_cast<void **>(&env), gJniVersion);
        LOG_I("_getEnv: GetEnv env=%p", env);
    } else {
        LOG_W("_getEnv: gJavaVM in null");
    }
    return env;
}

extern "C" JNIEnv *ensureEnvCreated() {
    JNIEnv *env = _getEnv();
    if (env == NULL && gJavaVM != NULL) {
        gJavaVM->AttachCurrentThread(&env, NULL);
        LOG_I("ensureEnvCreated: AttachCurrentThread env=%p", env);
    } else {
        LOG_W("ensureEnvCreated: Failed to get env=%p, gJavaVM=%p", env, gJavaVM);
    }
    return env;
}

/**
 * 调用Java层(com.hulk.jni.JavaJniCaller)的方法:
 * public void call(String uri, String method, String arg)
 * @param uri 统一定位符 eg "context:.hulk/test"
 * @param method 方法 eg "get_proxy_port"
 * @param arg 参数
 * @return 返回0表示成功,其他失败
 */
int execJavaJniCall(char *uri, char *method, char *arg) {
    LOG_D("execJavaJniCall: uri=%s, uri=%s, uri=%s", uri, method, arg);
    JNIEnv *env = ensureEnvCreated();
    if (env == nullptr) {
        LOG_E("execJavaJniCall: env is NULL");
        return -1;
    }
    if (gJavaJniCallerClass == nullptr) {
        LOG_E("execJavaJniCall: gJavaJniCallerClass is NULL");
        return -2;
    }
    jmethodID call_method_ID = env->GetMethodID(gJavaJniCallerClass, "call",
                                                "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
    if (call_method_ID == nullptr) {
        LOG_E("execJavaJniCall: Got call_method_ID is NULL");
        return -3;
    }
    jstring j_uri = nullptr;
    jstring j_method = nullptr;
    jstring  j_arg = nullptr;
    /**
    j_uri = env->NewStringUTF("context:.hulk/test");
    j_method = env->NewStringUTF("get_proxy_port");
    j_arg = env->NewStringUTF("34534");
    */
    j_uri = env->NewStringUTF(uri);
    j_method = env->NewStringUTF(method);
    j_arg = env->NewStringUTF(arg);
    LOG_D("execJavaJniCall: CallVoidMethod gJavaJniCallerClass=%p, call_method_ID%p", &gJavaJniCallerClass, &call_method_ID);
    env->CallVoidMethod(gJavaJniCallerClass, call_method_ID, j_uri, j_method, j_arg);//无返回值情况
    LOG_D("execJavaJniCall: Finished.");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值