«java.lang.NullPointerException: попытка получить длину нулевого массива» при попытке восстановить строку [] [] с помощью onSaveInstanceState () - PullRequest
0 голосов
/ 04 марта 2019

Я пытался сделать так, чтобы WordSearch можно было восстановить, когда я вернусь к активности и фрагментирую после того, как я нажму кнопку «Домой» (В режиме - Не сохранять действия),

Нажмите кнопку домой: должен вызвать onSaveInstance.

Нажмите приложение еще раз: должно вернуться в приложение и восстановить его состояние раньше.

Но я почему-то не смог вернуть поле String [] [] this.characterGrid.

Кто-нибудь знает почему?

import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;

import com.google.gson.annotations.SerializedName;

import java.util.Arrays;
import java.util.List;

public class WordSearch implements Parcelable {
  public static final Parcelable.Creator<WordSearch> CREATOR =
      new Parcelable.Creator<WordSearch>() {
        public WordSearch createFromParcel(Parcel source) {
          return new WordSearch(source);
        }

        public WordSearch[] newArray(int size) {
          return new WordSearch[size];
        }
      };
  public final String word;
  public final @SerializedName("character_grid") String[][] characterGrid;
  public final @SerializedName("word_locations") List<String> translations;
  public final @SerializedName("source_language") String sourceLanguage;
  public final @SerializedName("target_language") String targetLanguage;

  WordSearch(String sourceLanguage, String targetLanguage, String word,
             String[][] characterGrid, List<String> translations) {
    this.sourceLanguage = sourceLanguage;
    this.targetLanguage = targetLanguage;
    this.word = word;
    this.characterGrid = characterGrid;
    this.translations = translations;
  }

  private WordSearch(Parcel in) {
    this.sourceLanguage = in.readString();
    this.targetLanguage = in.readString();
    this.word = in.readString();
    int size = in.readInt();
    Log.d("gzl", "WordSearch characterGrid size = " + size);
    this.characterGrid = new String[size][];
    Log.d("gzl", "WordSearch characterGrid size = " + size);
    for (int i = 0; i < size; i++) {
      Log.d("gzl", "WordSearch " + Arrays.toString(this.characterGrid[i]));
      in.readStringArray(this.characterGrid[i]);
//      if (this.characterGrid[i] != null) {
//        in.readStringArray(this.characterGrid[i]);
//      }
      Log.d("gzl", "WordSearch characterGrid position " + i + " "
              + Arrays.toString(this.characterGrid[i]));
    }
    this.translations = in.createStringArrayList();
  }

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

  @Override public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(this.sourceLanguage);
    dest.writeString(this.targetLanguage);
    dest.writeString(this.word);
    dest.writeInt(this.characterGrid.length);
    Log.d("gzl", "writeToParcel " + this.characterGrid.length + "");
    for (String[] array : this.characterGrid) {
      Log.d("gzl", "writeToParcel " + Arrays.toString(array));
      dest.writeStringArray(array);
    }
    dest.writeStringList(this.translations);
    Log.d("gzl", "writeToParcel\n");

    RuntimeException re = new RuntimeException();
    re.fillInStackTrace();
    Log.e("gzl", "writeToParcel stackTrace", re);
  }
}

Фрагмент:

    @Override
    public void onResume() {
        super.onResume();
        if (wordSearches != null) {
//            Log.d("gzl", wordSearches.get(position).toString());
            Log.d("gzl", "onResume position = " + position);
            Log.d("gzl", "onResume wordSearches = " + wordSearches);
            setupWordSearch(wordSearches.get(position), boardView, sourceWord);
        }
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        if (wordSearches != null) {
            outState.putParcelableArrayList(WORD_SEARCHES, wordSearches);
            outState.putInt(CURRENT_POSITION, position);
            Log.d("gzl", "onSaveInstanceState");
            Log.d("gzl", wordSearches.toString());
        }
        super.onSaveInstanceState(outState);
    }

Как восстановить данные:

   @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            position = savedInstanceState.getInt(CURRENT_POSITION);
//            wordSearches = savedInstanceState.getParcelableArrayList(WORD_SEARCHES);

            wordSearches = savedInstanceState.<WordSearch>getParcelableArrayList(WORD_SEARCHES);
            if (wordSearches != null) {
                Log.d("gzl", "onCreate " + wordSearches.toString());
            } else {
                Log.d("gzl", "onCreate " + "wordSearches = null");
            }

            Log.d("gzl", "onCreate savedInstanceState " + savedInstanceState.toString());

        } else {
            getWordSearches();
        }
    }

Я получил следующий logcat:

2019-03-04 23:42:13.317 25699-25699/com.gongzelong.duolingowordsearch D/gzl: WordSearch characterGrid size = 8
2019-03-04 23:42:16.830 25699-25713/com.gongzelong.duolingowordsearch W/zygote64: Clearing bad or obsolete profile data from file /data/misc/profiles/cur/0/com.gongzelong.duolingowordsearch/primary.prof: Profile EOF reached prematurely for ReadProfileHeader
2019-03-04 23:42:21.947 25699-25699/com.gongzelong.duolingowordsearch D/gzl: WordSearch characterGrid size = 8
2019-03-04 23:42:25.529 25699-25699/com.gongzelong.duolingowordsearch D/gzl: WordSearch null
2019-03-04 23:46:52.471 25699-25699/com.gongzelong.duolingowordsearch D/AndroidRuntime: Shutting down VM
2019-03-04 23:46:52.508 25699-25699/com.gongzelong.duolingowordsearch E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.gongzelong.duolingowordsearch, PID: 25699
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.gongzelong.duolingowordsearch/com.gongzelong.duolingowordsearch.MainActivity}: java.lang.NullPointerException: Attempt to get length of null array
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
     Caused by: java.lang.NullPointerException: Attempt to get length of null array
        at android.os.Parcel.readStringArray(Parcel.java:1261)
        at com.gongzelong.duolingowordsearch.model.WordSearch.<init>(WordSearch.java:48)
        at com.gongzelong.duolingowordsearch.model.WordSearch.<init>(WordSearch.java:12)
        at com.gongzelong.duolingowordsearch.model.WordSearch$1.createFromParcel(WordSearch.java:16)
        at com.gongzelong.duolingowordsearch.model.WordSearch$1.createFromParcel(WordSearch.java:14)
        at android.os.Parcel.readParcelable(Parcel.java:2860)
        at android.os.Parcel.readValue(Parcel.java:2754)
        at android.os.Parcel.readListInternal(Parcel.java:3184)
        at android.os.Parcel.readArrayList(Parcel.java:2384)
        at android.os.Parcel.readValue(Parcel.java:2775)
        at android.os.Parcel.readArrayMapInternal(Parcel.java:3123)
        at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:273)
        at android.os.BaseBundle.unparcel(BaseBundle.java:226)
        at android.os.Bundle.getSparseParcelableArray(Bundle.java:1009)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1361)
        at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1784)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1852)
        at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3269)
        at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:3223)
        at android.support.v4.app.FragmentController.dispatchCreate(FragmentController.java:190)
        at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:369)
        at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:85)
        at com.gongzelong.duolingowordsearch.MainActivity.onCreate(MainActivity.java:14)

Но я могу правильно написать ToParcel:

2019-03-04 23:41:43.149 25699-25699/com.gongzelong.duolingowordsearch D/gzl: writeToParcel [ü, á, p, a, n]
2019-03-04 23:41:43.150 25699-25699/com.gongzelong.duolingowordsearch D/gzl: writeToParcel [k, a, k, m, l]
2019-03-04 23:41:43.150 25699-25699/com.gongzelong.duolingowordsearch D/gzl: writeToParcel [a, x, q, e, h]
2019-03-04 23:41:43.150 25699-25699/com.gongzelong.duolingowordsearch D/gzl: writeToParcel [p, s, a, j, í]
2019-03-04 23:41:43.150 25699-25699/com.gongzelong.duolingowordsearch D/gzl: writeToParcel [á, q, l, j, l]

Обновление:

Я пытался добавить разрешение (чтение и запись), как это в MainActivity # onCreate, но это не такисправить ошибку.

if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) {
  //android 6.0 之后需要在activity中动态获取权限
  if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
          != PackageManager.PERMISSION_GRANTED) {
    //申请WRITE_EXTERNAL_STORAGE权限
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
            1);
  }
  if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
          != PackageManager.PERMISSION_GRANTED) {
    //申请WRITE_EXTERNAL_STORAGE权限
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
            1);
  }
}

Ответы [ 2 ]

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

Заменить следующее утверждение:

in.readStringArray(this.characterGrid[i]);

на

this.characterGrid[i] = in.createStringArray();
0 голосов
/ 04 марта 2019

Как только вам придётся читать / записывать вещи в Parcel, которые не поддерживаются изначально, я рекомендую создать класс ParcelableUtils, чтобы помочь организовать ваш код.В этом случае String[][] изначально не поддерживается, поэтому я бы создал методы для его обработки.

public class ParcelableUtils {

    private ParcelableUtils() {
        throw new UnsupportedOperationException("static methods only");
    }

    public static String[][] createStringArrayArray(Parcel in) {
        int size = in.readInt();

        if (size != -1) {
            String[][] arrayArray = new String[size][];

            for (int i = 0; i < size; i++) {
                arrayArray[i] = in.createStringArray();
            }

            return arrayArray;
        } else {
            return null;
        }
    }

    public static void writeStringArrayArray(Parcel dest, String[][] arrayArray) {
        if (arrayArray != null) {
            dest.writeInt(arrayArray.length);

            for (String[] array : arrayArray) {
                dest.writeStringArray(array);
            }
        } else {
            dest.writeInt(-1);
        }
    }
}

Общая методика состоит в том, чтобы сначала записать размер массива массивов,а затем написать каждый массив.Если вы посмотрите на источник Parcel.java, это именно то, как реализовано writeStringArray().То же самое верно и для другой стороны: мы читаем размер массива массивов, а затем создаем каждый массив непосредственно из Parcel.

С этим на месте ваша реализация Parcelable в WordSearch становится значительно легче иметь дело:

private WordSearch(Parcel in) {
    this.sourceLanguage = in.readString();
    this.targetLanguage = in.readString();
    this.word = in.readString();
    this.characterGrid = ParcelableUtils.createStringArrayArray(in);
    this.translations = in.createStringArrayList();
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(this.sourceLanguage);
    dest.writeString(this.targetLanguage);
    dest.writeString(this.word);
    ParcelableUtils.writeStringArrayArray(dest, characterGrid);
    dest.writeStringList(this.translations);
}
...