JNI方法注册方式分为动态与静态注册。
下划线隔开,通过javah生成带签名的函数,然后去实现这些函数,这种也是官方推荐的方式。
Java层方法声明成native方法:
public native String signture(String sig);
JNI层对应的方法:
JNIEXPORT jstring JNICALL Java_jni_chowen_com_nativeapp_MainActivity_signture
(JNIEnv *env, jobject jobject1, jstring jstring1) {
//md5加密
const char *jcstr = (env)->GetStringUTFChars(jstring1, 0);
MD5 md5;
md5.update(jcstr);
string jstring2 = md5.toString();
//package_name
jstring packName = getPackname(env, jobject1, jobject1);
const char *c = env->GetStringUTFChars(packName, 0);
LOGE("getPackageName: %s", c);
//signature
jstring signatures = getSignature(env, jobject1, jobject1);
const char *signaturesc = env->GetStringUTFChars(signatures, 0);
LOGE("signatures: %s", signaturesc);
return env->NewStringUTF(jstring2.c_str());
}
优点:
java与jni方法对应清晰
缺点:
必须按照函数规则,方法名很长
运行时查找函数效率低
定义的native方法
public native String stringFromJNI();
public native String signture(String sig);
int RegisterNatives(JNIEnv *env) {
jclass clazz = env->FindClass("jni/chowen/com/nativeapp/MainActivity");
if (clazz == NULL) {
LOGE("con't find class: jni/chowen/com/nativeapp/MainActivity");
return JNI_ERR;
}
// 动态注册的函数数组
JNINativeMethod methods_MainActivity[] = {
{"stringFromJNI", "()Ljava/lang/String;", (void *) stringFromJNI},
{"signture", "(Ljava/lang/String;)V", (void *) signture}
};
LOGE("can find class: jni/chowen/com/nativeapp/MainActivity %d >>> %d", sizeof(methods_MainActivity), sizeof(methods_MainActivity[0]));
// int len = sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]);
// 实际注册的函数,clazz:对象,methods_MainActivity:函数数组
//sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]): 方法个数
return env->RegisterNatives(clazz, methods_MainActivity,
sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]));
}
env->RegisterNatives(clazz, methods_MainActivity,
sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]));
RegisterNatives为函数动态注册的方法
参数:
clazz:java对象
methods_MainActivity:函数数组
sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]):计算函数个数
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
JNINativeMethod为一个结构体。定义在jni.h头文件中。
const char* name:java层native函数名称
const char* signature:函数签名描述
void* fnPtr:Native对应的函数指针
那么动态注册的这个方法RegisterNatives 调用时机是什么时候呢?下来介绍下JNI_OnLoad(JavaVM* vm, void* reserved)函数。那么问题来了,JNI_OnLoad函数什么时候会被调用呢?
原来是当通过System.loadLibrary()加载so的时候,VM会立即调用JNI_OnLoad函数。所以一些初始化的工作可以放到JNI_OnLoad函数里去完成。所以动态注册就在这个函数里完成java native方法与so函数之间的绑定关系。
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
LOGE("JNI_OnLoad RegisterNatives JNI_OnLoad");
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
jint result = RegisterNatives(env);
LOGE("RegisterNatives result: %d", result);
return JNI_VERSION_1_6;
}
动态注册在framework层使用的多,而静态注册一般我们平时开发在上层用的较多,也是官方推荐的方式。
用javah风格的代码,则dvm调用dvmResolveNativeMethod进行动态延迟解析,直到需要调用的时候才会解析。
延迟加载
void dvmResolveNativeMethod(const u4* args, JValue* pResult,
const Method* method, Thread* self)
{
ClassObject* clazz = method->clazz;
/*
* If this is a static method, it could be called before the class
* has been initialized.
*/
if (dvmIsStaticMethod(method)) {
if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
assert(dvmCheckException(dvmThreadSelf()));
return;
}
} else {
assert(dvmIsClassInitialized(clazz) ||
dvmIsClassInitializing(clazz));
}
/* start with our internal-native methods */
//在内部本地方法表中查询
DalvikNativeFunc infunc = dvmLookupInternalNativeMethod(method);
if (infunc != NULL) {
/* resolution always gets the same answer, so no race here */
IF_LOGVV() {
char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
LOGVV("+++ resolved native %s.%s %s, invoking",
clazz->descriptor, method->name, desc);
free(desc);
}
if (dvmIsSynchronizedMethod(method)) {
ALOGE("ERROR: internal-native can't be declared 'synchronized'");
ALOGE("Failing on %s.%s", method->clazz->descriptor, method->name);
dvmAbort(); // harsh, but this is VM-internal problem
}
DalvikBridgeFunc dfunc = (DalvikBridgeFunc) infunc;
dvmSetNativeFunc((Method*) method, dfunc, NULL);
dfunc(args, pResult, method, self);
return;
}
/* now scan any DLLs we have loaded for JNI signatures */
// 去已加载的动态库中查询
void* func = lookupSharedLibMethod(method);
if (func != NULL) {
/* found it, point it at the JNI bridge and then call it */
dvmUseJNIBridge((Method*) method, func);
(*method->nativeFunc)(args, pResult, method, self);
return;
}
IF_ALOGW() {
char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
ALOGW("No implementation found for native %s.%s:%s",
clazz->descriptor, method->name, desc);
free(desc);
}
dvmThrowUnsatisfiedLinkError("Native method not found", method);
}
去以加载的动态库中查询,这个时候会调用findMethodInLib函数,这个函数就是动态库中没有重载JNI_OnLoad 函数。然后在动态库中查询注册函数。
/*
* (This is a dvmHashForeach callback.)
*
* Search for a matching method in this shared library.
*
* TODO: we may want to skip libraries for which JNI_OnLoad failed.
*/
static int findMethodInLib(void* vlib, void* vmethod)
{
const SharedLib* pLib = (const SharedLib*) vlib;
const Method* meth = (const Method*) vmethod;
char* preMangleCM = NULL;
char* mangleCM = NULL;
char* mangleSig = NULL;
char* mangleCMSig = NULL;
void* func = NULL;
int len;
if (meth->clazz->classLoader != pLib->classLoader) {
ALOGV("+++ not scanning '%s' for '%s' (wrong CL)",
pLib->pathName, meth->name);
return 0;
} else
ALOGV("+++ scanning '%s' for '%s'", pLib->pathName, meth->name);
/*
* First, we try it without the signature.
*/
preMangleCM =
createJniNameString(meth->clazz->descriptor, meth->name, &len);
if (preMangleCM == NULL)
goto bail;
// java与native进行替换,替换成javah风格函数
mangleCM = mangleString(preMangleCM, len);
if (mangleCM == NULL)
goto bail;
ALOGV("+++ calling dlsym(%s)", mangleCM);
func = dlsym(pLib->handle, mangleCM);
if (func == NULL) {
mangleSig =
createMangledSignature(&meth->prototype);
if (mangleSig == NULL)
goto bail;
mangleCMSig = (char*) malloc(strlen(mangleCM) + strlen(mangleSig) +3);
if (mangleCMSig == NULL)
goto bail;
sprintf(mangleCMSig, "%s__%s", mangleCM, mangleSig);
ALOGV("+++ calling dlsym(%s)", mangleCMSig);
func = dlsym(pLib->handle, mangleCMSig);
if (func != NULL) {
ALOGV("Found '%s' with dlsym", mangleCMSig);
}
} else {
ALOGV("Found '%s' with dlsym", mangleCM);
}
bail:
free(preMangleCM);
free(mangleCM);
free(mangleSig);
free(mangleCMSig);
return (int) func;
}
mangleString(preMangleCM, len): java与native进行替换,替换成javah生成的风格函数。
总结几步:
1.vm调用 dvmResolveNativeMethod函数
2.void* func = lookupSharedLibMethod(method) 去已加载的动态库中查询函数的实现。
3.查询到然后执行下面函数将JNI函数地址保持到ClassObject中
dvmUseJNIBridge((Method) method, func)
(method->nativeFunc)(args, pResult, method, self)
4.最后将函数添加到ClassObject结构体中的directMethods结构体成员中。

