Java嵌套parcelable,无法初始化数组

Posted

技术标签:

【中文标题】Java嵌套parcelable,无法初始化数组【英文标题】:Java nested parcelable, can't initialize an array 【发布时间】:2019-06-25 19:33:02 【问题描述】:

好的,我已经阅读了几乎所有关于堆栈溢出的可打包问题,并尝试了多种解决方案来解决这个问题。所以总的来说我有一个android项目,我想通过Intent将2个ArrayLists的数组从一个类发送到另一个类。

注意:这不是嵌套 parcelable 线程 here 的副本,它不处理数组。它不是 this 的副本,因为我已将所有嵌套类都设为 parcelable。它不是 this 或 this 的副本,因为我没有使用数组列表,而且我认识到错误是初始化失败。它不是this 的副本,因为在这种情况下我不需要无参数构造函数(我已经测试过添加一个并且它不会改变错误)。它也不是this 的副本,因为它不是嵌套在包裹中的包裹。

我必须做一些显而易见的事情,因为只有在我的数组尚未初始化时才会出现空指针错误。但是,在我知道我希望它有多长之前,我无法初始化我的数组长度......

(作为旁注,也许有人可以让我更深入地了解readTypedList 类以及XXX.Creator 究竟做了什么,因为这无疑可能是问题的一部分。对于任何未来有同样问题的人here是指向Parcel javadoc 的链接,它提供了丰富的信息,但没有解决我的问题。)

所以现在我有了一个 Display 对象。 Display 对象仅用于存储String[] 并允许对其进行打包。与 断开的行已在下面进行了注释,对于任何有包裹经验的人来说都应该是显而易见的,因为它显然没有被初始化:

class Display implements Parcelable 

    String[] labels;

    public Display(String[] labels) 
        this.labels = labels;
    

    protected Display(Parcel in) 
        in.readStringArray(this.labels); // **** THIS LINE BREAKS
    

    @Override
    public void writeToParcel(Parcel dest, int flags) 
        dest.writeStringArray(this.labels);
    

    @Override
    public int describeContents() 
        return 0;
    

    public static final Creator<Display> CREATOR = new Creator<Display>() 
        @Override
        public Display createFromParcel(Parcel in) 
            return new Display(in);
        

        @Override
        public Display[] newArray(int size) 
            return new Display[size];
        
    ;

所以现在我问自己我能做些什么来解决这个问题,并调查了许多其他有希望名字的线程,但他们没有解决我的问题。

Here 是一个我本来会提供帮助的答案,同样here 他们建议使用createTypedList() 而不是readTypedList()

但是,我尝试了第一个答案和this,但它们没有工作,因为我事先不知道我希望我的String[] 存在多长时间,这最终导致了一个 错误的数组长度错误。第二个答案没有帮助,因为显然我不想创建一个新的类型列表,而是使用我已经拥有的类型列表,所以通过创建一个新列表,我最终得到一个空白列表并丢失了我的数据。

所有这些问题的根源在于我的包裹嵌套在另一个包裹中并通过以下方式调用:

in.readTypedList(allChartLabels, Display.CREATOR);

因为它是嵌套的,所以调用 CREATOR 的方式极大地限制了我的选择,导致我无法解决问题。

在各种测试中,我遇到了许多错误,但没有解决方案...(我的代码当前抛出的错误是 Attempt to get length of null array 错误)。


我知道有人对此问题有解决方案,更多详细信息请参见我的其余代码:

public class SummaryEntry implements Parcelable 

    private ArrayList<Calculation> allChartValues; // NOTE: this is another nested parcel class which is basically a duplicate of Display but with float[]
    private ArrayList<Display> allChartLabels;

    public SummaryEntry() 
        // initialize
        allChartValues = new ArrayList<>();
        allChartLabels = new ArrayList<>();
    

    protected SummaryEntry(Parcel in) 
        this();

        // order in which we do this matters:
        in.readTypedList(allChartLabels, Display.CREATOR);
        in.readTypedList(allChartValues, Calculation.CREATOR);

    

    @Override
    public void writeToParcel(Parcel out, int i) 
        // order in which we do this matters, must be same as reading typed lists
        out.writeTypedList(allChartLabels);
        out.writeTypedList(allChartValues);

    

    /// ... getters and setters excluded because of irrelevance ///


    /// PARCELABLE METHODS

    @Override
    public int describeContents() 
        return 0;
    

    public static final Creator<SummaryEntry> CREATOR = new Creator<SummaryEntry>() 
        @Override
        public SummaryEntry createFromParcel(Parcel in) 
            return new SummaryEntry(in);
        

        @Override
        public SummaryEntry[] newArray(int size) 
            return new SummaryEntry[size];
        
    ;


感谢您抽出宝贵时间阅读这篇长文。理想情况下,我正在寻找String[] 初始化问题的解决方案,或者是否有人可以为包含数组的嵌套包裹发布他们的工作代码,或者最后也许有人可以指出一种更简单的方法来实现传递这些项目而无需嵌套两个包裹.

【问题讨论】:

【参考方案1】:

你可以像下面的代码那样做:

像这样使用 writeArray 和 readArray:

尝试使用以下更新的代码:

package com.myapplication;

import android.os.Parcel;
import android.os.Parcelable;

public class User implements Parcelable 

    String name;
    int age;
    String [] array;

    @Override
    public int describeContents() 
        return 0;
    

    @Override
    public void writeToParcel(Parcel dest, int flags) 
        dest.writeString(this.name);
        dest.writeInt(this.age);
        dest.writeStringArray(this.array);
    

    public User() 
    

    protected User(Parcel in) 
        this.name = in.readString();
        this.age = in.readInt();
        this.array = in.createStringArray();
    

    public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() 
        @Override
        public User createFromParcel(Parcel source) 
            return new User(source);
        

        @Override
        public User[] newArray(int size) 
            return new User[size];
        
    ;

【讨论】:

for( int i = 0; i &lt; size; i++) 中的变量“大小”是什么?不幸的是,我现在不想要我的数组大小...... 感谢您的帮助。但不幸的是,我在这一行也收到错误:a = new String[objects.length];“尝试获取空数组的长度”。这意味着in.readArray(null) 没有找到该项目。 @JFreeman 你可以看到你的程序中断的构造函数的变化; 好的,太好了!谢谢你!【参考方案2】:

我仍然无法解决原始问题的后半部分(即如何包裹数组),但通过稍微重新设计我的程序,我能够成功实现嵌套包裹。

新设计将实现分为 4 个嵌套类,实现简单明了。我还删除了我的数组,因为我无法在包裹中正确初始化它们。

编辑:我还偶然发现了 this website,它会自动为您创建 parcelables。非常有用,我希望我以前知道它! 另请注意,显然没有必要为地块StringFloat 创建地块类。您可以改为调用Float.class.getClassLoader()String.class.getClassLoader() 来为这些类使用parcel CREATORS。

这是我的代码:

Parcelable 类 Q 包含以下 ArrayList:

Parcelable 类 A,其中包含 2 个字符串和浮点数的 Arraylists

通过将包裹分解成更小的嵌套包裹,我能够逐步测试包裹,直到我得到最终的解决方案,该解决方案能够通过我的Intent 通过此代码传递:

在发送端:

    // create the parcel
    Q parcel = new Q();

    ArrayList<String> strings = new ArrayList<>();
    ArrayList<Float> stats = new ArrayList<>();

    // ... populate arraylists ... //

    Intent I = new Intent(CURRENTACTIVITY.this, FUTUREACTIVITY.class);
    // PUT THE THING
    I.putExtra("Data", parcel);

    startActivity(I);

在接收端:

    Q parcel = getIntent().getParcelableExtra("Data"); // get data

工作包裹的完整代码: 这段代码非常长且重复,但设置包裹的基本原则很清楚,如果您想创建自己的包裹,则易于重复使用。

// top level parcelable class Q
public class Q  implements Parcelable 

    private ArrayList<A> element;

    public Q()
    
        this.element = new ArrayList<A>();
    

    public void addToA(A a)
    
        element.add(a);
    

    public ArrayList<A> getA() 
        return element;
    

    public void setA(ArrayList<A> a) 
        this.element = a;
    

    @Override
    public int describeContents() 
        return 0;
    

    @Override
    public void writeToParcel(Parcel dest, int flags) 

        dest.writeTypedList(this.element);

    

    private Q (Parcel in)
        element = new ArrayList<A>();
        in.readTypedList(this.element, A.CREATOR);
    

    public static final Parcelable.Creator<Q> CREATOR = new Parcelable.Creator<Q>() 
        public Q createFromParcel(Parcel in) 
            return new Q(in);
        

        public Q[] newArray(int size) 
            return new Q[size];
        
    ;



// nested parcel object A
public class A implements Parcelable 

    private ArrayList<Float> f;
    private ArrayList<String> s;

    public A() 
        f = new ArrayList<>();
        s = new ArrayList<>();
    

    public A(ArrayList<Float> f, ArrayList<String> s) 
        this.f = f;
        this.s = s;
    

    public ArrayList<String> getS() 
        return s;
    

    public void setS(ArrayList<String> s) 
        this.s = s;
    

    public ArrayList<Float> getF() 
        return f;
    

    public void setF(ArrayList<Float>  f) 
        this.f = f;
    


    protected A(Parcel in) 
        if (in.readByte() == 0x01) 
            f = new ArrayList<>();
            in.readList(f, Float.class.getClassLoader());
         else 
            f = null;
        
        if (in.readByte() == 0x01) 
            s = new ArrayList<>();
            in.readList(s, String.class.getClassLoader());
         else 
            s = null;
        
    

    @Override
    public int describeContents() 
        return 0;
    

    @Override
    public void writeToParcel(Parcel dest, int flags) 
        if (f == null) 
            dest.writeByte((byte) (0x00));
         else 
            dest.writeByte((byte) (0x01));
            dest.writeList(f);
        
        if (s == null) 
            dest.writeByte((byte) (0x00));
         else 
            dest.writeByte((byte) (0x01));
            dest.writeList(s);
        
    

    @SuppressWarnings("unused")
    public static final Parcelable.Creator<A> CREATOR = new Parcelable.Creator<A>() 
        @Override
        public A createFromParcel(Parcel in) 
            return new A(in);
        

        @Override
        public A[] newArray(int size) 
            return new A[size];
        
    ;

【讨论】:

以上是关于Java嵌套parcelable,无法初始化数组的主要内容,如果未能解决你的问题,请参考以下文章

通过 Parcelable 发送嵌套 ArrayList 时出现 NullPointerException

04Java基础语法(循环嵌套breakcontinue方法方法的重载数组动态初始化)

意图getextra中嵌套Parcelable的空指针异常

无法使用 Gson 将包含嵌套 json 数组的 json 转换为等效的 JAVA 类

Java学习之数组

Java编程思想:嵌套类