简体   繁体   English

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

[英]Is it necessary to keep classes of the same package in the same dex while using multiple dex files

About the Title 关于标题

"classes of the same package" means classes that share the same package access. “相同包的类”表示共享相同包访问的类。 Please pay attention to the fact that class abcFoo doesn't have package access to class abBar . 请注意,类abcFoo没有类abBar的包访问 Because the latter can't access to the former if the former's modifier is default. 因为如果前者的修饰符是默认值,后者无法访问前者。

Problem 问题

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: 如果我将同一个包中的两个类拆分成两个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):DexOpt:非法方法访问(从Lcom / fish47 / multidex / TestMatchWord调用Lcom / fish47 / multidex / Foo; .isWholeWord(Lcom / fish47 / multidex / Foo;)Z;)
I/dalvikvm( 6498): Could not find method com.fish47.multidex.core.Foo.isWholeWord, referenced from method com.fish47.multidex.core.TestMatchWord.test_english I / dalvikvm(6498):找不到方法com.fish47.multidex.core.Foo.isWholeWord,从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 W / dalvikvm(6498):VFY:无法解析虚方法758:Lcom / fish47 / multidex / Foo; .isWholeWord(Lcom / fish47 / multidex / Foo;)Z


Conjecture 推测

That's code which pop out the error message below: 该代码会弹出以下错误消息:
vm/analysis/Optimize.c ==> line: 697 - 714 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;
}


Pay attention to the value of resClass->classLoader . 注意resClass-> classLoader的值。 If the two classes aren't from the same dex file, its value will be set 0xdead3333 . 如果这两个类不是来自同一个dex文件,则其值将设置为0xdead3333
vm/analysis/Optimize.c ==> line: 285 - 299 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;
    }
}


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. 这是一个技巧,它让checkAccess(...)方法最后返回false,如果这两个类在同一个包中,彼此可访问但不公开。
vm/oo/AccessCheck.c ==> line: 88 - 116 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 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;

    ...
} 

This is a somewhat strange area, owing largely to the "pre-verification" and optimization performed by dexopt. 这是一个有点奇怪的领域,主要是由于dexopt执行的“预验证”和优化。 For background, you should read the comments at the start of oo/Class.cpp (lines 39-153). 对于背景,您应该阅读oo / Class.cpp开头的注释 (第39-153行)。

(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.) (注意:文件在ICS中已从“.c”更改为“.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. 一般来说,只要两个DEX文件都由同一个类加载器加载,不同DEX文件中同一个包中的两个类就能够使用包范围相互访问。 That's what the checks in AccessCheck.cpp enforce. 这就是AccessCheck.cpp中的检查所强制执行的。

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. 您在Optimize.cpp中看到的是解析器的并行实现 - dvmOptResolveClassdvmResolveClass - 在验证和优化期间使用。 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). 它会调整的类加载器,你提到的,但只有当它里面dexopt运行(这是对检查!gDvm.optimizing意思)。 If it's inside a normally-executing VM instance, the loader is not tweaked during the checks. 如果它位于正常执行的VM实例中,则在检查期间不会调整加载程序。

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

The code in tweakLoader says: if I'm in dexopt, and I'm not optimizing an actual bootstrap DEX file (eg 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. tweakLoader的代码说:如果我在tweakLoader中,并且我没有优化实际的bootstrap DEX文件(例如framework.jar),那么我需要确保包范围检查假定当前的类引导类加载器没有加载DEX文件。

For example, I could create a class called java.lang.Stuff in my app. 例如,我可以在我的应用程序中创建一个名为java.lang.Stuff的类。 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. 在dexopt中,因为所有内容都由一个加载器加载,所以如果我们不调整加载器,它将能够触及其他java.lang类中的包私有内容。 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. 实际运行应用程序时, java.lang类来自引导加载程序,而Stuff类来自应用程序加载程序,因此应禁止这些访问。

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. 就你的具体问题而言,只要使用相同的加载器加载两个DEX文件,我就希望调用能够正常工作。 If one DEX is loaded by the app framework and the other by a custom DexClassLoader then I wouldn't expect it to work. 如果一个DEX由应用程序框架加载,另一个由自定义DexClassLoader那么我不希望它工作。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM