2013-04-01 14 views
5

Sto effettuando chiamate di rete da un IntentService ma continuo a ricevere NetworkOnMainThreadException. La mia comprensione è che un IntentService viene sempre eseguito su un thread di lavoro, quindi sono sorpreso di vedere questo. Il punto cruciale potrebbe essere che il mio IntentService chiama una classe helper statica che esegue le chiamate di rete. La classe helper statica è istanziata nella mia principale classe Application.NetworkOnMainThreadException in IntentService

Ho pensato che sarebbe ancora eseguito sul thread di lavoro di IntentService. Cosa mi manca?

Sinceramente preferisco discussioni informative inebrianti su una correzione rapida del codice. Ma se è richiesto il codice, deve essere fornita di codice:

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

Inoltre, 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 

SOLUZIONE

La questione di fondo era un po 'di codice legacy che stava chiamando onHandleIntent esplicitamente . In LoginService.java sopra:

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

Ciò causa il codice onHandleIntent eseguire sul thread principale, come viene chiamato dall'evento onStart (che funziona apparentemente thread principale).

+1

Ti manca parte del codice in modo che possiamo vedere cosa sta succedendo. Si prega di inviare un po 'di codice, l'output logcat e puntare alla riga da cui proviene l'errore. – MCeley

risposta

4

Ho individuato il problema. Dai un'occhiata a questo override di sconcertante in LoginService.java sopra:

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

Questa è la causa del codice di onHandleIntent da eseguire sul thread principale, come viene chiamato dall'evento onStart (che corre a quanto pare sul thread principale). Mi piacerebbe leggere la mente dello sviluppatore che l'ha inserito!

2

I tuoi sospetti sono corretti. Stai creando un'istanza della classe NetworkUtils dalla tua classe di applicazione. Non funzionerà in un thread in background, non importa come lo chiami.

+0

Ciò implica che posso istanziare NetworkUtils altrove dalla classe dell'applicazione e quindi farlo funzionare su un thread in background _e_ può rimanere una classe statica? (inizia a sperimentare con il costruttore pigro) –

+1

hrm, anche rimuovendo l'istanza di NetworkUtils dalla classe Application e permettendogli di costruire al volo ancora fallisce. Immagino che una classe di helper statica non funzioni affatto e devo istanziarla localmente ovunque siano necessari NetworkUtils. –

+0

@click_whir di nuovo, sei corretto :) –

2

In senso stretto, è una classe che contiene variabili statiche. Vi consiglio caldamente di evitare variabili statiche. Invece, utilizza l'API di Android per mantenere lo stato in oggetti come SharedPreferences o Bundles.

L'ambiente dell'oggetto Android è transitorio di progettazione. Invece di mantenere lo stato in memoria, mantenerlo in oggetti e costrutti progettati appositamente per esso, come Bundles e SharedPreferences. Sarai molto più felice. Prova qualcos'altro, e finirai per provare a spremere una grande massa di vermi in una lattina molto piccola.

+0

hm ... buon consiglio, ho davvero incontrato situazioni già dove andare con il modello Android rende lo sviluppo più felice. Questo è un po 'di codice che ho ereditato, e sono (apparentemente in modo sbagliato) applicare la nozione di Java secondo cui le classi helper che non hanno uno stato dovrebbero essere classi statiche. –

+0

sì, ci sono alcuni costrutti personalizzati qui che sto eliminando. BroadcastReceiver per tutta l'applicazione sta diventando un ResultReceiver solo per questo servizio. Anche il contesto nella classe statica è solo lì per ottenere stringhe (costanti di classe R) per il feedback degli utenti, quindi invierò un risultato più puro tramite ResultReceiver, _then_ accoppiato con stringhe di feedback nell'attività/frammento di chiamata. Ciò mi consentirà di effettuare chiamate statiche ai membri di NetworkUtils e di non dover memorizzare l'istanza di detta classe nella classe Application. Perdona la mia mancanza della reputazione di votare il tuo utile feedback. –

Problemi correlati