Android TransactionTooLargeException при передаче Parcelable ArrayList - PullRequest
0 голосов
/ 08 марта 2019

Я занимаюсь разработкой приложения для Android-викторины (я новичок). Итак, у меня есть абстрактный класс «Вопросы» и три подкласса для типов вопросов (несколько ответов, вопрос с изображением и истинные / ложные вопросы). Я читаю свои вопросы из трех разных файлов CSV и затем сохраняю их в ArrayList<Questions>.

Я искал, как передать ArrayList из действия в другое. Я обнаружил, что мы можем сделать это с помощью getSerializableExtra. Я пытался сделать это, но потом понял, что не смогу сделать это из-за моего Drawable. Поэтому я реализовал класс Parcelable, чтобы сделать это.

Но теперь, когда я запускаю новое действие, оно сбрасывается из-за исключения TransactionTooLargeException, и я не знаю, что делать. Я искал ответы и читал много сообщений о переполнении стека, но не нашел решений. Многие использовали Fragment, но я им не пользуюсь. Можно ли передать мой arraylist другим действиям?

Вот код

Questions.java

public abstract class Questions implements Parcelable {

    public Questions() {
        super();
    }

    protected Questions(Parcel in) {
        super();
    }

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

    public abstract Themes getTheme();

    public abstract String getQuestion();

    public abstract String getAnswer();

    public abstract boolean isValid(String answer);

    public abstract String[] getAnswers();

    public abstract Drawable getImage();

    public abstract int getType();

    public abstract String toString();
}

MultipleQuestion.java

public class MultipleQuestion extends Questions {

    private Themes theme;
    private String question;
    private String[] answers;
    private int idGood;

    public MultipleQuestion(Themes theme, String question, String answer, String wrong_one, String wrong_two, String wrong_three) {
        this.theme = theme;
        this.question = question;
        this.answers = new String[]{answer, wrong_one, wrong_two, wrong_three};

        // The different answers are shuffle directly here and the position of the good answer is saved
        Collections.shuffle(asList(answers));
        for (int i = 0; i < answers.length; i++) {
            if (answers[i].equals(answer)) {
                idGood = i;
            }
        }
    }

    protected MultipleQuestion(Parcel in) {
        super(in);
        theme = Themes.valueOf(in.readString());
        question = in.readString();
        answers = in.createStringArray();
        idGood = in.readInt();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.theme.name());
        dest.writeString(question);
        dest.writeStringArray(answers);
        dest.writeInt(idGood);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<MultipleQuestion> CREATOR = new Creator<MultipleQuestion>() {
        @Override
        public MultipleQuestion createFromParcel(Parcel parcel) {
            return new MultipleQuestion(parcel);
        }

        @Override
        public MultipleQuestion[] newArray(int i) {
            return new MultipleQuestion[i];
        }
    };

    public Themes getTheme() {
        return theme;
    }

    public String getQuestion() {
        return question;
    }

    public String getAnswer() {
        return answers[idGood];
    }

    public boolean isValid(String answer) {
        return answer.equals(answers[idGood]);
    }

    public String[] getAnswers() {
        return answers;
    }

    public Drawable getImage() {
        return null;
    }

    public int getType() {
        return 1;
    }

    public String toString() {
        String res = "";
        res += "Theme : " + theme.toString() + "\n";
        res += "Question : " + question + "\n";
        res += "Answers : " + answers[0] + " ; " + answers[1] + " ; " + answers[2] + " ; " + answers[3] + "\n";
        res += "Good answer : " + answers[idGood] + "\n";
        return res;
    }
}

ImageQuestion.java

public class ImageQuestion extends Questions {

    private Themes theme;
    private String question;
    private Drawable image;
    private String[] answers;
    private int idGood;

    public ImageQuestion(Themes theme, String question, Drawable image, String answer, String wrong_one, String wrong_two, String wrong_three) {
        this.theme = theme;
        this.question = question;
        this.image = image;
        this.answers = new String[]{answer, wrong_one, wrong_two, wrong_three};

        // The different answers are shuffle directly here and the position of the good answer is saved
        Collections.shuffle(asList(answers));
        for (int i = 0; i < answers.length; i++) {
            if (answers[i].equals(answer)) {
                idGood = i;
            }
        }
    }

