2013-04-01 2 views
5

Je fais des appels réseau à partir d'un IntentService mais je reçois toujours une exception NetworkOnMainThreadException. Ma compréhension est qu'un IntentService fonctionne toujours sur un thread de travail, donc je suis surpris de voir cela. L'élément crucial peut être que mon IntentService appelle une classe d'assistance statique qui effectue les appels réseau. La classe d'assistance statique est instanciée dans ma classe Application principale.NetworkOnMainThreadException dans IntentService

Je pensais que cela s'exécuterait toujours sur le thread de travail du IntentService. Qu'est-ce que je rate? Honnêtement, je préfère les discussions informatives capitales sur un correctif de code rapide. Mais si le code est nécessaire, le code doit être fourni:

//MyApplication.java 
public class MyApplication extends Application{ 

private static NetworkUtils utils; 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
     utils = new NetworkUtils(this); 
     ... 
    } 
    ... 
} 

//NetworkUtils.java 
public class NetworkUtils { 

    private static Context context; 
    private static final Gson gson = new Gson(); 

    public NetworkUtils(Context context) { 
     this.context = context; 
    } 

    public static final DataResponse login(String email, String password) { 
     //*** NetworkOnMainThreadException OCCURS HERE *** 
     DataResponse response = HttpConnection.put(url, json); 
     ... 
     return response; 
    } 
    ... 
} 

//LoginService.java 
public class LoginService extends IntentService { 

    public LoginService() { 
     super("LoginService"); 
    } 

    @Override 
    public void onStart(Intent intent, int startId) { 
     onHandleIntent(intent); 
    } 

    @Override 
    protected void onHandleIntent(Intent intent) { 
     Bundle bundle = new Bundle(); 
     DataResponse response = NetworkUtils.login(email, password); 
     ... 
     bundle.putBoolean(MyConstants.ExtraKeys.LOGGED, response.success); 
     MainApplication.getApplicationInstance().sendBroadCast(MyConstants.Actions.LOGIN, bundle); 
    } 
} 

//LoginActivity.java 
public class LoginActivity extends ActionBarActivity implements IDialogClickListener { 
    ... 
    public void onLoginButtonPressed() { 
     Intent intent = new Intent(MainApplication.getApplicationInstance(), LoginService.class); 
     this.startService(intent); 
    } 
} 

Aussi, Logcat:

> 04-01 18:20:41.048: VERBOSE/com.foo.foo(28942): 
>  com.foo.foo.network.HttpConnection.execute - METHOD: PUT 
> 04-01 18:20:41.068: ERROR/com.foo.foo(28942): 
>  com.foo.foo.social.NetworkUtils.login - class 
>  android.os.NetworkOnMainThreadException: null 
> 04-01 18:20:41.169: DEBUG/com.foo.foo(28942): 
>  com.foo.foo.MainActivity$MyReceiver.onReceive - BROADCAST RECEIVED: 
>  [email protected] - Intent { act=com.foo.foo.login 
>  dat=com.foo.foo.scheme://data/1364854841079 (has extras) } 
> 04-01 18:20:41.169: INFO/com.foo.foo(28942): 
>  com.foo.foo.activity.LoginActivity.setData - ACTION: com.foo.foo.login 
>  - ISERROR: true 

SOLUTION

La question sous-jacente était un peu de code hérité qui appelle onHandleIntent explicitement . Dans LoginService.java ci-dessus:

@Override 
public void onStart(Intent intent, int startId) { 
    onHandleIntent(intent); 
} 

Ceci est à l'origine du code onHandleIntent à exécuter sur le thread principal, comme on l'appelle de l'événement onStart (qui se déroule apparemment thread principal).

+1

Il vous manque une partie de votre code pour que nous puissions voir ce qui se passe. S'il vous plaît poster du code, la sortie logcat, et pointez sur la ligne de l'erreur vient. – MCeley

Répondre

4

J'ai repéré le problème. Regardez cette dérogation déconcertante dans LoginService.java ci-dessus:

@Override 
public void onStart(Intent intent, int startId) { 
    onHandleIntent(intent); 
} 

Ceci est à l'origine du code onHandleIntent à exécuter sur le thread principal, comme on l'appelle de l'événement onStart (qui se déroule apparemment thread principal). J'aimerais lire l'esprit du développeur qui a mis cela dans!

2

Vos soupçons sont corrects. Vous instanciez la classe NetworkUtils de votre classe d'application. Cela ne va pas fonctionner dans un fil de fond, peu importe comment vous l'appelez.

+0

Est-ce que cela implique que je peux instancier NetworkUtils ailleurs à partir de la classe d'application et ainsi l'exécuter sur un thread d'arrière-plan _et_ il peut rester une classe statique? (va expérimenter avec le constructeur paresseux) –

+1

hrm, même en supprimant l'instance de NetworkUtils de la classe Application et en lui permettant de construire à la volée échoue toujours. Je suppose qu'une classe d'assistance statique ne fonctionnera pas du tout et je dois l'instancier localement partout où des NetworkUtils sont nécessaires. –

+0

@click_whir à nouveau, vous avez raison :) –

2

Strictement parlant, c'est une classe qui contient des variables statiques. Je vous suggère fortement d'éviter les variables statiques. Au lieu de cela, utilisez l'API Android pour conserver l'état dans des objets tels que SharedPreferences ou Bundles.

L'environnement d'objet Android est transitoire par conception. Au lieu de conserver l'état en mémoire, persistez-le dans les objets et les constructions qui lui sont spécialement conçus, tels que Bundles et SharedPreferences. Vous serez beaucoup plus heureux. Essayez n'importe quoi d'autre, et vous finirez par essayer de resserrer une grande masse de vers dans une toute petite boîte.

+0

hm ... bon conseil, j'ai en effet déjà rencontré des situations où juste aller avec le modèle android permet un développement plus heureux. C'est un peu du code que j'ai hérité, et je suis (apparemment à tort) appliquant la notion de Java que les classes auxiliaires qui n'ont pas d'état devraient être des classes statiques. –

+0

oui, il y a quelques constructions personnalisées ici que je fais disparaître. Le BroadcastReceiver à l'échelle de l'application est en train de devenir un ResultReceiver pour ce service uniquement. Aussi Context dans la classe statique est seulement là pour obtenir des chaînes (constantes de classe R) pour les commentaires des utilisateurs, donc je vais envoyer un résultat plus pur via ResultReceiver, _then_ pair avec des chaînes de feedback dans l'activité/le fragment appelant. Cela me permettra de faire des appels statiques sur les membres de NetworkUtils et de ne pas avoir à stocker l'instance de cette classe dans la classe Application. Pardonnez-moi mon manque de réputation pour voter vos commentaires utiles. –