【问题标题】:NetworkOnMainThreadException in IntentServiceIntentService 中的 NetworkOnMainThreadException
【发布时间】:2013-03-23 02:38:39
【问题描述】:

我正在从 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 事件(显然在主线程上运行)调用的。

【问题讨论】:

  • 您缺少一些代码,因此我们可以看到发生了什么。请发布一些代码,logcat 输出,并指向错误所在的行。

标签: android static-methods intentservice


【解决方案1】:

我发现了这个问题。在上面的 LoginService.java 中查看这个令人费解的覆盖:

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

这导致onHandleIntent 代码在主线程上运行,因为它是从 onStart 事件(显然在主线程上运行)中调用的。我很想读懂开发者的想法!

【讨论】:

    【解决方案2】:

    你的怀疑是正确的。您正在从应用程序类中实例化 NetworkUtils 类。不管你怎么称呼它,它都不会在后台线程中运行。

    【讨论】:

    • 这是否意味着我可以在应用程序类的其他地方实例化 NetworkUtils,从而让它在后台线程上运行并且它可以保持为静态类? (去尝试惰性构造函数)
    • hrm,即使从 Application 类中删除 NetworkUtils 的实例并允许它动态构建仍然失败。我想一个静态帮助类根本不起作用,我必须在需要 NetworkUtils 的地方在本地实例化它。
    • 还是不行。我已经发现了这个问题。在上面的 LoginService.java 中查看这个令人费解的覆盖:@Override public void onStart(Intent intent, int startId) { onHandleIntent(intent);这导致 onHandleIntent 代码在主线程上运行,因为它是从 onStart 事件(显然在主线程上运行)调用的。我很想了解将 that 放入.... 的开发人员的想法
    【解决方案3】:

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

    Android 对象环境在设计上是暂时的。与其将状态保存在内存中,不如将其保存在专门为其设计的对象和构造中,例如 Bundles 和 SharedPreferences。你会快乐很多。尝试其他任何方法,你最终都会试图将大量的蠕虫挤回一个很小的罐子里。

    【讨论】:

    • hm... 好建议,我确实已经遇到过仅使用 android 模型就可以实现更快乐的开发的情况。这是我继承的一些代码,我(显然是错误的)应用了 Java 概念,即没有状态的帮助类应该是静态类。
    • 是的,这里有一些自定义结构我正在取消。应用程序范围的 BroadcastReceiver 正在变成仅用于此服务的 ResultReceiver。此外,静态类中的上下文仅用于获取用户反馈的字符串(R 类常量),因此我将通过 ResultReceiver 发送更纯净的结果,然后将其与调用活动中的反馈字符串配对/分段。这将允许我对 NetworkUtils 的成员进行静态调用,而不必将所述类的实例存储在 Application 类中。请原谅我缺乏对您的有用反馈进行投票的声誉。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-24
    • 2014-10-07
    • 1970-01-01
    相关资源
    最近更新 更多