Lifka
Lifka

Reputation: 249

How to call a specific class method using AlarmManager?

I would like execute an action, wait n seconds, execute another action, wait again, etc (asynchronously).

Firstly, I got it using AsyncTask. The problem is that I need it occurs even when the device is locked (because the goal is to send orders to another device).

Then I've thought to use AlarmManager, doing something like that:

public class MainClass {
    private static MainClass instance;
    private static Context context;

    private MainClass(){}

    public static MainClass getInstance(Context context){
        MainClass.context = context;
        if (instance == null) instance = new MainClass();
        return instance;
    }

    // [...]

    private Alarm alarm = null;
    private static Worker worker = null ;
    private static Handler my_handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message message) {
            worker.executeNextAction();
        }
    };

    public void start(){
        alarm = new Alarm();
        worker = new Worker();
        worker.executeNextAction();
    }

    public static class Alarm extends BroadcastReceiver
    {
        @Override
        public void onReceive(Context context, Intent intent)
        {
            my_handler.sendMessage(new Message());
        }

        public void setAlarm(Context context, int seconds)
        {
            AlarmManager alarm_manager =( AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
            Intent intent = new Intent(context, Alarm.class);
            PendingIntent pending_intent = PendingIntent.getBroadcast(context, 0, intent, 0);
            alarm_manager.set(AlarmManager.RTC_WAKEUP, 1000 * seconds, pending_intent);
        }

        public void cancelAlarm(Context context) { /*[...]*/ }
    }

    private class Worker {
        void executeNextAction() {
            // do tasks
            alarm.setAlarm(context, duration);
        }
        // [...]
    }
}

This does not work because worker would be null in handleMessage().

How I can call the method executeNextAction() from onReceive() method?

Upvotes: 0

Views: 197

Answers (2)

andika_kurniawan
andika_kurniawan

Reputation: 567

You can use PeriodicWorkRequest in Work Manager to run sequence jobs. Work Manager run background job gracefully and handle many cases like not running if no internet connection, low battery, etc (It use Constraint)

Upvotes: 0

Richard Dapice
Richard Dapice

Reputation: 885

WorkManager is highly configurable and will allow you to create a PeriodicWorkRequest or a OneTimeWorkRequest these are guaranteed to succeed. PeriodicWorkRequest will fire when you schedule the work, as well as when you have specified in the timer. It will execute in the background even if the app is closed or backgrounded. If you didn't want your task to execute immediately you can use a PWR(PeriodicWorkRequest) with a FlexInterval. See the docs below for more info.

WorkManager Docs

WorkManager Architecture

WorkmManager CodeLab

For example, I created two PeriodicWorkRequests that refresh services and keeps the user logged in always by renewing their token. When the user authenticates the PeriodicWorkRequest is created. In my case, I didn't need it to fire right away as they have just received and cached this information so I utilized the FlexInterval. When the app is backgrounded or closed, the workers continue to refresh services every 12 hours and refresh the token every 6. It works like a charm.

Here is an example:

Build Work:

 override fun beginWork() {

        val periodicWorkRequest = PeriodicWorkRequest.Builder(
                MyWorker::class.java,
                REPEAT_INTERVAL, TimeUnit.MINUTES, // How often work should repeat
                // Flex not required.
                FLEX_INTERVAL, TimeUnit.MINUTES) // Limits execution into a time window
                .setConstraints(
                     Constraints.Builder().setRequiredNetworkType(
                                       NetworkType.CONNECTED).build())
                .addTag(MY_WORKER_TAG)
                .build()

        WorkManager.getInstance().enqueueUniquePeriodicWork(
                MY_UNIQUE_WORK,
                ExistingPeriodicWorkPolicy.KEEP,
                periodicLoginRequest)

Worker:

class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) {

    override fun doWork(): Result {
            // DO WORK HERE

            Result.success()
        } else {
            // HANDLE FAILURE HERE
            Result.failure()
        }

The above is a simple implementation, but it should give you the general idea.

Upvotes: 2

Related Questions