安卓so动态库加载代理实现,可以实现C层的类反射效果

本文介绍了一种在Android环境下动态加载SO文件并进行JNI函数映射的方法,通过这种方式可以灵活地调整本地代码与Java层之间的交互,解决了由于包名冲突导致的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一般来说如果我们需要加载so文件,需要一个java对应层的类,即有一个类必须要是包名和类名是不变的。


比如说下面的c层代码,这样写就必须要求有个类是com.example.hellojni.HelloJni,调用的方法为stringFromJNI

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

#ifndef _Included_com_example_hellojni_HelloJni
#define _Included_com_example_hellojni_HelloJni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_hellojni_HelloJni
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI
  (JNIEnv *, jobject);

/*
 * Class:     com_example_hellojni_HelloJni
 * Method:    unimplementedStringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_unimplementedStringFromJNI
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif


但是有些时候我们需要不使用这个包名,因为可能包名有冲突,这种时候,我们应该怎么办呢?


我们知道,C层是可以用本地方法去加载so文件的。

即dlopen 和dlsym


dlopen是打开so,dlsym是导出函数,可以对应到函数指针实现。


我们知道jni可以动态注册函数,它会在运行时里动态映射函数表。


我们想要实现的是完整的功能,就是不需要再编写c层,一次编写,就只需要更改java层代码实现,就可以实现所有对应的本地代码代理。


类似反射的实现办法。

这里是c层的实现,本质就是在调用的时候将函数指针重新注册到jni的映射中。

#include <string.h>
#include <jni.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <android/log.h>
#include <assert.h>
#include <vector>
#define LOG_TAG "debug log"
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args)
using namespace std;
const char *kClassName = "com/siecom/nativelibs/NativeLoad";//指定要注册的类
void *fp;
vector<JNINativeMethod> vec;
static int registerJniMapping(JNIEnv *env, jobject thiz, jstring java_func, jstring native_func);

jstring native_hello(JNIEnv *env, jobject thiz, jstring str) {
    return env->NewStringUTF("test");
}

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;
}


jint load_so(JNIEnv *env, jobject thiz, jstring so_name, jstring func_name) {
    void *filehandle = NULL;
    void *getResult = NULL;
    jint result = -1;
    char *so = jstringTostring(env, so_name);
    char *func = jstringTostring(env, func_name);
    filehandle = dlopen(so, RTLD_LAZY);
    if (filehandle) {
        LOGE("filehandle %d", filehandle);
        getResult = dlsym(filehandle, func);
        LOGE("dlsym %d", getResult);
        fp = getResult;
        if (getResult) {
            result = 0;
            return result;
        }
    }
    return result;
}


static void getMethods(){

       JNINativeMethod method1,method2,method3;
       method1.name = "test";
       method1.signature = "(Ljava/lang/String;)Ljava/lang/String;";
       method1.fnPtr  = (void *) native_hello;
       vec.push_back(method1);

       method2.name = "load_so";
       method2.signature = "(Ljava/lang/String;Ljava/lang/String;)I",
       method2.fnPtr  = (void*)load_so;
       vec.push_back(method2);

       method3.name = "registerJniMapping";
       method3.signature = "(Ljava/lang/String;Ljava/lang/String;)I",
       method3.fnPtr  = (void*)registerJniMapping;
       vec.push_back(method3);

}

/*
* 为某一个类注册本地方法
*/
static int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *gMethods,
                                 int numMethods) {
    jclass clazz;
    clazz = env->FindClass(className);
    if (clazz == NULL) {
        LOGE("Native registration unable to find class '%s'", className);
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        LOGE("RegisterNatives failed for '%s'", className);
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

static int registerJniMapping(JNIEnv *env, jobject thiz, jstring java_func, jstring native_func) {
    void *tmp = fp;
    int pos = vec.size();
    LOGE("before pos:%d", pos);
    JNINativeMethod method;
    method.name = jstringTostring(env, java_func);
    method.signature = jstringTostring(env, native_func);
    method.fnPtr = (void *) tmp;
    vec.push_back(method);
    pos = vec.size();
    LOGE("after pos:%d", pos);
    return registerNativeMethods(env, kClassName, &vec[0],
                                 vec.size());
}
/*
* 为所有类注册本地方法
*/
static int registerNatives(JNIEnv *env) {
    getMethods();
    return registerNativeMethods(env, kClassName, &vec[0],
                                 vec.size());
}
typedef union {
    JNIEnv *env;
    void *venv;
} UnionJNIEnvToVoid;
/*
* System.loadLibrary("lib")时调用
* 如果成功返回JNI版本, 失败返回-1
*/
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    UnionJNIEnvToVoid uenv;
    uenv.venv = NULL;
    jint result = -1;
    JNIEnv *env = NULL;
    //LOGI("JNI_OnLoad");
    if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("ERROR: GetEnv failed");
        goto bail;
    }
    env = uenv.env;
    if (registerNatives(env) != JNI_TRUE) {
        LOGE("ERROR: registerNatives failed");
        goto bail;
    }
    result = JNI_VERSION_1_4;
    bail:
    return result;
}

这是对应java层,当然java层还是需要有个固定的包名的,这是不能避免的,但是你可以直接使用这个类去load你需要的so库类


 static
    {
        System.loadLibrary("Loadso");
        /**
         * 动态注册jni函数
         * 不需要写对应的c层函数,直接映射对应关系
         * 需要函数的返回类型和入参java层变量签名如(Ljava/lang/String;)I
         *
         */
        int res =  NativeLoad.load_so("libwltdecode.so","Java_com_ivsign_android_IDCReader_IDCReaderSDK_wltInit");
        if(res==0)
            Log.e("loadso", "正确加载so和函数");
        res = NativeLoad.registerJniMapping("init","(Ljava/lang/String;)I");
        if(res==1)
            Log.e("loadso:", "正确重注册jni");

        res =  NativeLoad.load_so("libwltdecode.so","Java_com_ivsign_android_IDCReader_IDCReaderSDK_wltGetBMP");
        if(res==0)
            Log.e("loadso", "正确加载so和函数");
        res = NativeLoad.registerJniMapping("wltGetBMP","([B[B)I");
        if(res==1)
            Log.e("loadso:", "正确重注册jni");

    }





评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值