安卓so动态库加载代理实现,可以实现C层的类反射效果
2016-05-26 15:07
501 查看
一般来说如果我们需要加载so文件,需要一个java对应层的类,即有一个类必须要是包名和类名是不变的。
比如说下面的c层代码,这样写就必须要求有个类是com.example.hellojni.HelloJni,调用的方法为stringFromJNI
但是有些时候我们需要不使用这个包名,因为可能包名有冲突,这种时候,我们应该怎么办呢?
我们知道,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");
}
比如说下面的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");
}
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法