    protected ImageQuestion(Parcel in) {
        super(in);
        Bitmap bitmap = (Bitmap)in.readParcelable(getClass().getClassLoader());

        theme = Themes.valueOf(in.readString());
        question = in.readString();
        image = new BitmapDrawable(Resources.getSystem(), bitmap);
        answers = in.createStringArray();
        idGood = in.readInt();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        Bitmap bitmap = (Bitmap)((BitmapDrawable) image).getBitmap();

        dest.writeString(this.theme.name());
        dest.writeString(question);
        dest.writeParcelable(bitmap, flags);
        dest.writeStringArray(answers);
        dest.writeInt(idGood);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<ImageQuestion> CREATOR = new Creator<ImageQuestion>() {
        @Override
        public ImageQuestion createFromParcel(Parcel parcel) {
            return new ImageQuestion(parcel);
        }

        @Override
        public ImageQuestion[] newArray(int i) {
            return new ImageQuestion[i];
        }
    };

    public Themes getTheme() {
        return theme;
    }

    public String getQuestion() {
        return question;
    }

    public Drawable getImage() {
        return image;
    }

    public String getAnswer() {
        return answers[idGood];
    }

    public boolean isValid(String answer) {
        return answer.equals(answers[idGood]);
    }

    public String[] getAnswers() {
        return answers;
    }

    public int getType() {
        return 3;
    }

    public String toString() {
        String res = "";
        res += "Theme : " + theme.toString() + "\n";
        res += "Question : " + question + "\n";
        res += "Answers : " + answers[0] + " ; " + answers[1] + " ; " + answers[2] + " ; " + answers[3] + "\n";
        res += "Good answer : " + answers[idGood] + "\n";
        res += "Image name : " + image.toString() + "\n";
        return res;
    }
}

TrueFalse.java

public class TrueFalse extends Questions {

    private Themes theme;
    private String question, answer;

    public TrueFalse(Themes theme, String question, String answer) {
        this.theme = theme;
        this.question = question;
        this.answer = answer;
    }

    protected TrueFalse(Parcel in) {
        super(in);
        theme = Themes.valueOf(in.readString());
        question = in.readString();
        answer = in.readString();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.theme.name());
        dest.writeString(question);
        dest.writeString(answer);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<TrueFalse> CREATOR = new Creator<TrueFalse>() {
        @Override
        public TrueFalse createFromParcel(Parcel parcel) {
            return new TrueFalse(parcel);
        }

        @Override
        public TrueFalse[] newArray(int i) {
            return new TrueFalse[i];
        }
    };

    public Themes getTheme() {
        return theme;
    }

    public String getQuestion() {
        return question;
    }

    public String getAnswer() {
        return answer;
    }

    public boolean isValid(String answer) {
        return this.answer.equals(answer);
    }

    public String[] getAnswers() {
        return null;
    }

    public Drawable getImage() {
        return null;
    }

    public int getType() {
        return 2;
    }

    public String toString() {
        String res = "";
        res += "Theme : " + theme.toString() + "\n";
        res += "Question : " + question + "\n";
        res += "Good answer : " + getAnswer() + "\n";
        return res;
    }
}

MainActivity.java : список передается в методе play ()

public class MainActivity extends AppCompatActivity {

    ArrayList<Questions> questions;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void readFill(Themes theme) {
        // multiple_questions.csv -> 6 columns : THEME ; question ; answer ; wrong ; wrong ; wrong
        // true_false_questions.csv -> 3 columns : THEME ; question ; answer (0/1)
        // image_question.csv -> 7 columns : THEME ; question ; drawable ; answer ; wrong ; wrong ; wrong

        switch(theme) {
            case ALL: fillAll();
            case TECH: fill(Themes.TECH);
            case CULTURE: fill(Themes.CULTURE);
            case ANIMALS: fill(Themes.ANIMALS);
            case SCIENCE: fill(Themes.SCIENCE);
        }
    }

