【问题标题】:Android ClassCastException with blank type具有空白类型的 Android ClassCastException
【发布时间】:2012-10-12 18:38:57
【问题描述】:

我的行为很奇怪,我想我更多的是在寻找解释而不是解决方案(尽管也欢迎解决方案!)。

代码如下:

PackageManager pm = context.getPackageManager();
List<PackageInfo> pkgList = pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);
if (pkgList == null)
   return null;
for (PackageInfo pkgInfo : pkgList) {
   ApplicationInfo appInfo = pkgInfo.applicationInfo;
   // do some stuff, doesn't modify pkgInfo or appInfo or pkgList
} 

在某些情况下,我会收到以下错误日志:

java.lang.ClassCastException: 无法转换为 android.content.pm.PackageInfo

报告线路:

for (PackageInfo pkgInfo : pkgList)

奇怪的是,ClassCastException 通常看起来像 (AFAIK):

java.lang.ClassCastException: foo.bar.ClassA 无法转换为 foo.bar.ClassB

但是,我看到的错误在第一部分显示为空白。

我决定研究一下,如果返回列表的函数在内部强制转换错误的对象列表并返回它或其他东西,可能会发生这种情况。于是我看了看:

ApplicationPackageManager.getInstalledPackages()

@SuppressWarnings("unchecked")
@Override
public List<PackageInfo> getInstalledPackages(int flags) {
    try {
        final List<PackageInfo> packageInfos = new ArrayList<PackageInfo>();
        PackageInfo lastItem = null;
        ParceledListSlice<PackageInfo> slice;

        do {
            final String lastKey = lastItem != null ? lastItem.packageName : null;
            slice = mPM.getInstalledPackages(flags, lastKey);
            lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR);
        } while (!slice.isLastSlice());

        return packageInfos;
    } catch (RemoteException e) {
        throw new RuntimeException("Package manager has died", e);
    }
}

好的,所以返回的列表是从ParceledListSlice.populateList()... 填充的

ParceledListSlice.populateList()

public T populateList(List<T> list, Creator<T> creator) {
    mParcel.setDataPosition(0);

    T item = null;
    for (int i = 0; i < mNumItems; i++) {
        item = creator.createFromParcel(mParcel);
        list.add(item);
    }

    mParcel.recycle();
    mParcel = null;

    return item;
}

所以项目是从PackageInfo.CREATOR.createFromParcel()...创建的。

最后是PackageInfocreator.createFromParcel

public static final Parcelable.Creator<PackageInfo> CREATOR
        = new Parcelable.Creator<PackageInfo>() {
    public PackageInfo createFromParcel(Parcel source) {
        return new PackageInfo(source);
    }

    public PackageInfo[] newArray(int size) {
        return new PackageInfo[size];
    }
};

所以一切似乎都很好。它正在创建一个PackageInfo 类型的ParceledListSlice,因此在populateList 中它正在创建一个PackageInfo 项目并将其放入ListPackageInfo 中,这是返回的列表。所有类型/类对我来说都很好。

所以我的问题,

  1. 上面的ClassCastException是怎么发生的?
  2. 为什么会显示“空白”类型的错误消息?
  3. 什么是可能的解决方案?

我正在考虑将列表作为Object 的列表并检查“instanceof”,但我认为这也行不通,因为它可能最终会说

ClassCastException: cannot be cast into java.lang.Object" 什么的。

任何关于如何发生这种情况的见解和解释将不胜感激。

  1. Dalvik/JVM 只是搞砸了吗?
  2. 内存是否已损坏?

我只能胡乱猜测 =)

【问题讨论】:

  • 如果您尝试手动遍历列表并打印出每个项目的类型怎么办?
  • @NathanVillaescusa 这是来自客户设备的日志,所以我不能这样做。它也不是 100% 可重现的。而且,我不认为我可以遍历每个项目并打印出类型,因为当我尝试获取该项目时,我认为它可能会抛出 ClassCastException。
  • 我在想一个传统的for loop,你循环到pkgList的长度。这将告诉您列表中的某些项目是否属于 PackageInfo 类。
  • 我猜你的意思是做一个 for (int i = 0; i
  • 无论如何,这不是我可以重现的问题,并且日志来自客户设备,所以我不能真正放置一些调试日志并将其推送并返回日志=(尽管感谢您的建议=) 我会很高兴从上面给定的代码中理解这样的日志是如何可能的。

标签: java android exception classcastexception android-package-managers


【解决方案1】:

如果你确定你在这条线上得到了ClassCastException

for (PackageInfo pkgInfo : pkgList)

当一个原因出现时 - 不知何故 pkgList 包含一个非 PackageInfo 类的值。我假设这是NULL 值。为什么在 ClassCast 错误消息中看不到它,因为 NULL 对象的 className 只是空字符串。

现在,如何解决这个问题 - 使用手动 for 循环。 for each 循环在后台使用 Iterator,因此您无法控制每次迭代的类转换。但是如果你会使用这样的代码:

for (int i = 0; i < pkgList.length; i++) {
    Object pkgInfoObj = pkgList.get(i);
    if (pkgInfoObj instanceof PackageInfo) {
        PackageInfo pkgInfo = (PackageInfo) pkgInfoObj;
        ApplicationInfo appInfo = pkgInfo.applicationInfo;
        // do some stuff, doesn't modify pkgInfo or appInfo or pkgList
    }
}

您将能够控制类转换。

此外,您可以catch ClassCastException,然后跳过这个循环步骤到下一个。

【讨论】:

  • 感谢您的回答。我一开始也考虑过这个。但是,有几件事我想回顾一下。您可以通过执行 pkgList.add(null) 将 null 添加到 pkgList,并且迭代它不会有问题(除非您尝试取消引用 null 项,但在这种情况下它将获得 NPE)。而且,从我粘贴的代码中可以看出,populateList() 函数从不添加空项。
  • 采取不同的方法,如果你说 pkgInfo 是某种 NULL 类型并且没有与之关联的类型,我认为 for 循环仍然不起作用。在“Object pkgInfoObj = pkgList.get(i);”中,它可能会说“ClassCastException : cannot be cast into java.lang.Object”,因为它没有类型。另外,如果它是对象类型,我想我最初会在原始日志中得到“ClassCastException:java.lang.Object 无法转换为 android.content.pm.PackageInfo”。感谢您的意见,但我想更好地了解正在发生的事情 =)
  • Object 是 Java 中所有类的超类,因此任何对象都可以转换为类型 Object
  • 所以看起来一个不正确类型的对象正试图被转换成一个 PackageInfo 对象。该对象如何进入 List 似乎是 Android 方面的一个错误,如错误报告所示。是的,如果添加了不同类型的空对象,错误日志可能会显示空字符串,因为您将无法在空对象上运行 getClass()。在这种情况下,您的 for 循环将起作用,因为实际上有一个错误类型的空对象添加到列表中。
【解决方案2】:

这似乎是 Android 本身的问题。我发现了一些针对 Android 的错误报告,它们表现出类似的行为:

Issue 26644: ClassCastException and IncompatibleClassChangeError

Issue 36560: Exception in (SensorManager.java:521)

我还看到了问题 26644 中提到的 IncompatibleClassChangeError。我唯一的猜测是,这些看似不可能的异常/错误是由于一些奇怪的内存损坏或 dalvik/jvm 问题造成的。代码本身似乎没有任何问题。如果其他人有更好的解释,请随时加入 =)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-18
    • 1970-01-01
    • 2010-10-22
    相关资源
    最近更新 更多