Я занимаюсь разработкой приложения для 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();
}