8

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

这是代码:

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()...创建的

最后creator.createFromParcelPackageInfo

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];
    }
};

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

所以我的问题,

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

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

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

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

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

我只能胡乱猜测=)

4

2 回答 2

0

如果你确定你ClassCastException 正好在这条线上

for (PackageInfo pkgInfo : pkgList)

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

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

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
    }
}

您将能够控制类转换。

此外,您可以抓住 ClassCastException并跳过这个循环步骤到下一个。

于 2012-10-23T11:48:48.010 回答
0

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

问题 26644:ClassCastException 和 IncompatibleClassChangeError

问题 36560:(SensorManager.java:521)中的异常

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

于 2012-10-23T22:20:57.913 回答