IntentService中的NetworkOnMainThreadException

9
我正在从IntentService进行网络调用,但仍然收到NetworkOnMainThreadException异常。我的理解是IntentService始终在工作线程上运行,所以我很惊讶看到这个异常。关键部分可能是我的IntentService调用了一个执行网络调用的静态辅助类。静态辅助类在我的主应用程序类中实例化。
我认为这仍将在IntentService的工作线程上执行。我错过了什么?
老实说,我更喜欢深入的信息讨论而不是快速的代码修复。但如果需要代码,我会提供代码:
//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);
    }
}

此外,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:
>     com.foo.foo.MainApplication@422d81d8 - 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

解决方案

问题的根源是一些遗留代码在显式地调用了onHandleIntent。在上面的LoginService.java文件中:

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

这导致onHandleIntent代码在主线程上运行,因为它是从onStart事件调用的(显然在主线程上运行)。


1
你缺少一些代码,所以我们无法看到发生了什么。请发布一些代码、logcat输出,并指出错误来自哪一行。 - Michael Celey
3个回答

5
我找到了问题所在。请查看LoginService.java文件中的这个令人困惑的覆盖(override):
@Override 
public void onStart(Intent intent, int startId) { 
    onHandleIntent(intent); 
} 

这导致onHandleIntent代码在主线程上运行,因为它是从onStart事件调用的(该事件显然在主线程上运行)。我很想知道将其放入代码的开发人员的想法!

2
你的猜测是正确的。你正在从应用程序类实例化NetworkUtils类。无论如何调用它,都不会在后台线程中运行。请注意保留HTML标签。

这是否意味着我可以在应用程序类之外实例化NetworkUtils,并使其在后台线程上运行,同时它仍然可以保持为静态类?(开始尝试使用延迟构造函数进行实验) - click_whir
1
即使从Application类中删除NetworkUtils实例并允许其动态构建,仍然无法解决问题。我猜静态辅助类根本行不通,必须在需要NetworkUtils的地方本地实例化它。 - click_whir
还是不行。我已经发现了问题所在。请查看LoginService.java中的这个令人困惑的覆盖: @Override public void onStart(Intent intent, int startId) { onHandleIntent(intent); }这会导致onHandleIntent代码在主线程上运行,因为它是从onStart事件调用的(显然在主线程上运行)。我很想读一下把“那个”放进去的开发者的心里话… - click_whir

2

严格来说,这是一个包含静态变量的类。我强烈建议您避免使用静态变量。相反,使用Android API在对象中持久化状态,例如SharedPreferences或Bundles。

Android对象环境是瞬时的设计。不要将状态保留在内存中,而应该将其保留在专门为此设计的对象和结构中,如Bundles和SharedPreferences。这样您会感到更加愉快。尝试其他方法,最终你将会把一大堆蠕虫挤进一个非常小的罐子里。


嗯...好建议,我确实已经遇到过这样的情况,只要按照Android模型进行开发就会更加愉快。这是我继承的一些代码,我(显然是错误的)应用了Java的概念,即没有状态的帮助类应该是静态类。 - click_whir
是的,这里有一些自定义结构我正在放弃。应用程序范围的BroadcastReceiver将仅转换为此服务的ResultReceiver。此外,静态类中的Context仅存在于获取用户反馈的字符串(R类常量),因此我将通过ResultReceiver发送更纯净的结果,然后在调用的Activity / Fragment中与反馈字符串配对。这将允许我对NetworkUtils的成员进行静态调用,而不必在Application类中存储该类的实例。请原谅我没有声望来投票支持您有用的反馈。 - click_whir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接