    public void fill(Themes pTheme) {
        BufferedReader br = null;
        questions = new ArrayList<>();
        try {
            String currentLine;
            br = new BufferedReader(new InputStreamReader(getAssets().open("multiple_questions.csv")));

            while ((currentLine = br.readLine()) != null) {
                String[] row = currentLine.split(";");
                Themes theme = Themes.fromString(row[0]);

                if (theme == pTheme) {
                    Questions question = new MultipleQuestion(theme, row[1], row[2], row[3], row[4], row[5]);
                    questions.add(question);
                }
            }

            br = new BufferedReader(new InputStreamReader(getAssets().open("true_false_questions.csv")));

            while ((currentLine = br.readLine()) != null) {
                String[] row = currentLine.split(";");
                Themes theme = Themes.fromString(row[0]);

                if (theme == pTheme) {
                    Questions question = new TrueFalse(theme, row[1], (row[2].equals("1") ? "True" : "False"));
                    questions.add(question);
                }
            }

            br = new BufferedReader(new InputStreamReader(getAssets().open("image_question.csv")));

            while ((currentLine = br.readLine()) != null) {
                String[] row = currentLine.split(";");
                Themes theme = Themes.fromString(row[0]);

                if (theme == pTheme) {
                    Resources resources = getApplicationContext().getResources();
                    final int resourceId = resources.getIdentifier(row[2].split("\\.")[0], "drawable", getPackageName());
                    Drawable img = ContextCompat.getDrawable(this, resourceId);

                    Questions question = new ImageQuestion(theme, row[1], img, row[3], row[4], row[5], row[6]);
                    questions.add(question);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        Collections.shuffle(questions);
    }

    public void fillAll() {
        BufferedReader br = null;
        questions = new ArrayList<>();
        try {
            String currentLine;
            br = new BufferedReader(new InputStreamReader(getAssets().open("multiple_questions.csv")));

            while ((currentLine = br.readLine()) != null) {
                String[] row = currentLine.split(";");
                Questions question = new MultipleQuestion(Themes.fromString(row[0]), row[1], row[2], row[3], row[4], row[5]);
                questions.add(question);
            }

            br = new BufferedReader(new InputStreamReader(getAssets().open("true_false_questions.csv")));

            while ((currentLine = br.readLine()) != null) {
                String[] row = currentLine.split(";");
                Questions question = new TrueFalse(Themes.fromString(row[0]), row[1], (row[2].equals("1") ? "True" : "False"));
                questions.add(question);
            }

            br = new BufferedReader(new InputStreamReader(getAssets().open("image_question.csv")));

            while ((currentLine = br.readLine()) != null) {
                String[] row = currentLine.split(";");
                Resources resources = getApplicationContext().getResources();
                final int resourceId = resources.getIdentifier(row[2].split("\\.")[0], "drawable", getPackageName());
                Drawable img = ContextCompat.getDrawable(this, resourceId);

                Questions question = new ImageQuestion(Themes.fromString(row[0]), row[1], img, row[3], row[4], row[5], row[6]);
                questions.add(question);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        Collections.shuffle(questions);
    }

    public void play() {
        Intent intent;
        int type = questions.get(0).getType();
        int number = 1;

        if (type == 1) {
            intent = new Intent(MainActivity.this, MultipleQuestionActivity.class);
        } else if (type == 2) {
            intent = new Intent(MainActivity.this, TrueFalseActivity.class);
        } else {
            intent = new Intent(MainActivity.this, ImageQuestionActivity.class);
        }

        intent.putExtra("number", number);
        intent.putParcelableArrayListExtra("questions", questions);

        for (int i = 0; i < questions.size(); i++) {
            Log.d("LIST", "Q" + i + " : " + questions.get(i).toString());
        }

        Log.d("QUEST","size init : " + questions.size());
        startActivity(intent);
    }

    /* I tried to use this but I had the same exception
    @Override
    protected void onSaveInstanceState(Bundle oldInstanceState) {
        super.onSaveInstanceState(oldInstanceState);
        oldInstanceState.clear();
    }
    */

    public void playAll(View view) {
        readFill(Themes.ALL);
        play();
    }

    public void playTech(View view) {
        readFill(Themes.TECH);
        play();
    }

    public void playCulture(View view) {
        readFill(Themes.CULTURE);
        play();
    }

    public void playAnimal(View view) {
        readFill(Themes.ANIMALS);
        play();
    }

    public void playScience(View view) {
        readFill(Themes.SCIENCE);
        play();
    }
}

MultipleQuestionActivity.java Все в onCreate

public class MultipleQuestionActivity extends AppCompatActivity {

    ProgressBar progress;
    MyCountDownTimer countTimer;
    ArrayList<Questions> questions;
    int number;

    @Override
    @SuppressWarnings("ConstantConditions")
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multiple_question);
        progress = findViewById(R.id.progressbar);

        Intent intent = getIntent();
        number = intent.getExtras().getInt("number");
        Log.d("QUEST", "int : " + number);

        //Bundle b = intent.getExtras();
        questions = intent.getParcelableArrayListExtra("questions");

        for (int i = 0; i < questions.size(); i++) {
            Log.d("LIST", "Q" + i + " : " + questions.get(i).toString());
        }

        Log.d("QUEST", "List size : " + questions.size());

        countTimer = new MyCountDownTimer(15000, 10);
        countTimer.start();
    }

    public class MyCountDownTimer extends CountDownTimer {

        public MyCountDownTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        @Override
        public void onTick(long millisUntilFinished) {
            int progressTime = (int) ((millisUntilFinished/100)/1.5);
            progressTime = (int) ((15000/100)/1.5) - progressTime;

            if (progressTime >= 35 && progressTime < 60) {
                progress.setProgressTintList(ColorStateList.valueOf(Color.rgb(255, 224, 53)));
            } else if (progressTime >= 55 && progressTime < 80) {
                progress.setProgressTintList(ColorStateList.valueOf(Color.rgb(249,148,47)));
            } else if (progressTime >= 75) {
                progress.setProgressTintList(ColorStateList.valueOf(Color.rgb(246, 41, 41)));
            }

            progress.setProgress(progress.getMax() - progressTime);
        }

        @Override
        public void onFinish() {
            finish();
        }
    }

}

ImageQuestionActivity.java и TrueFalseActivity.java имеют один и тот же код, что и MultipleQuestionActivity.java, поэтому я не буду помещать его здесь. Пожалуйста, скажите мне, есть ли другой способ передать ArrayList, или мне не нужно делать этот список.

В моем коде есть еще одна проблема, которую я не вижу (и это не главная проблема из-за исключения), это то, что есть только вопросы темы "Наука", независимо от того, что называется функцией play.

Вот Logcat:

2019-03-08 11:29:27.623 29975-29975/com.morgane.quizit E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!  (parcel size = 14632360)
2019-03-08 11:29:27.623 29975-29975/com.morgane.quizit D/AndroidRuntime: Shutting down VM
2019-03-08 11:29:27.624 29975-29975/com.morgane.quizit E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.morgane.quizit, PID: 29975
    java.lang.IllegalStateException: Could not execute method for android:onClick
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:390)
        at android.view.View.performClick(View.java:6669)
        at android.view.View.performClickInternal(View.java:6638)
        at android.view.View.access$3100(View.java:789)
        at android.view.View$PerformClick.run(View.java:26145)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6863)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385)
        at android.view.View.performClick(View.java:6669) 
        at android.view.View.performClickInternal(View.java:6638) 
        at android.view.View.access$3100(View.java:789) 
        at android.view.View$PerformClick.run(View.java:26145) 
        at android.os.Handler.handleCallback(Handler.java:873) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6863) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
     Caused by: java.lang.RuntimeException: Failure from system
        at android.app.Instrumentation.execStartActivity(Instrumentation.java:1862)
        at android.app.Activity.startActivityForResult(Activity.java:4599)
        at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:767)
        at android.app.Activity.startActivityForResult(Activity.java:4557)
        at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:754)
        at android.app.Activity.startActivity(Activity.java:4918)
        at android.app.Activity.startActivity(Activity.java:4886)
        at com.morgane.quizit.MainActivity.play(MainActivity.java:151)
        at com.morgane.quizit.MainActivity.playAll(MainActivity.java:164)
        at java.lang.reflect.Method.invoke(Native Method) 
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385) 
        at android.view.View.performClick(View.java:6669) 
        at android.view.View.performClickInternal(View.java:6638) 
        at android.view.View.access$3100(View.java:789) 
        at android.view.View$PerformClick.run(View.java:26145) 
        at android.os.Handler.handleCallback(Handler.java:873) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6863) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 
     Caused by: android.os.TransactionTooLargeException: data parcel size 14632360 bytes
        at android.os.BinderProxy.transactNative(Native Method)
        at android.os.BinderProxy.transact(Binder.java:1177)
        at android.app.IActivityManager$Stub$Proxy.startActivity(IActivityManager.java:3702)
        at android.app.Instrumentation.execStartActivity(Instrumentation.java:1856)
        at android.app.Activity.startActivityForResult(Activity.java:4599) 
        at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:767) 
        at android.app.Activity.startActivityForResult(Activity.java:4557) 
        at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:754) 
        at android.app.Activity.startActivity(Activity.java:4918) 
        at android.app.Activity.startActivity(Activity.java:4886) 
        at com.morgane.quizit.MainActivity.play(MainActivity.java:151) 
        at com.morgane.quizit.MainActivity.playAll(MainActivity.java:164) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385) 
        at android.view.View.performClick(View.java:6669) 
        at android.view.View.performClickInternal(View.java:6638) 
        at android.view.View.access$3100(View.java:789) 
        at android.view.View$PerformClick.run(View.java:26145) 
        at android.os.Handler.handleCallback(Handler.java:873) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:193) 
        at android.app.ActivityThread.main(ActivityThread.java:6863) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 

Обновление

Я пытался использовать класс DataFragment, как сказано в комментарии, но теперь я получаю исключение NullPointerException.

DataFragment.java

public class DataFragment extends Fragment {

    // data object we want to retain
    private ArrayList<Questions> data;

    // this method is only called once for this fragment
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // retain this fragment
        setRetainInstance(true);
    }

    public void setData(ArrayList<Questions> data) {
        this.data = data;
    }

    public ArrayList<Questions> getData() {
        return data;
    }
}

MainActivity.java (метод play ())

public void play() {
        Intent intent;
        int type = questions.get(0).getType();
        int number = 1;

        if (type == 1) {
            intent = new Intent(MainActivity.this, MultipleQuestionActivity.class);
        } else if (type == 2) {
            intent = new Intent(MainActivity.this, TrueFalseActivity.class);
        } else {
            intent = new Intent(MainActivity.this, ImageQuestionActivity.class);
        }

        intent.putExtra("number", number);
        //intent.putParcelableArrayListExtra("questions", questions);
        FragmentManager fm = getSupportFragmentManager();
        data = (DataFragment) fm.findFragmentByTag("data");

        if (data == null) {
            data = new DataFragment();
            fm.beginTransaction().add(data, "data").commit();
            data.setData(questions);
        }

        for (int i = 0; i < questions.size(); i++) {
            Log.d("LIST", "Q" + i + " : " + questions.get(i).toString());
        }

        Log.d("QUEST","size init : " + questions.size());
        startActivity(intent);
    }

