0

J'ai un jeu dont l'activité main appelle trois autres activités utilisant startActivityForResult - le premier (SignInActivity) retourne un nom d'utilisateur, ou permet la création d'un nouveau; la seconde (LevelChooser) utilise getSharedPreferences pour trouver un fichier de préférences avec le nom de cet utilisateur, ou en crée un nouveau, affiche la progression de l'utilisateur jusqu'à maintenant (niveaux débloqués, étoiles gagnées), et permet à l'utilisateur de choisir n'importe quel niveau débloqué; le troisième (GameActivity) met à jour le fichier de préférences de l'utilisateur si le niveau est terminé avec succès avant de revenir (via main) à LevelChooser. Dans LevelChooser j'ai remplacé onBackPressed pour vous renvoyer à la SignInActivity; En GameActivity c'est onFinish qui est surchargé, de sorte que vous revenez à la LevelChooser indépendamment de la façon dont cela se passe. Maintenant, neuf fois sur dix, tout fonctionne exactement comme prévu, mais parfois non: parfois, au lieu de voir les étoiles et les niveaux réels de l'utilisateur, le LevelChooser affiche un ensemble de valeurs incorrectes et théoriquement impossibles (par exemple un niveau montré comme verrouillé, mais complété avec trois étoiles). Cela arrive souvent (mais pas toujours) si vous choisissez un niveau et que vous revenez en arrière lorsque vous ouvrez le jeu: il vous permettra alors de jouer n'importe quel niveau affiché comme déverrouillé, mais le GameActivity ne sauvegarde pas votre résultat si vous complétez le niveau et les mêmes mauvais niveaux s'affichent lorsque vous revenez à LevelChooser; en revanche, si vous retirez LevelChooser et choisissez à nouveau le même nom d'utilisateur, cela revient à se comporter comme prévu. J'ai également réussi à reproduire l'erreur en recommençant à plusieurs reprises les niveaux et en les retirant - si vous essayez assez souvent, cela finit par mal tourner. Pour mon propre nom d'utilisateur (et, je pense, pour tous les utilisateurs) la mauvaise information est toujours la même, c'est-à-dire que le problème est intermittent mais, quand il se produit, pas aléatoire. J'ai essayé le débogage mais, pour une raison quelconque, (a) le problème se produit uniquement sur mon téléphone, pas sur l'émulateur, et (b) lors du débogage (par opposition à l'exécution) sur mon téléphone, il fonctionne correctement ou Sinon, si ça ne marche pas, il suffit de terminer (AFAIR sans même le dialogue "X has stopped") au lieu d'afficher le mauvais écran de niveau. La seule chose que j'ai réussi à voir dans le débogage est que le onCreate de l'activité LevelChooser est parfois exécuté plus d'une fois. Étant donné que le problème est intermittent et qu'il n'est pas directement reproductible, je me demande si j'ai involontairement supposé qu'un processus asynchrone a été achevé de manière linéaire et opportune, et qu'il est habituellement (mais pas toujours) obligatoire; ou sinon, je pense que j'ai échoué à comprendre quelque chose de pertinent et significatif sur le cycle de vie de l'activité. Sinon, je suis perplexe et je devine.getSharedPreferences Android startActivityForResult erreur intermittente

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(); 
} 

Capture de visualisation correct: A screenshot of LevelChooser with no errors

Capture d'écran après avoir choisi le niveau 1 ci-dessus, puis sur la sauvegarde: Screenshot of LevelChooser with errors

Répondre

0

Il se trouve que le problème est pas getSharedPreferences() en tant que telle, mais qui était parfois la user variable retournée par getIntent().getStringExtra() - dans des circonstances que j'ai trouvé dur pour fixer ou reproduire de manière fiable - null, comme dans this question. Cette chaîne a ensuite été utilisée comme paramètre pour getSharedPreferences(), résultant en un ensemble de résultats enregistrés pour l'utilisateur null. Ces résultats (montrés dans la deuxième image de la question) étaient alors affichés chaque fois que le problème réapparaissait.

La solution - bien qu'il n'explique pas pourquoi getStringExtra() devrait revenir null - était null garde les extras en mettant le code suivant dans la onCreate des deux activités nécessitant un utilisateur valide:

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

Si cette action est exécutée avant le chargement de la mise en page de la nouvelle activité, elle revient à l'activité principale et réessaie (pour ainsi dire) sans que l'utilisateur n'y voit de problème.