F. Penteado
F. Penteado

Reputation: 67

Task running once a day on Xamarin Forms

I started using xamarin a few months ago, and, until now, I didn't have the need of doing something like this. I'm developing an app that, once a day, should run a WCF web service and verify if an information is true. If it is true, it should show a notification on the device. My problem is that I don't know how to perform it, i've read about backgrounding and schedule tasks, but I didn't understand well how can I perform this. How can I do it using Xamarin.Forms?

Thank you!

Upvotes: 3

Views: 2511

Answers (2)

Victor Hugo Terceros
Victor Hugo Terceros

Reputation: 3169

In this case for Android you can use you can use JobScheduler, see this class

[Service(Name = "com.xamarin.samples.downloadscheduler.DownloadJob", 
         Permission = "android.permission.BIND_JOB_SERVICE")]
public class DownloadJob : JobService
{
    public override bool OnStartJob(JobParameters jobParams)
    {            
        Task.Run(() =>
        {
            //Your periodic task here
        });

        return true;  
    }

    public override bool OnStopJob(JobParameters jobParams)
    {
        //true so we re-schedule the task
        return true; 
    }
}

Then you can create a Factory to call this service.

public static class ReadLocationSchedulerFactory
    {
        public static JobInfo.Builder CreateJobBuilderUsingJobId<T>(this Context context, int jobId) where T : JobService
        {
            var javaClass = Java.Lang.Class.FromType(typeof(T));
            var componentName = new ComponentName(context, javaClass);
            return new JobInfo.Builder(jobId, componentName);
        }

    }

Then in your Main Activity you have to call the Factory.

protected override void OnCreate(Bundle bundle)
        {                                
            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App172S.App());

            #region Scheduler

            var jobBuilder = this.CreateJobBuilderUsingJobId<ReadLocationScheduler>(152);
            //This means each 20 mins                
            jobBuilder.SetPeriodic(20 * 60 * 1000); 
            //Persists over phone restarts
            jobBuilder.SetPersisted(true); 
            //If Fails re-try each 2 mins
            jobBuilder.SetBackoffCriteria(120 * 1000, BackoffPolicy.Linear); 
            var jobInfo = jobBuilder.Build();

            var jobScheduler = (JobScheduler)GetSystemService(JobSchedulerService);
            jobScheduler.Cancel(152);
            var scheduleResult = jobScheduler.Schedule(jobInfo);

            if (JobScheduler.ResultSuccess == scheduleResult)
            {
                //If OK maybe show a msg
            }
            else
            {
               //If Failed do something
            }

            #endregion

        }

Upvotes: 2

Koen
Koen

Reputation: 106

For Android, a solution needs 4 components:

  1. AlarmManager to set daily check schedule
  2. BroadCastReceiver to receive the daily trigger and call the IntentService
  3. IntentService to execute awaitable calls
  4. OnBootReceiver to ensure alarms are set again after a device reboot

For iOS, you will most likely need remote push notifications.


Some sample code below for the Android components:

AlarmManager - Setting the Alarm

[assembly: Dependency(typeof(AlarmHelper))] // above the namespace
...
class AlarmHelper: IAlarm
{
    var now = Calendar.Instance;
    var alarmTime = Calendar.Instance;
    alarmTime.Set(CalendarField.HourOfDay, settings.AlarmHour); // Set Alarm start Hour
    alarmTime.Set(CalendarField.Minute, settings.AlarmMinutes); // Set Alarm Start Minutes

    if (alarmTime.Before(now))
    {
        alarmTime.Add(CalendarField.Hour, 24);
    }

    var intent = new Intent(Android.App.Application.Context, typeof(ScheduledAlarmHandler));
    var pendingIntent = PendingIntent.GetBroadcast(Android.App.Application.Context, 0, intent, PendingIntentFlags.CancelCurrent);
    var alarmManager = Android.App.Application.Context.GetSystemService(Context.AlarmService) as AlarmManager;

    alarmManager.SetRepeating(AlarmType.RtcWakeup, alarmTime.TimeInMillis, AlarmManager.IntervalDay, pendingIntent); 
}

BroadCastReceiver - Receiving the Alarm

[BroadcastReceiver]
class ScheduledAlarmHandler : WakefulBroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
        Console.WriteLine("ScheduledAlarmHandler", "Starting service @" + SystemClock.ElapsedRealtime());
        Intent service = new Intent(context, typeof(WakefulAPIService)); 
        StartWakefulService(context, service);
    }
}

IntentService - Executing awaitable calls

[Service]
[IntentFilter(new String[] { "com.test.testApp.WakefulAPIService" })]
class WakefulAPIService : IntentService
{
    protected override void OnHandleIntent(Intent intent)
    {
        // Your API Call code here

        Console.WriteLine("WakefulAPIService", "Completed service @ " + SystemClock.ElapsedRealtime());
        Android.Support.V4.Content.WakefulBroadcastReceiver.CompleteWakefulIntent(intent);
    }
}

OnBootReceiver - Ensuring alarms are set again after a device reboot

[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { "android.intent.action.BOOT_COMPLETED", "android.intent.action.QUICKBOOT_POWERON" })]
class OnBootReceiver : BroadcastReceiver
{

    public override void OnReceive(Context context, Intent intent)
    {
        Console.WriteLine("On Boot Reveiver", "Alarm Set Again after Reboot");
        var alarmHelper = new AlarmHelper();
        alarmHelper.SetAlarm();
    }

}

The required permissions for this to work are 'RECEIVE_BOOT_COMPLETED' and 'WAKE_LOCK'

Upvotes: 5

Related Questions