System.loadLibrary
----Runtime.loadLibrary0
------Runtime.doLoad
--------Runtime.nativeLoad
SO加载过程如下:
System.loadLibrary
@CallerSensitive
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}
Runtime.loadLibrary0
@CallerSensitive
public void loadLibrary(String libname) {
loadLibrary0(VMStack.getCallingClassLoader(), libname);
}
Runtime.doLoad
synchronized void loadLibrary0(ClassLoader loader, String libname) {
if (libname.indexOf((int)File.separatorChar) != -1) {
throw new UnsatisfiedLinkError(
"Directory separator should not appear in library name: " + libname);
}
String libraryName = libname;
if (loader != null) {
String filename = loader.findLibrary(libraryName);
if (filename == null) {
// It's not necessarily true that the ClassLoader used
// System.mapLibraryName, but the default setup does, and it's
// misleading to say we didn't find "libMyLibrary.so" when we
// actually searched for "liblibMyLibrary.so.so".
throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
System.mapLibraryName(libraryName) + "\"");
}
String error = doLoad(filename, loader);
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
return;
}
String filename = System.mapLibraryName(libraryName);
List<String> candidates = new ArrayList<String>();
String lastError = null;
for (String directory : getLibPaths()) {
String candidate = directory + filename;
candidates.add(candidate);
if (IoUtils.canOpenReadOnly(candidate)) {
String error = doLoad(candidate, loader);
if (error == null) {
return; // We successfully loaded the library. Job done.
}
lastError = error;
}
}
if (lastError != null) {
throw new UnsatisfiedLinkError(lastError);
}
throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
}
Runtime.nativeLoad
private String doLoad(String name, ClassLoader loader) {
String librarySearchPath = null;
if (loader != null && loader instanceof BaseDexClassLoader) {
BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
librarySearchPath = dexClassLoader.getLdLibraryPath();
}
// nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
// of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
// internal natives.
synchronized (this) {
return nativeLoad(name, loader, librarySearchPath);
}
}
有过JNI开发的同学应该熟悉System.loadLibrary这个方法,函数的注册映射就是从这里开始的。最后调用nativeLoad这个native方法,对应jni的函数java_lang_Runtime.cpp中Dalvik_java_lang_Runtime_nativeLoad然后调用vm/ Native.cpp中dvmLoadNativeCode函数
// platform/dalvik2/vm/native/java_lang_Runtime.cpp
static void Dalvik_java_lang_Runtime_nativeLoad(const u4* args,
JValue* pResult) {
StringObject* fileNameObj = (StringObject*) args[0];
Object* classLoader = (Object*) args[1];
StringObject* ldLibraryPathObj = (StringObject*) args[2];
...
bool success = dvmLoadNativeCode(fileName, classLoader, &reason);
}
//platform/dalvik2/+/refs/heads/master/vm/Native.cpp
bool dvmLoadNativeCode(const char* pathName, Object* classLoader,
char** detail)
{
SharedLib* pEntry;
void* handle;
bool verbose;
/* reduce noise by not chattering about system libraries */
verbose = !!strncmp(pathName, "/system", sizeof("/system")-1);
verbose = verbose && !!strncmp(pathName, "/vendor", sizeof("/vendor")-1);
if (verbose)
ALOGD("Trying to load lib %s %p", pathName, classLoader);
*detail = NULL;
/*
* See if we've already loaded it. If we have, and the class loader
* matches, return successfully without doing anything.
*/
// 检查是否已经加载过
pEntry = findSharedLibEntry(pathName);
if (pEntry != NULL) {
//classLoader是否相同,一个so只能由一个classLoader加载
if (pEntry->classLoader != classLoader) {
ALOGW("Shared lib '%s' already opened by CL %p; can't open in %p",
pathName, pEntry->classLoader, classLoader);
return false;
}
if (verbose) {
ALOGD("Shared lib '%s' already loaded in same CL %p",
pathName, classLoader);
}
if (!checkOnLoadResult(pEntry))
return false;
return true;
}
Thread* self = dvmThreadSelf();
ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
// 打开so动态库文件,创建一个handle
handle = dlopen(pathName, RTLD_LAZY);
dvmChangeStatus(self, oldStatus);
if (handle == NULL) {
*detail = strdup(dlerror());
ALOGE("dlopen(\"%s\") failed: %s", pathName, *detail);
return false;
}
/* create a new entry */
SharedLib* pNewEntry;
pNewEntry = (SharedLib*) calloc(1, sizeof(SharedLib));
pNewEntry->pathName = strdup(pathName);
pNewEntry->handle = handle;
pNewEntry->classLoader = classLoader;
dvmInitMutex(&pNewEntry->onLoadLock);
pthread_cond_init(&pNewEntry->onLoadCond, NULL);
pNewEntry->onLoadThreadId = self->threadId;
/* try to add it to the list */
//将SharedLib添加到哈希表中
SharedLib* pActualEntry = addSharedLibEntry(pNewEntry);
if (pNewEntry != pActualEntry) {
ALOGI("WOW: we lost a race to add a shared lib (%s CL=%p)",
pathName, classLoader);
freeSharedLibEntry(pNewEntry);
return checkOnLoadResult(pActualEntry);
} else {
if (verbose)
ALOGD("Added shared lib %s %p", pathName, classLoader);
bool result = true;
void* vonLoad;
int version;
// 查看so是否有JNI_OnLoad函数,有并调用这个函数
vonLoad = dlsym(handle, "JNI_OnLoad");
// vonLoad==NULL 说明so里没有JNI_OnLoad方法
if (vonLoad == NULL) {
ALOGD("No JNI_OnLoad found in %s %p, skipping init",
pathName, classLoader);
} else {
/*
* Call JNI_OnLoad. We have to override the current class
* loader, which will always be "null" since the stuff at the
* top of the stack is around Runtime.loadLibrary(). (See
* the comments in the JNI FindClass function.)
*/
OnLoadFunc func = (OnLoadFunc)vonLoad;
Object* prevOverride = self->classLoaderOverride;
self->classLoaderOverride = classLoader;
oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
if (gDvm.verboseJni) {
ALOGI("[Calling JNI_OnLoad for \"%s\"]", pathName);
}
// 加载JNI_OnLoad方法
version = (*func)(gDvmJni.jniVm, NULL);
dvmChangeStatus(self, oldStatus);
self->classLoaderOverride = prevOverride;
if (version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 &&
version != JNI_VERSION_1_6)
{
ALOGW("JNI_OnLoad returned bad version (%d) in %s %p",
version, pathName, classLoader);
/*
* It's unwise to call dlclose() here, but we can mark it
* as bad and ensure that future load attempts will fail.
*
* We don't know how far JNI_OnLoad got, so there could
* be some partially-initialized stuff accessible through
* newly-registered native method calls. We could try to
* unregister them, but that doesn't seem worthwhile.
*/
result = false;
} else {
if (gDvm.verboseJni) {
ALOGI("[Returned from JNI_OnLoad for \"%s\"]", pathName);
}
}
}
if (result)
pNewEntry->onLoadResult = kOnLoadOkay;
else
pNewEntry->onLoadResult = kOnLoadFailed;
pNewEntry->onLoadThreadId = 0;
/*
* Broadcast a wakeup to anybody sleeping on the condition variable.
*/
dvmLockMutex(&pNewEntry->onLoadLock);
pthread_cond_broadcast(&pNewEntry->onLoadCond);
dvmUnlockMutex(&pNewEntry->onLoadLock);
return result;
}
}
以上步骤:
1.检查so是否已经加载过,如果是检查classLoader是否相同,一个so只能由一个classLoader加载
2.打开so动态库文件,创建一个handle
3.将SharedLib添加到哈希表中
4.查看so是否有JNI_OnLoad函数,有并调用这个函数,vonLoad==NULL 说明so里没有JNI_OnLoad方法
5.有JNI_OnLoad函数这时就会去加载JNI_OnLoad方法
JNI_OnLoad
---RegisterNatives
--------dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
const char* signature, void* fnPtr)
--------------dvmUseJNIBridge(method, fnPtr)-> (method->nativeFunc = func)
最终将JNI函数起始地址保存到ClassObject结构体directMethods中。
struct ClassObject : Object {
/* static, private, and <init> methods */
int directMethodCount;
Method* directMethods;
/* virtual methods defined in this class; invoked through vtable */
int virtualMethodCount;
Method* virtualMethods;
}
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co
如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只
我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时
作为新的阿里云用户,您可以50免费试用多种优惠,价值高达1,700美元(或8,500美元)。这将让您了解和体验阿里云平台上提供的一系列产品和服务。如果您以个人身份注册免费试用,您将获得价值1,700美元的优惠。但是,如果您是注册公司,您可以选择企业免费试用,提交基本信息通过企业实名注册验证,即可开始价值$8,500的免费试用!本教程介绍了如何设置您的帐户并使用您的免费试用版。关于免费试用在我们开始此试用之前,您还必须遵守以下条款和条件才能访问您的免费试用:只有在一年内创建的账户才有资格获得阿里云免费试用。通过此免费试用优惠,用户可以免费试用免费试用活动页面上列出的每种产品一次。如果您有多个帐