突然缺少Android 4上的许多类

时间:2018-09-28 01:46:06

标签: java android dalvik

我的应用程序在Android 4.0.3及更高版本上一直运行良好。通过最近的次要更新,它突然开始在Android 4.x设备上崩溃。当我在Android 4上对其进行调试时,它可以很好地构建和安装,但是从

开始,控制台会出现很多错误。
W/dalvikvm: VFY: unable to find class referenced in signature (Landroid/media/midi/MidiDeviceInfo;)
I/dalvikvm: Failed resolving Lcom/arlomedia/myapp/App$4; interface 113 'Landroid/media/midi/MidiManager$OnDeviceOpenedListener;'
W/dalvikvm: Link of class 'Lcom/arlomedia/myapp/App$4;' failed
E/dalvikvm: Could not find class 'com.arlomedia.myapp.App$4', referenced from method com.arlomedia.myapp.App.addMidiPorts
W/dalvikvm: VFY: unable to resolve new-instance 447 (Lcom/arlomedia/myapp/App$4;) in Lcom/arlomedia/myapp/App;
D/dalvikvm: VFY: replacing opcode 0x22 at 0x0007

,然后在我的应用程序中的许多类上出现类似的错误。该应用程序将运行,直到需要使用这些缺少的类之一,然后崩溃。

我已经阅读了几篇与Android 4的64K方法限制有关的类似问题,可以想象,在最近的更新中,我碰巧越过了64K阈​​值。因此,我尝试根据these instructions设置multidex,但这没有什么区别,我也不知道如何判断它是否有效。构建APK文件并检查其内容时,我仅看到一个classes.dex文件。这可能意味着我的multidex设置无法正常工作,或者我的方法不超过64K,而我的问题出在其他方面。

我的第二个理论是,在Android 6中添加的MidiDeviceInfo类的使用在Android 4上引起了问题。但是,我使用版本检查和@TargetApi(24)注释来忽略MIDI代码在Android 4-5上。而且这个问题在Android 5上不会发生。但是奇怪的是,这是每次都会弹出的第一个错误。

这些理论之一听起来是否正确,或者听起来像是其他问题?

顺便说一句,“无法找到签名中引用的类”是什么意思-方法签名?我确实有一些方法使用MidiDeviceInfo类型的参数,但是我用@TargetApi(24)注释了所有方法,并且编译没有问题。

更新

借助homerman的技巧,我可以看到方法的数量不是问题。这使我回到MIDI兼容性上,但是我正在查看与以前版本的差异,并且我看不到MIDI代码的任何更改。我还要寻找什么?

更新2

自上一个版本以来,我在差异中找不到任何相关的内容,因此我从VCS中检出了最后一个版本并运行它,它运行正常。但是我在该版本的logcat中注意到MIDI类的所有相同错误。因此,看来这些并不是真正的问题的一部分。我想这就是Android遇到新类时所做的事情。我一直在比较上一个好版本和当前版本之间的logcat,然后看到了不同之处:我最近在一个类中添加了一个OnScrollChangeListener。这就产生了另一批丢失的类错误,这些错误不是上一个应用程序版本中的。当我删除该新功能时,该应用程序又可以在Android 4上正常运行。

这是问题所在:我有一个这样的类定义:

public class DocumentViewer extends RelativeLayout implements View.OnScrollChangeListener {

    if (Build.VERSION.SDK_INT >= 23) {
        textView.setOnScrollChangeListener(this);
    }

    @Override
    public void onScrollChange(final View scrollView, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
        Log.d("onScrollChange", "scroll changed: " + scrollY);
    }

}

显然,类定义中View.OnScrollChangeListener的存在是在弄乱一切。有没有办法声明仅适用于受支持的Android版本?

显然,Android 5会优雅地忽略此不受支持的引用,但Android 4不会。

2 个答案:

答案 0 :(得分:2)

在API级别23中添加了View.OnScrollChangeListener接口。这意味着您的类定义已经是一个问题;如果此类在23岁之前加载,您将崩溃。

解决方案是创建一个公共父级(可能是一个接口)和该父级的两个具体实现:一个用于23之前的版本,一个用于23+的版本。然后使用工厂基于当前运行的设备获取适当的实现。

public interface DocumentViewer {
    // interface methods
}
public class DocumentViewPre23 extends RelativeLayout implements DocumentViewer {
    // interface methods
}
public class DocumentViewer23 extends RelativeLayout implements DocumentViewer, View.OnScrollChangeListener {
    // interface methods
}

然后您可能会遇到类似的情况

public static DocumentViewer getDocumentViewer() {
    if (Build.VERSION.SDK_INT >= 23) {
        return new DocumentViewer23();
    } else {
        return new DocumentViewerPre23();
    }
}

Android支持库经常这样做,这是值得的。这是一个来自ActionBarDrawerToggle的示例,该示例根据API版本在两种实现之间切换:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
    mActivityImpl = new JellybeanMr2Delegate(activity);
} else {
    mActivityImpl = new IcsDelegate(activity);
}
public interface Delegate {
    ...
}
@RequiresApi(18)
private static class JellybeanMr2Delegate implements Delegate {
    ...
}
private static class IcsDelegate implements Delegate {
    ...
}

答案 1 :(得分:0)

Ben P的回答使我处在正确的轨道上,但是我对将类转换为接口感到困惑,所以这是我想出的:

// this was the original class and I added scrollViewDidScroll() to keep all my related code in one class
public class DocumentViewer extends RelativeLayout {

    if (Build.VERSION.SDK_INT >= 23) {
        if (this instanceof DocumentViewerExtras) {
            // assign the listener on supported devices
            DocumentViewerExtras thisExtras = (DocumentViewerExtras)this;
            textView.setOnScrollChangeListener(thisExtras);
        }
    }

    public void scrollViewDidScroll(final View scrollView, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
        // do something with the scroll results
    }

}

// this is a new class that adds OnScrollChangeListener on supported devices
@RequiresApi(23)
public class DocumentViewerExtras extends DocumentViewer implements View.OnScrollChangeListener {

    public DocumentViewerExtras(Fragment fragment) {
        super(fragment);
    }

    @Override
    public void onScrollChange(final View scrollView, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
        this.scrollViewDidScroll(scrollView, scrollX, scrollY, oldScrollX, oldScrollY);
    }

}

// this method instantiates the best available class
public DocumentViewer getDocumentViewer(Fragment fragment) {
    if (Build.VERSION.SDK_INT >= 23) {
        return new DocumentViewerExtras(fragment);
    } else {
        return new DocumentViewer(fragment);
    }
}

// this is how I get a new instance of the class
DocumentViewer documentViewer = getDocumentViewer(this);