Core Android

Working with JobIntentService

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 to explain JobIntentService, We try to follow best practise 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 advanced background service that create a separate background thread to perform oprations 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 configration 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 destroyed automatically that time toast appear with message “Job Execution Finished”. That mean JobIntentService is running well when application is foreground. Here is noticeable point when app is foreground IntentService also work well, It (IntentService) crash when app in background but It 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() mothods. As you this code will start the service when device tiggered BOOT_COMPLETED event. This is the way of checking JobIntentService is runs in 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 memorise earlier explain 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 verfication, 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

2
Leave a Reply

avatar
1 Comment threads
1 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
2 Comment authors
MorrisNarasimham Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Narasimham
Guest
Narasimham

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

Morris
Guest
Morris

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/