Timed tasks can be implemented in a number of ways on Android:

1. AlarmManager

Use the system’s “alarm” function for timing and heartbeat. The advantage of this service is that it is accurate enough, and can lock the screen depending on the type type, and even keep the heartbeat when shutdown using alarmManager.power_off_wakeup (this is a real use of hardware timing, Once the specified task execution time is reached, it will wake up the CPU to execute it. However, due to the influence of some SDK versions, some versions do not support it.

/** * Project name: not telling you * Package name: not telling you * File name: ${} * Author: Jerry ON 2016/12/7 15:42 * Email: [email protected] */
public class TimeTaskUtil {    
    private final AlarmManager mAlarManager;    
    public TimeTaskUtil(Context context) {        
      mAlarManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);    
    }    
    /** * Enable the polling service **@paramContext *@paramCLS Service Service object *@paramRequestCode specifies the requestCode *@paramIntervalSecond performs a task every few seconds *@paramBundle polls the service for the argument */    
    public void startPollingService(Context context, Class<? > cls, String action,int requestCode, int intervalSecond, Bundle bundle) {
        Intent intent = new Intent(context, cls);
        intent.setAction(action);
        intent.putExtra("bundle", bundle);
        PendingIntent pi = PendingIntent.getService( 
               context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT
        );
        mAlarManager.setRepeating(AlarmManager.ELAPSED_REALTIME,  SystemClock.elapsedRealtime(), intervalSecond * 1000, pi);
    } 
   /** * Stop polling service **@paramContext *@paramCLS polling service object *@paramAction Indicates the action */ of the task
    public void stopPollingService(Context context, Class<? > cls,int requestCode, String action) {
        Intent intent = newIntent(context, cls); intent.setAction(action); PendingIntent operationIntent = PendingIntent.getService( context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT ); mAlarManager.cancel(operationIntent); }}Copy the code

The above is only for heartbeat processing between AlarmManager and Service. AlarmManager can also perform similar heartbeat callback processing with activities, BroadcastReceiver and other components.


2. Timer in JDK

Can also be used to do polling heartbeat, not detailed, presumably friends are familiar with it.

3. Use Handler to perform polling

We can use this mechanism to implement the heartbeat polling effect, not to say directly on the source code:

/** * Project name: Jerry * Package name: Jerry * File name: ${PollingTask} * Author: Jerry on 2016/12/10 09:45 * email: [email protected] */
public class PollingTask {
    private static final String TAG = "PollingTask";
    private Handler mHandler;
    private InnerTask mInnerTask;
    private TaskObserver mTaskObserver;
    // The default heartbeat time is 5 seconds
    private static final int HEART_BEAT_RATE = 5;
    private long mHeartBeatRate;
    private TaskObservable mObservable;
    /** * Create task *@return. * /
    public PollingTask createTask(a){
        createTask(HEART_BEAT_RATE);
        return this;
    }
    /** * Create task *@paramHeartBeatRate Heartbeat duration, in seconds *@return. * /
    public PollingTask createTask(int heartBeatRate) {
        this.mHeartBeatRate = heartBeatRate * 1000;
        mHandler = new Handler();
        mInnerTask = new InnerTask(this);
        mTaskObserver = new TaskObserver();
        mObservable = new TaskObservable();
        mObservable.addObserver(mTaskObserver); 
       return this;
    }
    /** * Set the heartbeat task time *@paramHeartBeatRate Heartbeat duration, in seconds *@return. * /
    public PollingTask setHearBeatRate(int heartBeatRate){
        this.mHeartBeatRate = heartBeatRate * 1000;
        return this;
    }
    /** * link to start task *@return. * /
    public PollingTask connected(a) {
        if (mHandler == null || mInnerTask == null) {
            throw new RuntimeException("please call createTask method create polling task!");
        }
        if (mHeartBeatRate <= 0) {
            throw new RuntimeException("please set HeartBeatRate by setHearBeatRate method!");
        }
        mHandler.removeCallbacks(mInnerTask);
        mHandler.post(mInnerTask);
        return this;
    }
    /** * Notifies task completion */
    public void notifyTaskFinish(a){
        mObservable.notifyTaskFinish();
    }
    /** * Destroy task */
    public void destroyTask(a){
        if (mHandler == null || mInnerTask == null) {
            throw new RuntimeException("please call createTask method create polling task!");
        }
        mHandler.removeCallbacks(mInnerTask);
    }
    private class InnerTask implements Runnable {
        private WeakReference<PollingTask> mWeak;
        InnerTask(PollingTask wrapper) {
            mWeak = new WeakReference<>(wrapper);
        }
        @Override
        public void run(a) {
            PollingTask outClass = mWeak.get();
            if(outClass ! =null) {
                if(outClass.mOnTaskListener ! =null) {
                    // Start the taskoutClass.mOnTaskListener.executeTask(outClass); }}}}/** ** Task observer */
    private class TaskObserver implements Observer {
        @Override
        public void update(Observable observable, Object data) {
            // Through the observer mode, the observed task notified the task polling observer, which needed to update and start a new round of task execution. PostDelayed by handler was used to delay the heartbeat effectmHandler.postDelayed(mInnerTask, mHeartBeatRate); }}/** * The task being observed */
    private class TaskObservable extends Observable {
        public void notifyTaskFinish(a) {
            // Indicates a change in status or content delivery
            setChanged();
            // Notify all observersnotifyObservers(); }}private OnTaskListener mOnTaskListener; 
   /** * Monitor task status *@paramListener Listens to the interface */
    public PollingTask setOnTaskListener(OnTaskListener listener) {
        this.mOnTaskListener = listener;
        return this;
    }
    public interface OnTaskListener {
        void executeTask(PollingTask pollingTask); }}Copy the code
  • Using this library to create a heartbeat task is very simple, and it is a chained call method:
// Initialize a heartbeat task object
PollingTask mPushPollingTask = new PollingTask();
// Create a 60-second heartbeat task
mPushPollingTask.createTask(60)
        .connected()
        .setOnTaskListener(new PollingTask.OnTaskListener() {
            @Override
            public void executeTask(PollingTask pollingTask) {
                // Execute a task, either synchronous or asynchronous
                // For example, get the news feedgetPushNewsInfos(); }});/ * * * refresh rules data information * / private void getPushNewsInfos () {mApiWrapper. GetPushNewsInfos (url, new ApiWrapper.HttpCallback<List<NewsInfo>>() {@Overridepublic void success(List<NewsInfo> data) { savaNewsInfo(data); // The key step is to call the notifyTaskFinish method of pollingTask once a task has been executed to notify the task observer to update the task again. Otherwise, polling is invalid. Mpushpollingtask.notifytaskfinish (); }@Overridepublic void failure(Exception e) { mPushPollingTask .notifyTaskFinish(); }}); } / / the end need to be closed recycling a heartbeat task, as long as the call mPushPollingTask. DestroyTask ();Copy the code

Conclusion: There are two main knowledge points:

  1. Use handler’s circular queue processing mechanism to poll the heartbeat.
  2. The observer pattern decouples the execution of a specific task from the Runnable within the PollingTask class, because no task has to hold a Handler object to do the looping task execution. Here, each specific task is treated as an observed object, and once the task is completed, the observer is notified of mPollingTask.notifyTaskFinish (). This mechanism is a bit like notifyDataSetChanged() in Adatper of ListView and RecyclerView. Next time, we will analyze the implementation principle and mechanism of notifyDataSetChanged.

Existing problems:

  1. It is not appropriate to create a PollingTask for a heartbeat task
  2. A PollingTask can handle only one asynchronous task at a time. If more than one asynchronous task is executed in the executeTask method, there is a problem with the call timing of mPollingTask.notifyTaskFinish (). Because different tasks take different time to complete.

Solution: There is no clear solution yet. The simple idea is to reconstruct PollingTask, maintain a map of task and task tag internally, map the map corresponding to an observer and the observed at the same time, perform traversal to check whether the task has been executed, and notify the observer of the corresponding task to update the heartbeat status. Next time we will look at the implementation of the AndroidEventBus event bus library.