-4

I am trying to do file downloading task using IntentService. I read that IntentService will create a worker thread and does the task requested. The following code will download a video file.

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

}

When i call startService from my MainActivity

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

I am getting NetworkOnMainThreadException error:

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) 

But if i try to run downloading part in a thread inside onStartCommand() its working fine and i was able to download the file.

Do i need to do network operation in separate Thread in case of IntentService every time?

vgokul129
  • 777
  • 12
  • 31
  • @JoxTraex: The question was very clear why there was NetworkOnMainThreadException for IntentService. I don't accept this is a duplicate. As IntentService is claimed to run on Worker Thread. – vgokul129 Dec 30 '17 at 18:54

4 Answers4

2

You should do your handling on a different thread by overriding onHandleIntent() method, and should not override the onStartCommand()

EDIT

You can see here how IntentService handles the onStartCommand for you, so you don't have to do it.

elmorabea
  • 3,243
  • 1
  • 14
  • 20
  • Can you tell me why do we have onStartCommand() method in IntentService ? Seems like onStartCommand is useless in case of IntentService. Please correct me if i was wrong. – vgokul129 Dec 30 '17 at 18:46
  • IntentService is just a subclass of Service class, so you inherit all the methods of Service class, including onStartCommand, but instead of handling threads yourself, IntentService does it for you, and give you the utility method of onHandleIntent on a background thread – elmorabea Dec 30 '17 at 18:53
  • onHandleIntent() is not getting called in case. Any idea why so ? – vgokul129 Dec 30 '17 at 18:57
  • 1
    Did you remove your override of all other service onStart methods? onStart() and onStartCommand ? – elmorabea Dec 30 '17 at 18:58
  • Oh yeah. onHandleIntent() called after removing onStartCommand() method completely. – vgokul129 Dec 30 '17 at 19:02
  • Can you tell me what impact does onStartCommand or onStart() over onHandleIntent() ? – vgokul129 Dec 30 '17 at 19:02
  • onStart is deprecated, instead in a normal servive (not IntentService) you should use onStartCommand, but the base IntentService handles this implementation for you, so if you want to handle each call in on a background thread and you want them to be queued, you should use IntentService and only override onHandleIntent(), if you want to handle threading yourself and handle when the service stops (stopSelf()) use Service instead – elmorabea Dec 30 '17 at 19:09
  • 1
    Thanks that's really useful. Thumb up. – vgokul129 Dec 30 '17 at 19:18
1

In general, you cannot do network calls in the main thread (UI Thread), so you get NetworkOnMainThreadException.

It is true that IntentService offloads tasks from the main thread. But you have to implement onHandleIntent(Intent).

Check documentation:

Keep in mind that tasks will be handled in the sequential order. If you what more flexible control use raw Service and manage threading yourself.

Maxim G
  • 1,479
  • 1
  • 15
  • 23
1

As noted in the documentation:

onStartCommand

You should not override this method for your IntentService. Instead, override onHandleIntent(Intent), which the system calls when the IntentService receives a start request.

It seems that onStartCommand is used on the mainthread while onHandleIntent is handled on the worker thread:

This "work queue processor" pattern is commonly used to offload tasks from an application's main thread. The IntentService class exists to simplify this pattern and take care of the mechanics. To use it, extend IntentService and implement onHandleIntent(Intent). IntentService will receive the Intents, launch a worker thread, and stop the service as appropriate.

Ref

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

JoxTraex
  • 13,423
  • 6
  • 32
  • 45
0

Your code should be executed in

onHandleIntent()

method. This is the method used to spawn a worker thread and do the work on it.

I would also suggest try shifting your work to

JobIntentService

As Android O and above will have a restriction where calling startService when app is not in foreground or has any visible component will throw exception.

JobIntentService is exactly similar to IntentService. The difference being that it'll internally use a JobScheduler api on devices which support the JobScheduler. Just override the

onHandleWork

Method and use it just like the IntentService. Use

enqueueWork 

To start the work instead of startService

Kushan
  • 5,855
  • 3
  • 31
  • 45