具有空白类型的Android ClassCastException

时间:2012-10-22 21:26:00

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

我得到一个奇怪的行为,我想我更需要解释而不是解决方案(虽然也欢迎解决方案!)。

以下是代码:

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.createFromParcel

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

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

所以我的问题,

  1. 上述ClassCastException会如何发生?
  2. 为什么错误消息会显示“空白”类型?
  3. 什么是可能的解决方案?
  4. 我在考虑将列表作为Object的列表并检查“instanceof”,但我认为这不会起作用,因为它可能最终会说

      

    ClassCastException:无法转换为java.lang.Object“或其他东西。

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

    1. Dalvik / JVM简直搞砸了吗?
    2. 内存是否已损坏?
    3. 我只能猜测猜测=)

2 个答案:

答案 0 :(得分:0)

如果您确定在此行中获得了ClassCastException

for (PackageInfo pkgInfo : pkgList)

当出现原因时 - 某种程度上pkgList包含的值不是PackageInfo类。我假设这是NULL值。为什么你在ClassCast错误消息中看不到它,因为NULL对象的className只是空字符串。

现在,如何解决这个问题 - 使用手动循环。 for aech循环使用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
    }
}

您将能够控制类投射。

此外,您可以抓住 ClassCastException,只需跳过此循环步骤即可。

答案 1 :(得分:0)

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

Issue 26644: ClassCastException and IncompatibleClassChangeError

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

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