Другие виды деятельности (onCreate)

@Override
    @SuppressWarnings("ConstantConditions")
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multiple_question);
        progress = findViewById(R.id.progressbar);

        Intent intent = getIntent();
        number = intent.getExtras().getInt("number");
        Log.d("QUEST", "int : " + number);

        //Bundle b = intent.getExtras();
        DataFragment data = (DataFragment) getSupportFragmentManager().findFragmentByTag("data");
        questions = data.getData();
        //questions = intent.getParcelableArrayListExtra("questions");

        for (int i = 0; i < questions.size(); i++) {
            Log.d("LIST", "Q" + i + " : " + questions.get(i).toString());
        }

        Log.d("QUEST", "List size : " + questions.size());

        countTimer = new MyCountDownTimer(15000, 10);
        countTimer.start();
    }

1 Ответ

0 голосов
/ 08 марта 2019

Я нашел способ передать ArrayList новым классом extends. Приложение:

public class App extends Application {

    public ArrayList<Questions> list;

}

Я добавил его в androidmanifest.xml:

<application
   android:name=".App"

И вот как яиспользуйте его:

App app = (App) getApplicationContext();
app.list = questions;

Чтобы получить список:

App app = (App) getApplicationContext();
questions = app.list;

Итак, я удалил реализацию Parcelable, и она работает.

...