【问题标题】:I don't get why this ClassCastException occurs我不明白为什么会发生这个 ClassCastException
【发布时间】:2012-01-05 16:22:23
【问题描述】:
// Application ...
Intent i = new Intent();
i.putExtra(EXTRA_FILE_UPLOAD_URIS, mGalleryAdapter.getItems()); 

Uri[] getItems() { return mItems; }

// Service ...
intent.getParcelableArrayExtra(EXTRA_FILE_UPLOAD_URIS); //works, returns Parcelable[]
Uri[] uris = (Uri[])intent.getParcelableArrayExtra(EXTRA_FILE_UPLOAD_URIS);
// ... Breaks with ClassCastException

UriParcelable 时,为什么转换为Uri[] 会中断?

【问题讨论】:

  • Uri 是继承自 Parcelable 还是相反?

标签: java android serialization android-intent parcelable


【解决方案1】:

用这个方法,对我有用。

Parcelable[] ps = getIntent().getParcelableArrayExtra();
Uri[] uri = new Uri[ps.length];
System.arraycopy(ps, 0, uri, 0, ps.length);

【讨论】:

    【解决方案2】:

    不幸的是,没有办法像 Java 中的数组那样进行强制转换。您将不得不迭代您的数组并单独转换每个对象。

    原因是安全类型,JVM 根本无法确保可以将数组的内容转换为 Uri,而不必遍历它们,这就是为什么您必须迭代它们并单独转换它们的原因。

    基本上因为Parcelable 可以被其他对象继承,所以不能保证数组只包含Uri 对象。但是,转换为超类型会起作用,因为那时类型安全就可以了。

    【讨论】:

    • 我也是这么想的(因为在 C# 中就是这样),所以我查找了 java 数组协方差,它似乎得到了支持(并且是类型安全的一个漏洞)。我的参考资料与当前的 JVM 相比是否已经过时? : angelikalanger.com/Articles/Papers/JavaGenerics/… , c2.com/cgi/wiki?JavaArraysBreakTypeSafety
    • 我认为类型安全与泛型有关,仅限于编译时。数组未生成。它们是参考,您可以进行投射。
    • 对,但是 Parcelable[] 最初是一个新的 Uri[] ......所以即使 Uri 是 Parcelable 但 Uri[] 不是 Parcelable[] 也一定是这样。 ..
    • @KevinCoulombe:如果该方法返回类似Parcelable[] parcelables = new Uri[]{}; 的内容,那么Uri[] uris = (Uri[])intent.getParcelableArrayExtra(EXTRA_FILE_UPLOAD_URIS); 绝对不会在运行时使用CCE 失败。尽管Parcelable 是一个接口,但即使当时是一个类,array[0] = new Parcelable() 在运行时也会以ArrayStoreException 失败。而且我不知道循环遍历整个数组并转换每个单独的元素时会有什么不同。
    • @βнɛƨнǤʋяʋиɢ 对我来说也没有意义,但循环元素有效
    【解决方案3】:

    数组确实具有多态行为——只有泛型类型没有。

    也就是说,如果Uri实现Parcelable那么

    你可以说:

    Parcelable[] pa = new Uri[size];
    Uri[] ua = (Uri[]) pa;
    

    你不能说:

    List<Parcelable> pl = new ArrayList<Uri>();
    

    如您所见,我们可以将pa 转换回Uri[]。那么问题是什么?这个ClassCastException 发生在您的应用程序被终止并且稍后重新创建保存的数组时。当它被重新创建时,运行时不知道它是什么类型的数组 (Uri[]),所以它只是创建一个 Parcelable[] 并将元素放入其中。因此,当您尝试将其转换为 Uri[] 时,ClassCastException

    请注意,当进程没有被杀死并且最初创建的数组 (Uri[]) 在状态保存/恢复轮次之间重用时,异常不会发生(理论上)。就像你改变方向一样。

    我只是想弄清楚为什么会这样。如果您想要一个解决方案,@solo 提供了一个不错的解决方案。

    干杯

    【讨论】:

    • 这是正确的答案。当我的服务尝试将 Parcelable[] 从 AccountManager 的 Bundle 转换为 Account[] 时,我偶然发现了偶尔重新出现的 ClassCastException。打电话后很少发生这种情况,最后 - 这是罪魁祸首!
    【解决方案4】:

    我认为正在发生的事情如下:

    class Parent { }
    
    class MaleParent extends Parent { }
    
    class FemaleParent extends Parent { }
    

    如果场景如上,则以下将在运行时失败:

    Parent[] parents = new FemaleParent[]{};
    MaleParent[] maleParents = (MaleParent[]) parents;
    

    以下内容不会引发异常:

    Parent[] parents = new MaleParent[]{};
    MaleParent[] maleParents = (MaleParent[]) parents;
    

    【讨论】:

      【解决方案5】:

      https://stackoverflow.com/a/8745966/72437https://stackoverflow.com/a/20073367/72437 很好地解释了为什么会发生这种崩溃。

      https://stackoverflow.com/a/14866690/72437 也有一个示例说明我们如何解决这个问题。

      我想提供代码示例,以帮助更好地理解。

      让我举例说明为什么此类事件有时会失败。

      举例说明为什么会发生这种崩溃

      package javaapplication12;
      
      /**
       *
       * @author yccheok
       */
      public class JavaApplication12 {
      
          public static class Parcelable {
      
          }
      
          public static class Uri extends Parcelable {
      
          }
      
          public static Parcelable[] getParcelableArrayExtraDuringLowMemoryRestoration() {    
              // The Android system has no way to know it needs to create Uri[],
              // during low memory restoration process.
      
              Parcelable[] parcelables = new Parcelable[3];
      
              for (int i=0; i<parcelables.length; i++) {
                  parcelables[i] = new Uri();
              }
      
              return parcelables;
          }
      
          public static Parcelable[] getParcelableArrayExtra() {        
              // The Android system has enough information that it needs to create Uri[]
      
              Uri[] temp = new Uri[3];
              for (int i=0; i<temp.length; i++) {
                  temp[i] = new Uri();
              }
              Parcelable[] parcelables = temp;
              return parcelables;
          }
      
          /**
           * @param args the command line arguments
           */
          public static void main(String[] args) {
              // OK
              {
                  // true
                  System.out.println(getParcelableArrayExtra() instanceof Uri[]);
      
                  Uri[] uris = (Uri[])getParcelableArrayExtra();
                  for (Uri uri : uris) {
                      System.out.println(uri);
                  }
              }
      
              // Crash!
              {
                  // false
                  System.out.println(getParcelableArrayExtraDuringLowMemoryRestoration() instanceof Uri[]);
      
                  // ClassCastException.
                  Uri[] uris = (Uri[])getParcelableArrayExtraDuringLowMemoryRestoration();         
                  for (Uri uri : uris) {
                      System.out.println(uri);
                  }
              }
          }    
      }
      

      一个示例来演示我们如何解决这个问题

      package javaapplication12;
      
      /**
       *
       * @author yccheok
       */
      public class JavaApplication12 {
      
          public static class Parcelable {
      
          }
      
          public static class Uri extends Parcelable {
      
          }
      
          public static Parcelable[] getParcelableArrayExtraDuringLowMemoryRestoration() {    
              // The Android system has no way to know it needs to create Uri[],
              // during low memory restoration process.
              Parcelable[] parcelables = new Parcelable[3];
      
              for (int i=0; i<parcelables.length; i++) {
                  parcelables[i] = new Uri();
              }
      
              return parcelables;
          }
      
          public static Parcelable[] getParcelableArrayExtra() {        
              // The Android system has enough information that it needs to create Uri[]
              Uri[] temp = new Uri[3];
              for (int i=0; i<temp.length; i++) {
                  temp[i] = new Uri();
              }
              Parcelable[] parcelables = temp;
              return parcelables;
          }
      
          private static Uri[] safeCastToUris(Parcelable[] parcelables) {
              if (parcelables instanceof Uri[]) {
                  return (Uri[])parcelables;
              }
              int length = parcelables.length;
              Uri[] uris = new Uri[length];
              System.arraycopy(parcelables, 0, uris, 0, length);
              return uris;
          }
      
          /**
           * @param args the command line arguments
           */
          public static void main(String[] args) {
              // OK
              {
                  Uri[] uris = safeCastToUris(getParcelableArrayExtra());
                  for (Uri uri : uris) {
                      System.out.println(uri);
                  }
              }
      
              // OK too!
              {
                  Uri[] uris = safeCastToUris(getParcelableArrayExtraDuringLowMemoryRestoration());            
                  for (Uri uri : uris) {
                      System.out.println(uri);
                  }
              }
          }    
      }
      

      【讨论】:

        猜你喜欢
        • 2012-10-18
        • 2021-10-24
        • 2016-01-24
        • 2020-03-28
        • 2018-01-27
        • 2016-02-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多