Android & Kotlin

Working with JobIntentService

Pinterest LinkedIn Tumblr

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 job is done.

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 name is MyJobIntentService which extends the JobIntentServcie.

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 project. MainActivity is automatically created. Now Open the MainActivity and expose OnClick() methods as same as to XML name.

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 one-second interval. Let’s run the project and click the Start JobIntentService button. You will “Job Execution Started ” toast will appear. Now check Logcat the result like below.

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

Download Sample Project – JobIntentService Sample App

8 Comments

  1. 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?

  2. 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

  3. 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

  4. Narasimham Reply

    it’s not working in oreo devices , after close the application it’s not running in background.

Write A Comment