Android IntentService: Working and advantages of using them
IntentService
handles asynchronous requests sent as Intents on demand basis. Request is made via startService(Intent)
call. Service is started if needed and it will handled all the requests in turn using a background thread. It will stop by itself as soon as it is done performing the task.
For using IntentService, create a class which extends IntentService and implement onHandleIntent(Intent)
. It will receive requests and handle them in background thread.
IntentService works on “work queue processor” mechanism and help to take load off from your application main thread. A single background thread is used to handle all the requests and requests are processed one by one. It might take time to process one request and other one has to wait for that time.
IntentService should be used if we don’t want our service to handle multiple requests simultaneously. For using IntentService, all we need to do is implement onHandleIntent(Intent)
method to perform the requested work and provide a constructor for service which must call super("WorkerThreadName")
.
Sample IntentService Application
We will create an application using IntentService which will download image from internet in background thread of IntentService. We will have main activity with one EditText to specify url of image and one button to start IntentService from onClick()
event.
Lets start with res/layout/activity_main.xml
. Modify your layout file as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.androidsrc.sampleintentservice.MainActivity" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="20dp" android:layout_marginTop="20dp" android:orientation="vertical" > <EditText android:id="@+id/urlText" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="please enter img url or default will be used" android:inputType="textUri" /> <ProgressBar android:id="@+id/downloadPD" android:layout_width="match_parent" android:layout_height="wrap_content" android:indeterminateOnly="true" android:visibility="gone" /> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="onClick" android:text="start download with IntentService" /> </LinearLayout> <ImageView android:id="@+id/imgView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:scaleType="fitCenter" /> </LinearLayout> |
We need to declare our service in Manifest. And since we will be downloading image from internet we need to take care of permissions as well. Add permission android.permission.INTERNET
for internet, android.permission.READ_EXTERNAL_STORAGE
and android.permission.WRITE_EXTERNAL_STORAGE
for reading and writing to storage.
Update your AndroidManifest.xml
as below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.androidsrc.sampleintentservice" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="com.androidsrc.sampleintentservice.SampleIntentService" android:exported="false" > </service> </application> </manifest> |
Let’s move over to MainActivity.java
, We need to implement View.OnClickListener
and override onClick(View)
method to start IntentSevice from there. After completion of task in IntentService, If required we need to communicate between Activity and IntentService. We can use simple BroadcastReceiver in our Activity class. We can register it while creating activity with specific IntentFilter, Intent braodcasted from IntentSevice after completion of task can be received in BroadcastReceiver.
In this article, we will use ResultReceiver
for communication between Activity and IntentService. With IntentService start intent, we will add extras to intent for url and ResultReceiver. If ResultReceiver object passed to IntentService has Handler from Activity context, it can be used to sending result to Activity using send(int resultCode, Bundle resultData)
.
Modify your MainActivity.java
as below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
package com.androidsrc.sampleintentservice; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.os.Handler; import android.os.ResultReceiver; import android.text.TextUtils; import android.view.View; import android.view.View.OnClickListener; import android.widget.EditText; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.Toast; public class MainActivity extends Activity implements OnClickListener { EditText urlText; ProgressBar pd; ImageView imgView; SampleResultReceiver resultReceiever; String defaultUrl = "http://developer.android.com/assets/images/dac_logo.png"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); resultReceiever = new SampleResultReceiver(new Handler()); urlText = (EditText) findViewById(R.id.urlText); pd = (ProgressBar) findViewById(R.id.downloadPD); imgView = (ImageView) findViewById(R.id.imgView); } private class SampleResultReceiver extends ResultReceiver { public SampleResultReceiver(Handler handler) { super(handler); } @Override protected void onReceiveResult(int resultCode, Bundle resultData) { switch (resultCode) { case SampleIntentService.DOWNLOAD_ERROR: Toast.makeText(getApplicationContext(), "error in download", Toast.LENGTH_SHORT).show(); pd.setVisibility(View.INVISIBLE); break; case SampleIntentService.DOWNLOAD_SUCCESS: String filePath = resultData.getString("filePath"); Bitmap bmp = BitmapFactory.decodeFile(filePath); if ( imgView != null & amp;& bmp != null){ imgView.setImageBitmap(bmp); Toast.makeText(getApplicationContext(), "image download via IntentService is done", Toast.LENGTH_SHORT).show(); } else{ Toast.makeText(getApplicationContext(), "error in decoding downloaded file", Toast.LENGTH_SHORT).show(); } pd.setIndeterminate(false); pd.setVisibility(View.INVISIBLE); break; } super.onReceiveResult(resultCode, resultData); } } @Override public void onClick(View v) { Intent startIntent = new Intent(MainActivity.this, SampleIntentService.class); startIntent.putExtra("receiver", resultReceiever); startIntent.putExtra("url", TextUtils.isEmpty(urlText.getText()) ? defaultUrl : urlText .getText().toString()); startService(startIntent); pd.setVisibility(View.VISIBLE); pd.setIndeterminate(true); } } |
Create a class named SampleIntentService.java
which will extend IntentService. Add constructor in order to specify name for backround thread and implement onHandleIntent(Intent)
. In onHandleIntent(Intent), we will retrieve ResultReceiver and url string from intent extras. Open HttpURLConnection
for retreived url and write InputStream from connection to FileOutputStream directing to File on device. If task is completed or failed, we notify it via ResultReceiver object.
Your final code should look like as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
package com.androidsrc.sampleintentservice; import android.app.IntentService; import android.content.Intent; import android.os.Bundle; import android.os.ResultReceiver; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public class SampleIntentService extends IntentService { public static final int DOWNLOAD_ERROR = 10; public static final int DOWNLOAD_SUCCESS = 11; public SampleIntentService() { super(SampleIntentService.class.getName()); } @Override protected void onHandleIntent(Intent intent) { String url = intent.getStringExtra("url"); final ResultReceiver receiver = intent.getParcelableExtra("receiver"); Bundle bundle = new Bundle(); File downloadFile = new File("/sdcard/IntentService_Example.png"); if (downloadFile.exists()) downloadFile.delete(); try { downloadFile.createNewFile(); URL downloadURL = new URL(url); HttpURLConnection conn = (HttpURLConnection) downloadURL .openConnection(); int responseCode = conn.getResponseCode(); if (responseCode != 200) throw new Exception("Error in connection"); InputStream is = conn.getInputStream(); FileOutputStream os = new FileOutputStream(downloadFile); byte buffer[] = new byte[1024]; int byteCount; while ((byteCount = is.read(buffer)) != -1) { os.write(buffer, 0, byteCount); } os.close(); is.close(); String filePath = downloadFile.getPath(); bundle.putString("filePath", filePath); receiver.send(DOWNLOAD_SUCCESS, bundle); } catch (Exception e) { receiver.send(DOWNLOAD_ERROR, Bundle.EMPTY); e.printStackTrace(); } } } |