在使用多个dex文件时,是否有必要将同一个包的类保留在同一个dex中 [英] Is it necessary to keep classes of the same package in the same dex while using multiple dex files

查看:189
本文介绍了在使用多个dex文件时,是否有必要将同一个包的类保留在同一个dex中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

同一个包的类是指共享相同包访问权限的类。请注意,课程 a.b.c.Foo 没有对 a.b.Bar 类的包访问权限。因为如果前者的修饰符是默认的,后者无法访问前者。

"classes of the same package" means classes that share the same package access. Please pay attention to the fact that class a.b.c.Foo doesn't have package access to class a.b.Bar . Because the latter can't access to the former if the former's modifier is default.

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

If I split two classes in the same packages into two dex files, even though I load them correctly, I will also get some error while running, the logcat likes:


I / dalvikvm(6498):DexOpt:非法方法访问(从Lcom / fish47 /调用Lcom / fish47 / multidex / Foo; .isWholeWord(Lcom / fish47 / multidex / Foo;)Z multidex / TestMatchWord;)

I / dalvikvm(6498):找不到方法com.fish47.multidex.core.Foo.isWholeWord,从方法com.fish47.multidex.core.TestMatchWord.test_english <引用br>
W / dalvikvm(6498):VFY:无法解析虚方法758:Lcom / fish47 / multidex / Foo; .isWholeWord(Lcom / fish47 / multidex / Foo;)Z

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


该代码会弹出以下错误消息:

vm / analysis / Optimize.c ==> line:697 - 714

That's code which pop out the error message below:
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 ==> 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

This is a trick, it lets checkAccess(...) method return false at last, if the two classes are in the same package, accessible to each other but not to public.
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);
}

vm / oo / AccessCheck.c ==> line:39 - 83

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行)。

This is a somewhat strange area, owing largely to the "pre-verification" and optimization performed by dexopt. For background, you should read the comments at the start of oo/Class.cpp (lines 39-153).

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

(Note: the files were changed from ".c" to ".cpp" back in ICS. You should probably be examining current sources, though in practice little has changed here in the last few years.)

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

Generally speaking, two classes in the same package in different DEX files would be able to access each other with package scope so long as both DEX files are loaded by the same class loader. That's what the checks in AccessCheck.cpp enforce.

你在Optimize.cpp中看到的是解析器的并行实现 - dvmOptResolveClass dvmResolveClass - 在验证和优化期间使用。它将调整类加载器,如上所述,但如果它在dexopt中运行(这是对!gDvm.optimizing 的检查意味着什么)。如果它在正常执行的VM实例中,则在检查期间不会调整加载器。

What you're looking at in Optimize.cpp is a parallel implementation of the resolver -- dvmOptResolveClass vs. dvmResolveClass -- that is used during verification and optimization. It will tweak the class loader as you noted, but only if it's running inside dexopt (that's what the check against !gDvm.optimizing means). If it's inside a normally-executing VM instance, the loader is not tweaked during the checks.

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

When running as part of dexopt, the code in Optimize.cpp is either verifying+optimizing the bootstrap classes, or verifying+optimizing a single non-bootstrap DEX file. Either way, all of the DEX files are loaded through the bootstrap loader, because the VM isn't really running and it's the only way to load classes. (The point of dexopt is to verify as many classes as possible at build or install time so we don't have to do it at app startup time. Read more about dexopt here.)

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

The code in tweakLoader says: if I'm in dexopt, and I'm not optimizing an actual bootstrap DEX file (e.g. framework.jar), then I need to make sure that the package-scope checks assume that the classes in the current DEX file are not being loaded by the bootstrap class loader.

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

For example, I could create a class called java.lang.Stuff in my app. In dexopt, because everything is loaded by a single loader, it would be able to touch package-private stuff in other java.lang classes if we didn't tweak the loader. When the app is actually run, the java.lang classes come from the bootstrap loader and the Stuff class comes from the app loader, so those accesses should be forbidden.

这就是代码的作用。就你的具体问题而言,我希望只要使用相同的加载器加载两个DEX文件,调用就会起作用。如果app框架加载了一个DEX,而另一个DEX由自定义 DexClassLoader 加载,那么我不希望它工作。

So that's what the code does. As far as your specific issue goes, I'd expect the calls to work so long as the same loader is used to load both DEX files. If one DEX is loaded by the app framework and the other by a custom DexClassLoader then I wouldn't expect it to work.

另外一个注意事项:您提交的错误提及 com.fish47.multidex.Foo com.fish47.multidex.core。 Foo ,它们不是同一个包。我不知道那是否相关。此外,如果还有其他 VFY 消息,那么即使它们有点难以理解,也可以包含这些消息。对于任何这种性质,同样重要的是指定你正在使用的Android版本 - 它在一段时间内没有改变,但是如果你回到足够远的地方就会有所不同。

One additional note: the errors you pasted in mention both com.fish47.multidex.Foo and com.fish47.multidex.core.Foo, which aren't the same package. I don't know if that's related. Also, if there are additional VFY messages it's good to include those, even if they're a bit unintelligible. And for anything of this nature it's also important to specify what version of Android you're using -- it hasn't changed in a while, but if you go back far enough it's very different.

这篇关于在使用多个dex文件时,是否有必要将同一个包的类保留在同一个dex中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