2010-12-14 5 views
20

J'essaie d'afficher un message simple via Toast, et je reçois une exception RunTime "envoi de message à un gestionnaire sur un thread mort". La classe qui tente d'afficher le message Toast étend IntentService. La classe (C2DMReceiver) provient en fait de l'exemple ChromeToPhone pour C2DM. Voici la méthode:Toast "envoyer un message à un gestionnaire sur un thread mort"

/** 
* Called when a cloud message has been received. 
*/ 
@Override 
public void onMessage(Context context, Intent intent) { 
    Log.i(LOG_TAG, "A message notification has occured with the cloud."); 

     Log.i(LOG_TAG, "Showing toast message of the broadcast..."); 
     Toast toast = Toast.makeText(context, "Some text", Toast.LENGTH_LONG); 
     toast.show(); 

     Log.i(LOG_TAG, "Sending notification of the broadcast..."); 
     LauncherUtils.generateNotification(this, "this is where the text would go.", "Broadcast", intent); 

    } 
} 

Je suppose que la classe s'étend IntentService qu'il serait possible de demander un simple message Toast d'ici de cette manière. N'est-ce pas correct?

Répondre

0

Vous devez afficher un toast à partir du fil principal ou il ne s'affichera pas à l'écran. Le gestionnaire s'exécute à partir du thread dans lequel il a été créé. Si vous créez votre gestionnaire dans onCreate de votre service intents, cela devrait fonctionner comme prévu lorsque vous envoyez votre message.

+0

Je ne suis pas sûr que je suis, car il n'y a pas de référence directe au gestionnaire de l'objet toast. J'ai implémenté la méthode onCreate, mais je reçois toujours une exception de threads morts. Quelle est la bonne façon de créer un gestionnaire dans cette situation? C'est mon onCreate, avec gestionnaire handler protégé; @Override public void onCreate() { super.onCreate(); gestionnaire = nouveau gestionnaire() { @Override public void handleMessage (message msg) { Log.i (LOG_TAG, "En handleMessage ..."); } }; } – John

28

Ceci est dû à un bogue dans AsyncTask dans le cadre Android. AsyncTask.java a le code suivant:

private static final InternalHandler sHandler = new InternalHandler(); 

Il attend que cela soit initialisé sur le thread principal, mais ce n'est pas garanti car il sera initialisé sur fil quel que soit arrive à faire la classe à exécuter ses initialiseurs statiques. J'ai reproduit ce problème où le gestionnaire référence un thread de travail.

Un motif commun qui provoque cette situation est l'utilisation de la classe IntentService. L'exemple de code C2DM le fait.

Une solution simple consiste à ajouter le code suivant à la méthode onCreate de l'application:

Class.forName("android.os.AsyncTask"); 

Cela forcera AsyncTask à initialiser dans le thread principal. J'ai déposé un bogue à ce sujet dans la base de données de bogues android. Voir http://code.google.com/p/android/issues/detail?id=20915.

+0

Merci beaucoup, juste remarqué que cela se passe au hasard, et votre solution a résolu le problème! –

+4

Je l'ai fait et cela n'a pas aidé même un peu. Toujours quand il a reçu une notification c2dm, il enregistre la même exception. – Ixx

+0

Ne fonctionnait pas pour moi non plus. – easycheese

24
public class NetworkService extends IntentService { 
    Handler mMainThreadHandler = null; 

    public NetworkService() { 
     super(NetworkService.class.getName()); 

     mMainThreadHandler = new Handler(); 
    } 

    @Override 
    protected void onHandleIntent(Intent arg) { 
     System.out.printf("Network service intent handling: %s\n", arg.toString()); 
     mMainThreadHandler.post(new Runnable() { 
      @Override 
      public void run() { 
       Toast.makeText(getApplicationContext(), "BusyBox updated", Toast.LENGTH_LONG).show(); 
      } 
     }); 
    } 
} 
+0

[Cela fonctionne pour moi] Quelqu'un peut-il s'il vous plaît expliquer ce qui se passe ici et pourquoi cela a fonctionné? –

+3

Comme [Jonathan Perlow l'a déclaré dans sa réponse] (http://stackoverflow.com/a/7818794/1208581), il y a un bug dans le Framework Android, qui provoque un Toast, qui n'est pas créé dans le thread principal, à exécuter dans un gestionnaire, qui n'est pas attaché au thread principal. En créant 'mMainThreadHandler' sur le thread principal (dans le constructeur), nous nous assurons que le Toast s'exécute dans le thread principal. De cette façon, nous travaillons autour du bug. – sulai

0

La méthode onMessage n'a pas été appelée sur le thread principal.

Vous devez donc créer un nouveau gestionnaire.

Appliquer le code suivant:

public class GCMIntentService extends GCMBaseIntentService 
{ 
    Handler handler; 
    public GCMIntentService() 
    { 
     handler = new Handler(); 
    } 
} 
9

Une autre façon d'accomplir des messages de grillage dans le fil principal de l'arrière-plan est d'utiliser une petite méthode d'utilité pour ce produit. L'astuce consiste à créer un gestionnaire attaché au looper du thread principal (Looper.getMainLooper()).

public class ToastUtils { 

    public static void showToastInUiThread(final Context ctx, 
     final int stringRes) { 

     Handler mainThread = new Handler(Looper.getMainLooper()); 
     mainThread.post(new Runnable() { 
      @Override 
      public void run() { 
       Toast.makeText(ctx, ctx.getString(stringRes), Toast.LENGTH_SHORT).show(); 
      } 
     }); 
    } 
} 
+0

Astuce très utile. Fonctionne comme un charme –

0

Vous pouvez exécuter votre code sur le thread d'interface comme ceci:

runOnUiThread(new Runnable() { 
    public void run() { 
    try { 
    //YOUR CODE 
    } catch (Exception e) { 
    Log.d(TAG, e.getMessage()); 
    } 
} 
}); 

Cela devrait bien

Questions connexes