Monday 13 February 2017

Task Scheduling in Android Via JobScheduler

Hello Guys!!! Hope You all doing well.
Today I am going to discuss JobScheduler in Android, its uses and implementation.
Obviously our first question what is JobScheduler ? What is Task Scheduling ?
Our Android apps, based on our requirement, can perform many of their tasks asynchronously, outside the direct flow of user interaction.
As for examples:

  • Updating network resources.
  • Downloading information.
  • Updating background tasks.
  • Scheduling system service calls.

Scheduling this work intelligently can improve our app’s performance, along with device battery life.
That's why JobScheduler does this scheduling work for us.
Options for scheduling
Following are the avilable way to schedule tasks in Android

  • JobScheduler API.
  • (outdated) AlarmManager
  • Firebase JobDispatcher
  • SyncAdapter

The job scheduler API
In Android 5.0 Lollipop (API 21) released job scheduler API via the JobScheduler class. This API allows to batch jobs when the device has more resources available. In general this API can be used to schedule everything that is not time critical for the user.

In Android 7 (N API 24) it become Android Framework JobScheduler
As per android developer website Explanation is like below:-
JobScheduler is the Android framework API for scheduling tasks or work. It first became available in Android 5.0 (API level 21), and remains under active development. Notably, Android 7.0 (API level 24) added the ability to trigger jobs based on ContentProvider changes.

JobScheduler is implemented in the platform, which allows it to collect information about jobs that need to run across all apps. This information is used to schedule jobs to run at, or around, the same time. Batching job execution in this fashion allows the device to enter and stay in sleep states longer, preserving battery life.
Advantages of the job scheduler API
  • The JobScheduler supports batch scheduling of jobs, that is not possible via custom SyncAdapter or the alarm manager.
  • By using JobScheduler android system can combine jobs so that battery consumption is reduced.
  • JobManager makes handling uploads easier as it handles automatically the unreliability of the network.
  • It also survives application restarts.
Example when to use job scheduler:
  • Tasks that should be done once the device is connect to a power supply
  • Tasks that require network access or a Wi-Fi connection.
  • Task that are not critical or user facing
  • Tasks that should be running on a regular basis as batch where the timing is not critical
We can schedule the task to run under specific conditions, such as:
  • Device is charging
  • Device is connected to an unmetered network
  • Device is idle
  • Start before a certain deadline
  • Start within a predefined time window, e.g., within the next hour
Inline image 1
job service life cycle

Hands On
Let us create a simple application that uses JobScheduler API. You can download full source code.
1. Creating the Job Service
To start, we are going to create a new Android project with a minimum required API of 21, because the JobScheduler API was added in the most recent version of Android
Dowload Source code



package com.example.saurabh.blogapplication;

import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.Context;
import android.content.Intent;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;

/**
 * Created by ee208840 on 16/5/17.
 */
//Require API Level 21
public class MyJobService extends JobService {

    private static final String TAG = "MyJobService";

    // this is required method for JobService
    // This method is called when the scheduled job
    // is started
    @Override
    public boolean onStartJob(JobParameters params) {
        Log.d(TAG, "on start job...");
        Toast.makeText(this, "MyJobService.onStartJob()", Toast.LENGTH_SHORT).show();
          /*
      * True - if your service needs to process
     * the work (on a separate thread).
     * False - if there's no more work to be done for this job.
     */
        return false;
    }

    // this is required method for JobService
    // This method is called when the scheduled job
    // is stopped
    @Override
    public boolean onStopJob(JobParameters params) {
        Log.d(TAG, "on stop job....");
        Toast.makeText(this, "MyJobService.onStopJob()", Toast.LENGTH_SHORT).show();
        return false;
    }


    // This method is called when the service instance
    // is created
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, " myJobService created....");
    }

    // This method is called when the service instance
    // is destroyed
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "myJobService destroyed...");
    }

    MainActivity myMainActivity;

    public void setUICallback(MainActivity activity) {
        myMainActivity = activity;
    }

    // This method is called when the start command
    // is fired
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "myJobService onStartCommand called...");
        Messenger callback = intent.getParcelableExtra("messenger");
        Message m = Message.obtain();
        m.what = 2;
        m.obj = this;
        try {
            callback.send(m);
        } catch (RemoteException e) {
            Log.e(TAG, "Error passing service object back to activity.");
        }
        return START_NOT_STICKY;

    }

    // Method that schedules the job
    public void scheduleJob(JobInfo build) {
        Log.d(TAG, "Scheduling job...");
        JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        jobScheduler.schedule(build);
    }
}

package com.example.saurabh.blogapplication;

