在IntentService中运行任务时出现NetworkOnMainThreadException

时间:2017-12-30 18:32:12

标签: android multithreading intentservice

我正在尝试使用IntentService执行文件下载任务。我读到IntentService将创建一个工作线程并执行所请求的任务。以下代码将下载视频文件。

public class MyServiceUsingIntentService extends IntentService{

    private int count=0;

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

    public MyServiceUsingIntentService(String name) {
        super("MyServiceUsingIntentService");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

                try {
                    URL url = new URL("http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4");
                    URLConnection urlConnection = url.openConnection();
                    urlConnection.setReadTimeout(5000);

                    debugMessage("urlConnection.getContentLength : " + urlConnection.getContentLength());

                    InputStream readStream = urlConnection.getInputStream();
                    String filename = "/sdcard/sample" + count++ +".mp4";
                    OutputStream writeStream = new FileOutputStream(filename);
                    int i;
                    byte[] byteArray = new byte[153600];
                    while((i = readStream.read(byteArray)) != -1){
                        writeStream.write(byteArray,0,i);
                    }
                    writeStream.close();
                    readStream.close();

                    debugMessage("Download done in doInBackground");
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }    
        return START_STICKY;
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {

    }

    public static void debugMessage(String message){
        Log.d("MyServiceUsingIntentService",message);
    }

}

当我从我的MainActivity调用startService时

startService(new Intent(MainActivity.this, MyServiceUsingIntentService.class));

我收到NetworkOnMainThreadException错误:

12-30 23:51:04.305 9586-9586/oneplus.app7 D/AndroidRuntime: Shutting down VM
12-30 23:51:04.306 9586-9586/oneplus.app7 E/AndroidRuntime: FATAL EXCEPTION: main
                                                            Process: oneplus.app7, PID: 9586
                                                            java.lang.RuntimeException: Unable to start service oneplus.app7.MyServiceUsingIntentService@be390c with Intent { cmp=oneplus.app7/.MyServiceUsingIntentService }: android.os.NetworkOnMainThreadException
                                                                at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3479)
                                                                at android.app.ActivityThread.-wrap21(ActivityThread.java)
                                                                at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1657)
                                                                at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                at android.os.Looper.loop(Looper.java:154)
                                                                at android.app.ActivityThread.main(ActivityThread.java:6334)
                                                                at java.lang.reflect.Method.invoke(Native Method)
                                                                at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
                                                                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
                                                             Caused by: android.os.NetworkOnMainThreadException
                                                                at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303)
                                                                at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:86)
                                                                at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:74)
                                                                at java.net.InetAddress.getAllByName(InetAddress.java:752)
                                                                at com.android.okhttp.internal.Network$1.resolveInetAddresses(Network.java:29)
                                                                at com.android.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:187)
                                                                at com.android.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:156)
                                                                at com.android.okhttp.internal.http.RouteSelector.next(RouteSelector.java:98)
                                                                at com.android.okhttp.internal.http.HttpEngine.createNextConnection(HttpEngine.java:346)
                                                                at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:329)
                                                                at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:247)
                                                                at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:457)
                                                                at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:405)
                                                                at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getHeaders(HttpURLConnectionImpl.java:162)
                                                                at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getHeaderField(HttpURLConnectionImpl.java:206)
                                                                at java.net.URLConnection.getHeaderFieldLong(URLConnection.java:628)
                                                                at java.net.URLConnection.getContentLengthLong(URLConnection.java:500)
                                                                at java.net.URLConnection.getContentLength(URLConnection.java:484)
                                                                at oneplus.app7.MyServiceUsingIntentService.onStartCommand(MyServiceUsingIntentService.java:54)
                                                                at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3462)
                                                                at android.app.ActivityThread.-wrap21(ActivityThread.java) 
                                                                at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1657) 
                                                                at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                                at android.os.Looper.loop(Looper.java:154) 
                                                                at android.app.ActivityThread.main(ActivityThread.java:6334) 
                                                                at java.lang.reflect.Method.invoke(Native Method) 
                                                                at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
                                                                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 

但是,如果我尝试在onStartCommand()内的线程中运行下载部分,它的工作正常,我就可以下载该文件了。

每次IntentService时,我是否需要在单独的线程中进行网络操作?

4 个答案:

答案 0 :(得分:2)

您应该通过覆盖onHandleIntent()方法在不同的主题上进行处理,而不应覆盖onStartCommand()

修改

您可以看到here IntentService如何为您处理onStartCommand,因此您不必这样做。

答案 1 :(得分:1)

一般情况下,你不能在主线程(UI线程)中进行网络调用,因此你得到NetworkOnMainThreadException。

IntentService确实从主线程中卸载了任务。但是你必须实现multiprocessing.Pipe()

检查文档:

请记住,任务将按顺序处理。如果你更灵活的控制使用原始服务并自己管理线程。

答案 2 :(得分:1)

如文档中所述:

onStartCommand

  

您不应该为IntentService重写此方法。相反,覆盖onHandleIntent(Intent),系统在IntentService收到启动请求时调用。

似乎onStartCommand用在mainthread上,而onHandleIntent是在工作线程上处理的:

  

这个"工作队列处理器"模式通常用于从应用程序的主线程卸载任务。存在IntentService类以简化此模式并处理机制。要使用它,请扩展IntentService并实现onHandleIntent(Intent)。 IntentService将接收Intents,启动工作线程,并根据需要停止服务。

参考

https://developer.android.com/reference/android/app/IntentService.html https://developer.android.com/reference/android/app/IntentService.html#onStartCommand(android.content.Intent, int, int)

答案 3 :(得分:0)

您的代码应该在

中执行
onHandleIntent()

方法。这是用于生成工作线程并对其进行操作的方法。

我还建议尝试将你的工作转移到

JobIntentService

由于Android O及以上版本有限制,当app不在前台或有任何可见组件时调用startService会引发异常。

JobIntentService与IntentService完全相似。不同之处在于它将在支持JobScheduler的设备上内部使用JobScheduler api。只需覆盖

即可
onHandleWork

方法并像使用IntentService一样使用它。使用

enqueueWork 

开始工作而不是startService