关于标题
“同一包的类”是指共享相同包访问权限的类。请注意abcFoo类没有对abBar类的包访问权限这一事实。因为如果前者的修饰符是默认的,则后者无法访问前者。
问题
如果我将同一个包中的两个类拆分成两个 dex 文件,即使我正确加载它们,运行时也会出现一些错误,logcat 喜欢:
I/dalvikvm(6498):DexOpt:非法方法访问(从 Lcom/fish47/multidex/TestMatchWord 调用 Lcom/fish47/multidex/Foo;.isWholeWord (Lcom/fish47/multidex/Foo;)Z;)
I/dalvikvm(6498 ):找不到方法 com.fish47.multidex.core.Foo.isWholeWord,引用自方法 com.fish47.multidex.core.TestMatchWord.test_english
W/dalvikvm(6498):VFY:无法解析虚拟方法 758:Lcom/ fish47/multidex/Foo;.isWholeWord (Lcom/fish47/multidex/Foo;)Z
推测
这是弹出以下错误消息的代码:
vm/analysis/Optimize.c ==> line: 697 - 714
/* 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 ==> 行: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 ==> 行: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);
}
vm/oo/AccessCheck.c ==> 行:39 - 83
bool dvmInSamePackage(const ClassObject* class1, const ClassObject* class2)
{
...
/* class loaders must match */
if (class1->classLoader != class2->classLoader)
return false;
...
}