2013-04-01 7 views
5

Ich mache Netzwerkanrufe von einem IntentService, aber immer noch eine NetworkOnMainThreadException. Mein Verständnis ist, dass ein IntentService immer auf einem Arbeitsthread läuft, also bin ich überrascht, dies zu sehen. Der entscheidende Teil könnte sein, dass mein IntentService eine statische Hilfsklasse aufruft, die die Netzwerkaufrufe durchführt. Die statische Hilfsklasse wird in meiner Hauptanwendungsklasse instanziiert.NetworkOnMainThreadException in IntentService

Ich dachte, dies würde immer noch auf dem Arbeitsthread des IntentService ausgeführt werden. Was vermisse ich?

Ehrlich gesagt bevorzuge ich berauschende informative Diskussionen über einen schnellen Code-Fix. Aber wenn Code erforderlich ist, muss Code zur Verfügung gestellt werden:

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

Auch 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

Das zugrunde liegende Problem ein wenig von Legacy-Code war die onHandleIntent ausdrücklich rief . In LoginService.java oben:

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

Dies verursacht der onHandleIntent Code auf dem Haupt-Thread ausgeführt werden, wie es aus dem onStart Ereignisse aufgerufen wird (die offensichtlich auf Haupt-Thread ausgeführt wird).

+1

Sie verpassen etwas von Ihrem Code, damit wir sehen können, was vor sich geht. Bitte poste etwas Code, die logcat-Ausgabe, und zeige auf die Zeile, von der der Fehler kommt. – MCeley

Antwort

4

Ich habe das Problem entdeckt. Sehen Sie sich diese verblüffende Überschreibung in LoginService.java oben:

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

Dies verursacht den onHandleIntent Code auf dem Haupt-Thread ausgeführt werden, wie es aus dem onStart Ereignisse aufgerufen wird (die offenbar auf Haupt-Thread ausgeführt wird). Ich würde gerne den Verstand des Entwicklers lesen, der das gemacht hat!

2

Ihr Verdacht ist richtig. Sie instanziieren die NetworkUtils-Klasse aus Ihrer Anwendungsklasse. Das wird nicht in einem Hintergrund-Thread laufen, egal wie du es nennst.

+0

Bedeutet dies, dass ich NetworkUtils von der Anwendungsklasse an anderer Stelle instanziieren kann und es so dazu bringe, auf einem Hintergrund-Thread zu laufen, _und_ es eine statische Klasse bleiben kann? (geht zum Experimentieren mit dem Lazy-Konstruktor) –

+1

hrm, sogar das Entfernen der Instanz von NetworkUtils aus der Application-Klasse und das Erlauben von Build-on-the-fly schlägt immer noch fehl. Ich denke, eine statische Hilfsklasse wird überhaupt nicht funktionieren und ich muss sie lokal instanziieren, wo auch immer NetworkUtils benötigt werden. –

+0

@click_whir wieder, Sie sind richtig :) –

2

Genau genommen handelt es sich um eine Klasse, die statische Variablen enthält. Ich schlage vor, dass Sie statische Variablen vermeiden. Verwenden Sie stattdessen die Android-API, um den Status in Objekten wie SharedPreferences oder Bundles beizubehalten.

Die Android-Objektumgebung ist durch Design vorübergehend. Anstatt den persistenten Zustand im Speicher beizubehalten, bewahren Sie ihn in Objekten und Konstrukten auf, die speziell dafür entworfen wurden, z. B. Bundles und SharedPreferences. Du wirst viel glücklicher sein. Versuchen Sie etwas anderes, und Sie werden am Ende versuchen, eine große Masse von Würmern in eine sehr kleine Dose zurückzupressen.

+0

hm ... guter Rat, ich habe in der Tat Situationen bereits, wo gerade mit dem Android-Modell für eine glücklichere Entwicklung macht getroffen. Dies ist ein bisschen Code, den ich geerbt habe, und ich bin (scheinbar falsch) die Java-Vorstellung an, dass Hilfsklassen, die keinen Zustand haben, statische Klassen sein sollten. –

+0

Ja, es gibt einige benutzerdefinierte Konstrukte hier, die ich weg mache. Der App-weite BroadcastReceiver wird nur für diesen Dienst zu einem ResultReceiver. Auch Context in der statischen Klasse ist nur da, um Strings (R-Klassenkonstanten) für Benutzerfeedback zu erhalten, also werde ich ein reineres Ergebnis über ResultReceiver senden, dann das Paar mit Feedbackstrings in der aufrufenden Aktivität/Fragment. Dies ermöglicht es mir, statische Aufrufe an die Mitglieder von NetworkUtils zu tätigen und muss die Instanz dieser Klasse nicht in der Application-Klasse speichern. Verzeihen Sie, dass mein Ruf fehlt, um Ihr hilfreiches Feedback zu wählen. –