0

У меня есть игра, чья main деятельность вызывает три других мероприятий с использованием startActivityForResult - первый (SignInActivity) возвращает имя пользователя, или позволяет создавать новый; второй (LevelChooser) использует getSharedPreferences, чтобы найти файл настроек с этим именем пользователя или создает новый, пока покажет прогресс пользователя (уровни разблокированы, заработанные звезды) и позволяет пользователю выбирать любой разблокированный уровень; третий (GameActivity) обновляет файл настроек пользователя, если уровень завершен успешно, прежде чем вернуться (через main) в LevelChooser. В LevelChooser я переопределил onBackPressed, чтобы вернуть вас в SignInActivity; в GameActivity это onFinish, которое переопределено, так что вы возвращаетесь к LevelChooser независимо от того, как это происходит.Android getSharedPreferences startActivityForResult прерывистого ошибка

Теперь, девять раз из десяти, все работает точно так, как предполагалось, но иногда это не так: иногда вместо того, чтобы видеть фактические звезды и уровни пользователя, LevelChooser показывает набор неправильных и теоретически невозможных значений (например, один уровень, показанный как заблокированный, но завершенный тремя звездами). Это часто (но не всегда) происходит, если вы выбираете уровень, а затем возвращаетесь из него, когда вы впервые открываете игру: он позволит вам играть на любом уровне, показанном как разблокированное, но GameActivity не сможет сохранить ваш результат, если вы закончите уровень и те же самые неправильные уровни отображаются, когда вы возвращаетесь к LevelChooser; альтернативно, если вы вернетесь из LevelChooser и переименуете одно и то же имя пользователя, оно вернется к поведению, как ожидалось. Я также сумел воспроизвести ошибку, повторно запустив уровни и отступив от них - если вы попробуете достаточно времени, это в конечном итоге пойдет не так. Для моего собственного имени пользователя (и I думаю, для всех пользователей) неверная информация всегда одна и та же, то есть проблема прерывистая, но, когда это происходит, не является случайным.

Я пробовал отладку, но по какой-то причине (а) проблема возникает только на моем телефоне, а не на эмуляторе, и (б) при отладке (в отличие от работы) на моем телефоне он работает правильно или иначе, если он пойдет не так, просто прекратится (AFAIR, даже не в диалоговом окне «Х остановился») вместо отображения экрана неправильного уровня. Единственное, что мне удалось обнаружить при отладке, - это то, что onCreate активности LevelChooser иногда выполняется несколько раз.

Поскольку проблема прерывистая, а не просто воспроизводимая, мне интересно, невольно ли я предположил, что какой-то асинхронный процесс будет/будет завершен своевременно, линейно и что он обычно (но не всегда) обязывает; или иначе, я думаю, что мне не удалось понять что-то значимое и значимое в жизненном цикле активности. В противном случае я в тупике и угадывании.

main Activity:

public class MainActivity extends AppCompatActivity { 

public ImageView splash; 
private int GET_USER_NAME_CODE = 0; 
private int GET_LEVEL_CODE = 1; 
private int PLAY_GAME_CODE = 2; 
private String user; 
private int level; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    // remove title 
    requestWindowFeature(Window.FEATURE_NO_TITLE); 
    setContentView(R.layout.activity_splash_screen); 

    splash = (ImageView) findViewById(R.id.splashView); 

    splash.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View view) { 
      Intent sign_intent = new Intent(MainActivity.this, SignInActivity.class); 
      startActivityForResult(sign_intent, GET_USER_NAME_CODE); 
     } 
    }); 

} 

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
{ 
    super.onActivityResult(requestCode, resultCode, data); 
    // check if the request code is same as what is passed 
    if(requestCode==GET_USER_NAME_CODE) 
    { 
     user=data.getStringExtra("USER"); 
     Intent intent = new Intent(MainActivity.this, LevelChooser.class); 
       intent.putExtra("user", user); 
       startActivityForResult(intent, GET_LEVEL_CODE); 
    } 
    else { 
     if(requestCode==GET_LEVEL_CODE) { 
      level=data.getIntExtra("LEVEL", 0); 
      if(level==-1) { 
       Intent sign_intent = new Intent(MainActivity.this, SignInActivity.class); 
       startActivityForResult(sign_intent, GET_USER_NAME_CODE); 
      } 
      else { 
       Intent intent = new Intent(MainActivity.this, GameActivity.class); 
       intent.putExtra("user", user); 
       intent.putExtra("level", level); 
       startActivityForResult(intent, PLAY_GAME_CODE); 
      } 
     } 
     else { 
      if(requestCode==PLAY_GAME_CODE) { 
       user=data.getStringExtra("user"); 
       Intent intent = new Intent(MainActivity.this, LevelChooser.class); 
       intent.putExtra("user", user); 
       startActivityForResult(intent, GET_LEVEL_CODE); 
      } 
     } 
    } 
} 

LevelChooser:

public class LevelChooser extends AppCompatActivity { 

private String user; 
private ImageView[] level; 
private Boolean[] locked; 
private int[] stars; 
private SharedPreferences userprefs; 
private SharedPreferences.Editor prefseditor; 
private Boolean createNewPrefsFile = false; 
private int tempResIdVisible; 
private int tempResIdInvisible; 
private ImageView tempView; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    requestWindowFeature(Window.FEATURE_NO_TITLE); 
    setContentView(R.layout.levelchooser); 
} 

@Override 
protected void onStart() { 
    super.onStart(); 


     level = new ImageView[21]; 
     locked = new Boolean[21]; 
     stars = new int[21]; 

     user = getIntent().getStringExtra("user"); 
     userprefs = getSharedPreferences(user, MODE_PRIVATE); 
     prefseditor = userprefs.edit(); 

     //level numbers for views etc start from 1 to match images etc 
     level[0] = null; 
     locked[0] = null; 
     stars[0] = 0; 

     locked[1] = false; 
     level[1] = (ImageView) findViewById(R.id.level1); 
     level[1].setOnClickListener(new LevelClickListener(level[1], 1)); 
     if (!userprefs.contains("stars1")) { 
      createNewPrefsFile = true; 
     } 
     stars[1] = userprefs.getInt("stars1", 0); 
     if (stars[1] != 0) { 
      for (int j = 1; j < 4; j++) { 
       tempResIdInvisible = getResources().getIdentifier("stars" + j + "_1", "id", getPackageName()); 
       tempView = (ImageView) findViewById(tempResIdInvisible); 
       tempView.setVisibility(View.INVISIBLE); 
      } 
      tempResIdVisible = getResources().getIdentifier("stars" + stars[1] + "_1", "id", getPackageName()); 
      tempView = (ImageView) findViewById(tempResIdVisible); 
      tempView.setVisibility(View.VISIBLE); 
     } 

     for (int i = 2; i < 21; i++) { 
      locked[i] = userprefs.getBoolean("locked" + i, true); 
      if (locked[i]) { 
       tempResIdVisible = getResources().getIdentifier("padlock" + i, "id", getPackageName()); 
       tempResIdInvisible = getResources().getIdentifier("level" + i, "id", getPackageName()); 
      } else { 
       tempResIdVisible = getResources().getIdentifier("level" + i, "id", getPackageName()); 
       tempResIdInvisible = getResources().getIdentifier("padlock" + i, "id", getPackageName()); 
       level[i] = (ImageView) findViewById(tempResIdVisible); 
       level[i].setOnClickListener(new LevelClickListener(level[i], i)); 
      } 
      tempView = (ImageView) findViewById(tempResIdVisible); 
      tempView.setVisibility(View.VISIBLE); 
      tempView = (ImageView) findViewById(tempResIdInvisible); 
      tempView.setVisibility(View.INVISIBLE); 

      stars[i] = userprefs.getInt("stars" + i, 0); 
      if (stars[i] != 0) { 
       for (int j = 1; j < 4; j++) { 
        tempResIdInvisible = getResources().getIdentifier("stars" + j + "_" + i, "id", getPackageName()); 
        tempView = (ImageView) findViewById(tempResIdInvisible); 
        tempView.setVisibility(View.INVISIBLE); 
       } 
       tempResIdVisible = getResources().getIdentifier("stars" + stars[i] + "_" + i, "id", getPackageName()); 
       tempView = (ImageView) findViewById(tempResIdVisible); 
       tempView.setVisibility(View.VISIBLE); 
      } 
     } 

     if (createNewPrefsFile) { 
      for (int i = 1; i < 21; i++) { 
       prefseditor.putBoolean("locked" + i, locked[i]); 
       prefseditor.putInt("stars" + i, stars[i]); 
       prefseditor.commit(); 
      } 
     } 
} 

GameActivity:

public class GameActivity extends AppCompatActivity implements TextToSpeech.OnInitListener { 

//TTS Object 
private TextToSpeech myTTS; 
//TTS status check code 
private int MY_DATA_CHECK_CODE = 0; 
private int level; 
private String user; 
private PhonemeGroup levelGroup; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    requestWindowFeature(Window.FEATURE_NO_TITLE); 
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
    setSupportActionBar(toolbar); 

    level = getIntent().getIntExtra("level", 0); 
    user = getIntent().getStringExtra("user"); 
    levelGroup = initializeLevels(level); 

    Intent checkTTSIntent = new Intent(); 
    checkTTSIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA); 
    startActivityForResult(checkTTSIntent, MY_DATA_CHECK_CODE); 
} 

@Override 
public void finish() { 
    Intent intent = new Intent(); 
    intent.putExtra("user", user); 
    setResult(2, intent); 

    super.finish(); 
} 

@Override 
public void onStop() { 
    if (myTTS != null) { 
     myTTS.stop(); 
    } 
    super.onStop(); 
} 

@Override 
public void onDestroy() { 
    if (myTTS != null) { 
     myTTS.shutdown(); 
    } 
    Button ok_button = (Button) findViewById(R.id.button); 
    ok_button.setOnClickListener(null); 
    ImageView tickImageView = (ImageView) findViewById(R.id.tickImageView); 
    tickImageView.setOnClickListener(null); 
    ImageView starsView = (ImageView) findViewById(R.id.starsImageView); 
    starsView.setOnClickListener(null); 

    super.onDestroy(); 

    unbindDrawables(findViewById(R.id.GameParentView)); 
    System.gc(); 
} 

Скриншот корректного отображения: A screenshot of LevelChooser with no errors

Скриншот после выбора 1-го уровня выше, а затем поддержку из: Screenshot of LevelChooser with errors

ответ

0

Оказывается, что проблема не связана с getSharedPreferences() как таковой, а скорее о том, что переменная user возвращаемый getIntent().getStringExtra() иногда - при обстоятельствах, я обнаружил, трудно для надежного закрепления или воспроизведения - null, как в this question. Затем эта строка использовалась как параметр для getSharedPreferences(), в результате чего набор сохраненных результатов для пользователя null. Эти результаты (показанные во втором изображении в вопросе) затем отображались каждый раз, когда проблема повторялась.

Решение - хотя это еще не объясняет, почему getStringExtra() должен вернуть null - был обнулить-охранник массовку, поставив следующий код в onCreate обоих видов деятельности, которые требуют действительного пользователя:

if(getIntent().getExtras()==null || getIntent().getStringExtra("user")==null) { 
    Intent intent = new Intent(); 
    setResult(0, intent); 
    finish(); 
    } 

Если это выполняется до того, как макет для нового действия будет загружен, он вернется к основному действию и повторит попытку (так сказать), если пользователь не обнаружит никаких проблем.

Смежные вопросы