In our previous article, I have explained the limitation of using the IntentService in Android Oreo devices. To overcome this problem android introduced JobIntentService. In this article, I’m gonna explain JobIntentService, We try to follow best practice of JobIntentService . At last, We will create a JobIntentService sample app as well. Scroll last for download JobIntentService Sample APK
Intent Service
The Intent Service is an advanced form of background service. It creates a separate working thread to perform background operation. Once his job is finished is automatically destroyed. In other words, you can say IntentService is an advanced background service that creates a separate background thread to perform operations and automatically destroyed when
What is the JobIntentService
In my opinion, JobIntentService is the modern way of using the background service to get our task done. The IntentService does not work well in our Oreo devices, That is it made our application crash in the Oreo devices. To overcome this problem android introduced JobIntentService that works well in all the devices. in other words, You can say JobIntentService is a modern way to run the background service from the background application.
Properties of the JobIntentService
- Min Support Version – It works in starting from API 14 onwards, So without any hesitation, you can use the JobIntentService for all your target application, That why it is known as a modern way to run the background service.
- Compatibility -The JobIntentService is on the pre Oreo devices it provides the functionality similar to that of the IntentService, but it has some exceptional Behaviour for the Oreo devices. that is it internally uses the job scheduler API.
- JobSchedulerAPI – JobScheduler API is another way to perform the background task and operations and Read our another article for more clarity of JobSchedulerAPI. Now, do not think much about JobScheduler API, because the JobIntentService is internally used this API so we don’t have to worry about its implementation.
- Pre Oreo and Onward – The job intensive is so it simply uses the functionality of the JobIntentServcie for the pre Oreo devices (Build Version <= 25 API ). But for the Oreo devices literally uses the JobScheduler API internally.
JobIntentService sample app
Step 1. Create a new class which is subclass of JobIntentService
Just Open the Android Studio, Go to File menu and create a new project, fill the project name (I’m using JobIntentService Example) and select the EmptyActivity template. Now create a new file with
package com.wave.jobintentserviceexample; import android.content.Intent; import android.support.annotation.NonNull; import android.support.v4.app.JobIntentService; /** * Created on : Feb 22, 2019 * Author : AndroidWave */ public class MyJobIntentService extends JobIntentService { @Override protected void onHandleWork(@NonNull Intent intent) { } }
Step2. Now override the onHandleWork() methods and expose enqueueWork() methods like below.
private static final String TAG = "MyJobIntentService"; /** * Unique job ID for this service. */ private static final int JOB_ID = 2; public static void enqueueWork(Context context, Intent intent) { enqueueWork(context, MyJobIntentService.class, JOB_ID, intent); }
@Override protected void onHandleWork(@NonNull Intent intent) { /** * Write code here.. Perform Long operation here such as Download/Upload of * file, Sync Some data * The system or framework is already holding a wake lock for us at this point */ }
Step 3 . Place your logic here
Suppose we want to print 1 to 1000 number with a one-second interval, Each task will take time 1 sec, So here now sleeping thread for one second. So complete class looks like below.
package com.wave.jobintentserviceexample; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.support.annotation.NonNull; import android.support.v4.app.JobIntentService; import android.util.Log; import android.widget.Toast; public class MyJobIntentService extends JobIntentService { final Handler mHandler = new Handler(); private static final String TAG = "MyJobIntentService"; /** * Unique job ID for this service. */ private static final int JOB_ID = 2; public static void enqueueWork(Context context, Intent intent) { enqueueWork(context, MyJobIntentService.class, JOB_ID, intent); } @Override public void onCreate() { super.onCreate(); showToast("Job Execution Started"); } @Override protected void onHandleWork(@NonNull Intent intent) { /** * Write code here.. Perform Long operation here such as Download/Upload of file, Sync Some data * The system or framework is already holding a wake lock for us at this point */ int maxCount = intent.getIntExtra("maxCountValue", -1); /** * Suppose we want to print 1 to 1000 number with one-second interval, Each task will take time 1 sec, So here now sleeping thread for one second. */ for (int i = 0; i < maxCount; i++) { Log.d(TAG, "onHandleWork: The number is: " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void onDestroy() { super.onDestroy(); showToast("Job Execution Finished"); } // Helper for showing tests void showToast(final CharSequence text) { mHandler.post(new Runnable() { @Override public void run() { Toast.makeText(MyJobIntentService.this, text, Toast.LENGTH_SHORT).show(); } }); } }
Step 4. Do following configuration in AndroidManifest.xml file
- For Pre-Oreo devices => We have to set uses permission – WAKE_LOCK permission
- For Oreo device => you have to declare android.permission.BIND_JOB_SERVICE
Just open the manifest file and add above things
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.wave.jobintentserviceexample"> <uses-permission android:name="android.permission.WAKE_LOCK" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" tools:ignore="GoogleAppIndexingWarning"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".MyJobIntentService" android:permission="android.permission.BIND_JOB_SERVICE" /> </application> </manifest>
5. For running this JobIntentService add button activity_main.xml
Open the activity_main.xml add button and set onClickMethods as onStartJobIntentService like this
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:background="@color/bg" android:letterSpacing="0.2" android:onClick="onStartJobIntentService" android:padding="16dp" android:text="Start JobIntentService" android:textAllCaps="false" android:textColor="#ffffff" android:textSize="16sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.497" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.845" /> <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="16dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" app:layout_constraintBottom_toTopOf="@+id/button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/oreo_icon" /> </android.support.constraint.ConstraintLayout>
6. Expose OnClick methods in MainActivity
As you remember I have selected EmptyActivity template while creating
package com.wave.jobintentserviceexample; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onStartJobIntentService(View view) { Intent mIntent = new Intent(this, MyJobIntentService.class); mIntent.putExtra("maxCountValue", 1000); MyJobIntentService.enqueueWork(this, mIntent); } }
As you saw, I have passing maxCountValue with Intent. as our logic, this service will print 1 to 1000 value with
D/MyJobIntentService: onHandleWork: The number is: 1 D/MyJobIntentService: onHandleWork: The number is: 2 D/MyJobIntentService: onHandleWork: The number is: 3 D/MyJobIntentService: onHandleWork: The number is: 4 .... .... D/MyJobIntentService: onHandleWork: The number is: 998 D/MyJobIntentService: onHandleWork: The number is: 999
After the job execution, The service will be destroyed automatically that time toast appears with the message “Job Execution Finished”. That means JobIntentService is up and running when the application is foreground. Here is a noticeable point when app is foreground IntentService also works well, It (IntentService) crash when the app in background but It does not happen with JobIntentService.
Now I’m trying to run this service when app is in background state. Let’s start this service with help of the BootComplete receiver.
7. Create a BootComplete Receiver
Create a new BroadcastReceiver with named is BootCompleteReceiver including override onReceive() methods. After that, copy the start JonIntentService button onClick() code into onReceive() methods. As you this code will start the service when device triggered BOOT_COMPLETED event. This is the way of checking JobIntentService is runs in a background app or not.
package com.wave.jobintentserviceexample; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class BootCompleteReceiver extends BroadcastReceiver { @Override public void onReceive(Context mContext, Intent intent) { if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { Intent mIntent = new Intent(mContext, MyJobIntentService.class); mIntent.putExtra("maxCountValue", 1000); MyJobIntentService.enqueueWork(mContext, mIntent); } } }
8. Finally, Registred the Receiver inside the Manifest file.
<receiver android:name=".BootCompleteReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver>
Now run the project, and memorize earlier explain the scenario. So we have to test when app is not in foreground JobIntentService is running well or not. Just restart the device. When the device will reboot BOOT_COMPLETED event will be triggered and receive in BootCompleteRecevier and start Service code will execute. Our service will also restart. for verification, you check on the screen “Job Execution Started” message toast will appear(check Logcat also). Hence proved JobInentService is run well when the app is background as well as the foreground. Happy Coding 🙂
Read our other article on Background Limitation on Android Oreo
8 Comments
Android Studio reports for ‘public static void enqueueWork’ that Inner classes cannot have static declarations. either this can’t be static or MyJobIntentService needs to be static – which should it be?
Bootreceiver code is not working in chinese devices
Nice tutorial bro
hi , i run the code of printing 1 to 100 using jobintentservice what i hv noted is sometimes it completed the task and destoryed well,
but sometime it stopped like when remove from recent list and when open app again it started whole process from starting ,
and Of course sometime it completed the task and didn’t destroyed .
can anybody have answer regarding this how to solve this behavior changes by the way right now i am testing it in lollipop not oreo and above
@Sunil JobIntentService is stoped on force close (or kill) application. If you looking for unstoppable service, that not be stoped on force kill or force close, You have to use Foreground Service or WorkManager. Same site you can search demo of these
hi, i tested this app in andorid lollipop it is running well when the app is in foreground and in recent list, but i am noticing that when i am clearing it from recent it didn’t work and stopped and then i Open the app again and it is restarted from printing 1 to 100 log value , can you pls explain why this unneccessary behaviour of this service in lollipop and i didn’t tested it now in oreo and above
it’s not working in oreo devices , after close the application it’s not running in background.
JobIntentService is automatically stopped while your task gets done. If you want to perform some long running task then use WorkManager
https://androidwave.com/android-workmanager-tutorial/