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:
Capture d'écran après avoir choisi le niveau 1 ci-dessus, puis sur la sauvegarde: