android4.4 Jni函数查找失败问题处理(java.lang.UnsatisfiedLinkError: Native method not found)

Jni函数查找失败问题分析

1、 问题点描述:

系统jni函数查找报错java.lang.UnsatisfiedLinkError: Native method not found。通常的原因是函数书写错误,但是在肯定函数没有问题的情况下,偶尔还会出现报错的问题。

2、 问题点原因分析:

1 经过log分析问题点发生时,软件在调用mcu的指令发送函数McuDaemon.write0,系统查找不到此函数在Jni层内的具体实现,故抛出函数查找不到的错误。

E/AndroidRuntime( 2471): java.lang.UnsatisfiedLinkError: Native method not found: com.soling.uart.McuDaemon.write0:([B)V

2 此函数在Jni层有正常实现,除了概率性报错外,其他场景下都能够正常运行。故不是软件代码编写的问题,而是系统函数查找检索出现问题。

3 继续分析log,在问题出现时,在另外一个线程有其他的库方法被调用到。推测为两个库的方法同时调用有冲突。

I/sysdata jni( 2471): sysdata_opera --->func:int private_store_write(const char*, const char*) write 340=128
I/sysdatajni(2471):sysdata_opera--->func:jint com_soling_emc_test_FileSystemDataSave_native_setdata(JNIEnv*, jobject, jstring, jstring) [private_data_write] :Write private data fail


4 分析源码,抛出函数查找不到的错误在android/dalvik/vm/Native.cpp第129行:

    /* 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);


由上述代码可以看到,lookupSharedLibMethod时获取到的函数为空。


5 系统查找检索的方法分析

函数如下:

/*
 * See if the requested method lives in any of the currently-loaded
 * shared libraries.  We do this by checking each of them for the expected
 * method signature.
 */
static void* lookupSharedLibMethod(const Method* method)
{
    if (gDvm.nativeLibs == NULL) {
        ALOGE("Unexpected init state: nativeLibs not ready");
        dvmAbort();
    }

    return (void*) dvmHashForeach(gDvm.nativeLibs, findMethodInLib,
        (void*) method);
}

6 dvmHashForeach 是HashTable中轮询函数的方法,findMethodInLib为从一个so库中查找函数的方法。可以看出系统加载每个程序的so库时,会保存在对应的一个HashTable中,java层调用native方法时,会遍历HashTable中保存的so库信息,来查找对应的jni函数。分析此流程可以看出,当问题出现时,无法从HashTable的so库中查找到对应的函数,但是此时可以看出对应的so库已经加载,但是检索函数没有查找对应的so库。故继续分析dvmHashForeach函数。

7 dvmHashForeach函数为:

/*
 * Scan every entry in the hash table and evaluate it with the specified
 * indirect function call. If the function returns 1, remove the entry from
 * the table.
 *
 * Does NOT invoke the "free" function on the item.
 *
 * Returning values other than 0 or 1 will abort the routine.
 */
int dvmHashForeachRemove(HashTable* pHashTable, HashForeachRemoveFunc func)
{
    int i, val, tableSize;

    tableSize = pHashTable->tableSize;

    for (i = 0; i < tableSize; i++) {
        HashEntry* pEnt = &pHashTable->pEntries[i];

        if (pEnt->data != NULL && pEnt->data != HASH_TOMBSTONE) {
            val = (*func)(pEnt->data);
            if (val == 1) {
                pEnt->data = HASH_TOMBSTONE;
                pHashTable->numEntries--;
                pHashTable->numDeadEntries++;
            }
            else if (val != 0) {
                return val;
            }
        }
    }
    return 0;
}

结合日志可以看到HashTable在轮询过程中,HashTable被外部线程改变,长度发生变化,导致so库没有查找完全。

3、 问题原因总结:

1、系统加载每个程序的so库时,会保存在对应的一个HashTable中。每次程序调用loadLibrary 时,hashTable会插入so库的信息,长度会根据情况发生变化.

2、java层调用native方法时,会遍历HashTable中保存的so库信息,来查找对应的jni函数。

3、当查找jni函数时,如果HashTable发生长度变化,so库在table中的信息可能发生位置偏移,但系统中dalvik使用的遍历HashTable的方法并不会更新遍历的长度,可能导致无法查找到对应库的jni函数。

4、 问题对策:

为了避免查找jni函数和加载so库两种动作冲突导致的jni函数查找失败,故程序开始运行时统一加载所有的so库。

5、结论:

软件设计过程中要多思考程序执行存在的不确定性,虽然有些时序发生的概率非常之小,也要从细节上提升软件的可靠性、准确性、稳定性。之所以会出现此次报错,归根结底是android系统本身的问题,但是在不能完全更换系统的情况下,必须针对不同场景,使用特定的方法来适配系统。


另:通过搜索此问题,找到google的dalvik虚拟机针对此查询特别将遍历hashtable的方法由动态长度改为静态长度,可能是针对某问题而改动,但由此引发了报错的隐患,故不进行源码修改,而从策略上来做规避。

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值