java dex全称_java – 在使用多个dex文件时,是否有必要将同一个包的类保留在同一个dex中...

关于标题

“相同包的类”表示共享相同包访问的类.请注意,类a.b.c.Foo没有对a.b.Bar类的包访问权.因为如果前者的修饰符是默认值,后者无法访问前者.

问题

如果我将同一个包中的两个类拆分成两个dex文件,即使我正确加载它们,我也会在运行时遇到一些错误,logcat喜欢:

I/dalvikvm( 6498): DexOpt: illegal method access (call Lcom/fish47/multidex/Foo;.isWholeWord (Lcom/fish47/multidex/Foo;)Z from Lcom/fish47/multidex/TestMatchWord;)

I/dalvikvm( 6498): Could not find method com.fish47.multidex.core.Foo.isWholeWord, referenced from method com.fish47.multidex.core.TestMatchWord.test_english

W/dalvikvm( 6498): VFY: unable to resolve virtual method 758: Lcom/fish47/multidex/Foo;.isWholeWord (Lcom/fish47/multidex/Foo;)Z

推测

/* access allowed? */

tweakLoader(referrer, resMethod->clazz);

bool allowed = dvmCheckMethodAccess(referrer, resMethod);

untweakLoader(referrer, resMethod->clazz);

if (!allowed) {

IF_LOGI() {

char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);

LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n",

resMethod->clazz->descriptor, resMethod->name, desc,

referrer->descriptor);

free(desc);

}

if (pFailure != NULL)

*pFailure = VERIFY_ERROR_ACCESS_METHOD;

return NULL;

}

注意resClass-> classLoader的值.如果这两个类不是来自同一个dex文件,则其值将设置为0xdead3333.

vm/analysis/Optimize.c ==> line: 285 – 299

static void tweakLoader(ClassObject* referrer, ClassObject* resClass)

{

if (!gDvm.optimizing)

return;

assert(referrer->classLoader == NULL);

assert(resClass->classLoader == NULL);

if (!gDvm.optimizingBootstrapClass) {

/* class loader for an array class comes from element type */

if (dvmIsArrayClass(resClass))

resClass = resClass->elementClass;

if (referrer->pDvmDex != resClass->pDvmDex)

resClass->classLoader = (Object*) 0xdead3333;

}

}

这是一个技巧,它让checkAccess(…)方法最后返回false,如果这两个类在同一个包中,彼此可访问但不公开.

vm/oo/AccessCheck.c ==> line: 88 – 116

static bool checkAccess(const ClassObject* accessFrom,

const ClassObject* accessTo, u4 accessFlags)

{

/* quick accept for public access */

if (accessFlags & ACC_PUBLIC)

return true;

/* quick accept for access from same class */

if (accessFrom == accessTo)

return true;

/* quick reject for private access from another class */

if (accessFlags & ACC_PRIVATE)

return false;

/*

* Semi-quick test for protected access from a sub-class, which may or

* may not be in the same package.

*/

if (accessFlags & ACC_PROTECTED)

if (dvmIsSubClass(accessFrom, accessTo))

return true;

/*

* Allow protected and private access from other classes in the same

* package.

*/

return dvmInSamePackage(accessFrom, accessTo);

}

bool dvmInSamePackage(const ClassObject* class1, const ClassObject* class2)

{

...

/* class loaders must match */

if (class1->classLoader != class2->classLoader)

return false;

...

}

解决方法:

这是一个有点奇怪的领域,主要是由于dexopt执行的“预验证”和优化.对于背景,您应该阅读oo/Class.cpp开头的注释(第39-153行).

(注意:文件在ICS中已从“.c”更改为“.cpp”.您可能应该检查当前的来源,但实际上在过去几年中这里没有什么变化.)

一般来说,只要两个DEX文件都由同一个类加载器加载,不同DEX文件中同一个包中的两个类就能够使用包范围相互访问.这就是AccessCheck.cpp中的检查所强制执行的.

您在Optimize.cpp中看到的是解析器的并行实现 – dvmOptResolveClass与dvmResolveClass – 在验证和优化期间使用.它会像你所说的那样调整类加载器,但前提是它在dexopt中运行(这就是检查!gDvm.optimizing意味着什么).如果它位于正常执行的VM实例中,则在检查期间不会调整加载程序.

当作为dexopt的一部分运行时,Optimize.cpp中的代码要么验证优化引导类,要么验证优化单个非引导DEX文件.无论哪种方式,所有DEX文件都是通过引导加载程序加载的,因为VM并没有真正运行,而且它是加载类的唯一方法. (dexopt的目的是在构建或安装时验证尽可能多的类,因此我们不必在应用程序启动时执行此操作.阅读有关dexopt here的更多信息.)

tweakLoader中的代码说:如果我在dexopt中,并且我没有优化实际的bootstrap DEX文件(例如framework.jar),那么我需要确保包范围检查假定当前的类引导类加载器没有加载DEX文件.

例如,我可以在我的应用程序中创建一个名为java.lang.Stuff的类.在dexopt中,因为所有内容都由一个加载器加载,所以如果我们不调整加载器,它将能够触及其他java.lang类中的包私有内容.实际运行应用程序时,java.lang类来自引导加载程序,而Stuff类来自应用程序加载程序,因此应禁止这些访问.

这就是代码的作用.就你的具体问题而言,只要使用相同的加载器加载两个DEX文件,我就希望调用能够正常工作.如果一个DEX由应用程序框架加载,另一个由自定义DexClassLoader加载,那么我不希望它工作.

另外一个注意事项:你粘贴的错误提到com.fish47.multidex.Foo和com.fish47.multidex.core.Foo,它们不是同一个包.我不知道那是否相关.此外,如果有额外的VFY消息,那么包括这些消息是好的,即使它们有点难以理解.对于任何这种性质,同样重要的是指定你正在使用的Android版本 – 它在一段时间内没有改变,但是如果你回到足够远的程度它就会非常不同.

标签:java,android,dalvik,dex,classloader

来源: https://codeday.me/bug/20190624/1282806.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值