import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.util.List;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button jobScheduleButton;
    private Button btnStartJob;
    private Button btnCancelJobs;

    ComponentName myServiceComponent;
    MyJobService myJobService;
    Handler myHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            myJobService = (MyJobService) msg.obj;
            myJobService.setUICallback(MainActivity.this);
        }
    };

    // Second Example
    JobScheduler jobScheduler;
    private static final int MYJOBID = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initRes();

        myServiceComponent = new ComponentName(this, MyJobService.class);
        Intent myServiceIntent = new Intent(this, MyJobService.class);
        myServiceIntent.putExtra("messenger", new Messenger(myHandler));
        startService(myServiceIntent);

        //Second example
        jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);

    }

    @Override
    public void onClick(View v) {
        if (v == jobScheduleButton) {
            JobInfo.Builder builder = new JobInfo.Builder(0, myServiceComponent);
            builder.setRequiresCharging(true);
            //builder.setRequiresDeviceIdle(true);
            //builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
            myJobService.scheduleJob(builder.build());
        }
        if (v == btnStartJob) {
            ComponentName jobService =
                    new ComponentName(getPackageName(), My2ndJobService.class.getName());
            JobInfo jobInfo =
                    new JobInfo.Builder(MYJOBID, jobService).setPeriodic(10000).build();
    /*
     * setPeriodic(long intervalMillis)
     * Specify that this job should recur with the provided interval,
     * not more than once per period.
     */
            int jobId = jobScheduler.schedule(jobInfo);
            if (jobScheduler.schedule(jobInfo) > 0) {
                Toast.makeText(MainActivity.this, "Successfully scheduled job: "
                        + jobId, Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(MainActivity.this, "RESULT_FAILURE: "
                        + jobId, Toast.LENGTH_SHORT).show();
            }
        }
        if (v == btnCancelJobs) {
            List<JobInfo> allPendingJobs = jobScheduler.getAllPendingJobs();
            String s = "";
            for(JobInfo j : allPendingJobs){
                int jId = j.getId();
                jobScheduler.cancel(jId);
                s += "jobScheduler.cancel(" + jId + " )";
            }
            Toast.makeText(MainActivity.this, s,
                    Toast.LENGTH_SHORT).show();

            //or
            //jobScheduler.cancelAll();
        }
    }

    private void initRes() {
        jobScheduleButton = (Button) findViewById(R.id.buttonScheduleJob);
        jobScheduleButton.setOnClickListener(MainActivity.this);
        btnStartJob = (Button) findViewById(R.id.startJob_btn);
        btnStartJob.setOnClickListener(MainActivity.this);
        btnCancelJobs = (Button) findViewById(R.id.cancelJobs_btn);
        btnCancelJobs.setOnClickListener(MainActivity.this);
    }

}
package com.example.saurabh.blogapplication;

import android.app.job.JobParameters;
import android.app.job.JobService;
import android.widget.Toast;

/**
 * Created by ee208840 on 16/5/17.
 */

public class My2ndJobService extends JobService{

    public My2ndJobService() {
    }

    @Override
    public boolean onStartJob(JobParameters params) {

        Toast.makeText(this, "My2ndJobService.onStartJob()", Toast.LENGTH_SHORT).show();
  /*
   * True - if your service needs to process
   * the work (on a separate thread).
   * False - if there's no more work to be done for this job.
   */
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {

        Toast.makeText(this, "My2ndJobService.onStopJob()", Toast.LENGTH_SHORT).show();

        return false;
    }
}

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="com.example.saurabh.blogapplication.MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="10dp"
        android:text="Hello Job Scheduler"
        android:textColor="#000000"
        android:textStyle="bold" />

    <Button
        android:id="@+id/buttonScheduleJob"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="10dp"
        android:layout_below="@+id/textView"
        android:textAllCaps="false"
        android:text="Schedule Job" />

    <TextView
        android:id="@+id/second_txt_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/buttonScheduleJob"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="10dp"
        android:textColor="#000000"
        android:textStyle="bold"
        android:text=".... Second Example on Job Scheduler ..."/>

    <Button
        android:id="@+id/startJob_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="10dp"
        android:layout_below="@+id/second_txt_view"
        android:text="Start a Job" />

    <Button
        android:id="@+id/cancelJobs_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_below="@+id/startJob_btn"
        android:layout_centerHorizontal="true"
        android:text="Cancel all Jobs" />


</RelativeLayout>
Please go through inline comment in above code. It will help you to understand Jobscheduler better.
Thanks
Saurabh
Happy Coding!!!!

2 comments:

  1. Very useful information that you have shared and it is very useful to me.Thanks for sharing the information with us.

    best mobile app development company in chennai

    ReplyDelete
  2. Apply job easily in your desired profile through Theincircle.com Mobile App from Google play store.

    ReplyDelete

Build a Custom Kernel Module for Android

Hi Guys!!!Hope you are doing well !!!. Today I will describe how you can write a custom kernel module(Hello world) for Android and load it a...