AsyncTask with Broadcast Receiver in Android:
What we are going to do ?
We are going to see an example for AsyncTask in Android with Broadcastreceiver. In our example we will be fetching the operator and circle information for the received
new SMS sender phone number.
Table of Contents
What is AsyncTask & BroadcastReceiver?
AsyncTask:
AsyncTask is basically to do a aynchronous jobs in android, which is nothing but the background processes. We can use AsynCTask to call URL’s, to fetch some details from internet and any business logics which needs some URL to be called/some details need to be sent to any URL’s.
BroadCast Receiver:
Broadcast receiver is basically to observe the incoming requests such as messages/bluetooth requests etc. If we want any business logics on receiving new messages then onReceive method of BroadCast Receiver can help us to achieve it.
But we need to ensure always that the related permissions are added into the “AndroidManifest.xml“
Below are the required permissions
To read the incoming messages,
<uses-permission android:name="android.permission.WRITE_SMS" /> <uses-permission android:name="android.permission.READ_SMS" /> <uses-permission android:name="android.permission.RECEIVE_SMS" />
To read/write files in the SDCard,
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Internet, Network related permissions [wifi],
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Do we have anything like AsyncTask in android?
Yes. We have services, handlers and threads for similar requirements and out of all “services and AsyncTask” is the most recommended for any background process requirments in android.
When should I use What ?
AsyncTask: If your business logics need to be completed in 10Secs or it’s just a information fetching from internet like “fetching some result”, “sending messages through API”, “Fetching cricket score” etc.
Services: If you want to run the background processes for a long time says more than 30 secs to fetch wether details(background process should run all the time) or to download any files from internet etc.,
Note: I have used jSoup-1.7.2.jar to hit the URL and read the responses.
How to add an External Jar into the android project ?
1. Paste the jSoup.jar to the libs folder, libs folder exist in app folder.
Project structure for your reference,
2. Right click on the pasted jSoup JAR and Add as Library… to add the jar into the build path.
Add to module –> ok
How to create the apk file from the command prompt ?
1. Go to the project path in the command prompt
2. Run the “gradlew.bat assembleDebug” command
3. Now the apk file will be generated in the below path,
PROJECT\app\build\outputs\apk
URL to get the circle and Operator Details:
We have called the above URL in our AsyncTask extended below class to get the operator and circle info. It’s just an example to hit the URL and to get some response. So exact operator and circle informations will not come.
doInBackground | onPostExecute | onPreExecute
When you execute this line, it will call the
AsyncTask extended class
FetchOprCircleInfo
doInBackground
method, which receives the inputs as String array. Its completely an independent process to the UI threads, once this process is done, it will return the response to the
onPostExecute
method, where you can write the UI display logics if any. If you want to display any Progress Dialog of the background process status [which is mandatory when your application is downloading something from the internet], then you can override the
onPreExecute
method and show the progress, this will be called whenever the publish method is called.
fetchOprCircleInfo.execute(new String[]{jdCircleOprInfoURL});
MainActivity.java source code:
package com.ngdeveloper.asynctaskexample; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.AsyncTask; import android.os.Bundle; import android.telephony.SmsMessage; import android.widget.TextView; import android.widget.Toast; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; public class MainActivity extends Activity { private TextView incomeNum, incomeMsg,numOpr, numCircle; IntentFilter iFilter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED"); registerReceiver(myReceiver, iFilter); incomeNum = (TextView) findViewById(R.id.Income_Msg_Number); numOpr = (TextView) findViewById(R.id.Num_Operator); numCircle = (TextView) findViewById(R.id.Num_Circle); incomeMsg = (TextView) findViewById(R.id.Income_Msg); } private final BroadcastReceiver myReceiver = new BroadcastReceiver() { public static final String SMS_BUNDLE = "pdus"; @Override public void onReceive(Context context, Intent intent) { Bundle intentExtras = intent.getExtras(); if (intentExtras != null) { Object[] sms = (Object[]) intentExtras.get(SMS_BUNDLE); String smsMessageStr = ""; for (int i = 0; i < sms.length; ++i) { SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) sms[i]); String smsBody = smsMessage.getMessageBody().toString(); // SMS body content if (null!=smsBody) { incomeMsg.setText("SMS Content :"+smsBody); } String address = smsMessage.getOriginatingAddress(); smsMessageStr += "SMS From: " + address + "\n"; Toast.makeText(context, smsMessageStr, Toast.LENGTH_LONG).show(); incomeNum.setText("SMS Received From :"+address.substring(3,address.length())); try { String jdCircleOprInfoURL = "http://demo.ngdeveloper.com/AsyncTaskAndroid/GetCircleOpr.php?input="+address.substring(3,address.length())+""; FetchOprCircleInfo fetchOprCircleInfo = new FetchOprCircleInfo(); fetchOprCircleInfo.execute(new String[]{jdCircleOprInfoURL}); } catch (Exception e) { Toast.makeText(context, "Javadomain.in-->" + e.getMessage(), Toast.LENGTH_LONG).show(); } } } } }; @Override public void onDestroy() { super.onDestroy(); unregisterReceiver(myReceiver); } public class FetchOprCircleInfo extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... reqArray) { String responseStatus = ""; try { if(reqArray!=null) { if (null != reqArray[0]) { // Jsoup used here Document doc = Jsoup.connect(reqArray[0]).timeout(0).get(); if (doc != null) { String result = doc.select("body").text(); responseStatus = result; } } } } catch (Throwable t) { t.printStackTrace(); } return responseStatus; } @Override protected void onPostExecute(String result) { String opr = ""; String circle = ""; if(null!=result && !result.isEmpty()){ if(result.contains("|")){ String[] splitPipe = result.split("\\|"); opr = splitPipe[0]; circle = splitPipe[1]; } } numOpr.setText("Operator : "+opr.toString()); numCircle.setText("Circle : "+circle.toString()); } } }
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="in.javadomain.asynctaskexample" > <uses-permission android:name="android.permission.WRITE_SMS" /> <uses-permission android:name="android.permission.READ_SMS" /> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="AsyncTaskExample" 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> </application> </manifest>
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:id="@+id/MainLayout"> <TextView android:layout_width="wrap_content" android:layout_height="30dp" android:textAppearance="?android:attr/textAppearanceLarge" android:text="Javadomain.in AsyncTask" android:id="@+id/textView" android:layout_gravity="center_horizontal" android:layout_marginTop="30dp" /> <TextView android:id="@+id/Income_Msg_Number" android:layout_width="match_parent" android:layout_height="30dp" android:text="" android:height="10dp" android:layout_marginLeft="20dp" android:layout_marginTop="50dp"> </TextView> <TextView android:id="@+id/Income_Msg" android:layout_width="match_parent" android:layout_height="30dp" android:text="" android:height="10dp" android:layout_marginLeft="20dp" android:layout_marginTop="50dp"> </TextView> <TextView android:id="@+id/Num_Operator" android:layout_width="match_parent" android:layout_height="30dp" android:text="" android:layout_marginLeft="20dp" android:layout_marginTop="10dp"> </TextView> <TextView android:id="@+id/Num_Circle" android:layout_width="match_parent" android:layout_height="30dp" android:text="" android:layout_marginLeft="20dp" android:layout_marginTop="10dp"> </TextView> </LinearLayout>
Output:
Is it possible to call more than one AsyncTask ?
AsyncTask inside Asynctask like writting many URL hitting/accessing the internet one by one in doInBackground is bad practise and I could not make it work the way I want when I tried.
So if you want to try more than one URL hitting then you can do in the below way,
Write all your business logics in one file [accessing the internet many times with many URL’s] then move this file to your server.
Now you can hit only this URL, which internally hits all the other URL’s and returns the results. Which you can access in onPostExecute method.
If you want to continue any of your business logics or UI display works you can write all those logics/codes in onPostExecute method.
Note:
You can use more than one AsyncTask, but if all AsyncTask’s are dependent with each other, then we can not make it the way we want, since its a asynchronous process and result can not be sequentially provided to the next AsyncTask to continue the process. Which may take more time at the background which may cause the ANR (Application Not Responding) issue.
If all the processes are independent then we can have more than one AsyncTask, but still there may be a chance for ANR.
How to return the onPostExecute response to UI ?
- You can directly access the TextView inside the onPostExecute method and set the results like below, [Above sample program use this way to get the response from the onPostExecute method and displays in the UI thread.]
private TextView txtView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); txtView = (TextView) findViewById(R.id.txtViewID); }
then in onPostExecute method,
@Override protected void onPostExecute(String result) { numOpr.setText("AsyncTask Response : "+result); }
Below are the solutions shared in the stackoverflow.com site for the onPostExecute result return issue, so try and share your comments,
- But still if you have BroadCastReceiver in one class and AsyncTask logics in one class then you have to make use of the interface to get the onPostExecute method result to proceed with the futher logics.
- The above same requirement can also be done using the getters/setters and the mainactivity should be passed to the AsyncTask constructor to initialize and set the value to the setters. Then whereever we want we can access the getters and access the AsyncTask returned responses.
super not called exception:
If you are getting the super not called exception then ensure that you have this below line, inside the onDestory() method. Always super.onDestory() should be called in first line.
super.onDestroy();
@Override public void onDestroy() { super.onDestroy(); unregisterReceiver(myReceiver); }
Share your comments in comments section if any……