{"id":37093,"date":"2018-06-04T12:00:05","date_gmt":"2018-06-04T19:00:05","guid":{"rendered":"https:\/\/blog.xamarin.com\/?p=37093"},"modified":"2019-04-04T07:45:58","modified_gmt":"2019-04-04T14:45:58","slug":"xamarin-android-firebase-job-dispatcher-background-scheduling-android-devices","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/xamarin\/xamarin-android-firebase-job-dispatcher-background-scheduling-android-devices\/","title":{"rendered":"Xamarin.Android Firebase Job Dispatcher: Background Scheduling for all Android Devices"},"content":{"rendered":"<p>\t\t\t\tRunning tasks in the background is important for Android apps. It&#8217;s what helps keep an app responsive to the user and allows work to get done even when the user isn\u2019t actively engaged with the app. As an example, consider uploading a large file or trying to apply a filter to a picture. Both of these tasks should be done in the background. To do so otherwise will result in the work blocking the UI thread, making the app unresponsive to the user. If the app is unresponsive for a long enough time, then Android will kill the process with the dreaded \u201cApplication Not Responding\u201d message.<\/p>\n<p>However, running tasks in the background is a compromise between ensuring that the work is performed without negatively impacting the usability of the device. For example, background work should not unnecessarily drain the battery by constantly keeping the device awake. Additionally, Android 6.0 and higher impose limitations on what and how that work can be performed in the background. These factors can make background processing in Android a complex scenario.<\/p>\n<p>As an example of this, consider uploading a file to a website. A large file might inflict significant data charges on the user if they are not connected to their home network. Alternatively, a slow network may cause significant battery usage because the network activity is keeping the device in a higher power state for a lengthy period of time. In this scenario, the app should try to schedule the upload when the device is charging and connected to an unmetered (free) network.<\/p>\n<h2>Introducing the FirebaseJobDispatcher<\/h2>\n<p>The Firebase Job Dispatcher is a library (backward compatible to API level 9) to simplify background processing by helping developers run and write apps that run intelligently and efficiently. The Firebase Job Dispatcher contains APIs that handle these details for the developer:<\/p>\n<ul>\n<li>Encapsulating the work in discrete classes for cleaner code.<\/li>\n<li>Intelligently scheduling the work to run when certain conditions are met.<\/li>\n<li>Rescheduling the work to run if the task fails, or allowing a task to run more than once.<\/li>\n<\/ul>\n<p>This library runs as a system service, exposed through an object that is an instance of the <code class=\"EnlighterJSRAW\">FirebaseJobDispatcher<\/code> class. This object will help the app create a job that contains some or all of the following meta-data:<\/p>\n<ul>\n<li><code class=\"EnlighterJSRAW\">JobService<\/code> is an abstract class that is extended by the app and contains the code for the job.<\/li>\n<li><strong>Criteria<\/strong> are the conditions that determine when a job should be run.<\/li>\n<li><strong>Triggers<\/strong> identify a time constraint for when the job should be run.<\/li>\n<li><strong>Parameters<\/strong> are any inputs that are required to run.<\/li>\n<\/ul>\n<p>This diagram shows how all of these concepts relate to each other:<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-37096 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/01-firebasejobdispatcherblog.png\" alt=\"Components of the Firebase Job Dispatcher\" width=\"541\" height=\"530\" \/><\/p>\n<p>The Firebase.JobDispatcher binding provides the <code>CreateJobDispatcher<\/code> extension method that will take any <code>Android.App.Context<\/code> (such as an Activity) and return a reference to the <code>FirebaseJobDispatcher<\/code>. This code snippet shows an Activity getting a reference the FirebaseJobDispatcher:<\/p>\n<pre class=\"EnlighterJSRAW\">Firebase.JobDispatcher.FirebaseJobDispatcher dispatcher = this.CreateJobDispatcher();<\/pre>\n<h2>Encapsulating Code in a JobService<\/h2>\n<p>The core of a job is the <code>Firebase.JobDispatcher.JobService<\/code> abstract class. This class is a specialized Android service that encapsulates the work to be performed, and is adorned with an IntentFilter declaring the <a href=\"https:\/\/github.com\/xamarin\/XamarinComponents\/blob\/master\/Android\/FirebaseJobDispatcher\/source\/Firebase.JobDispatcher\/Additions\/FirebaseJobServiceIntent.cs#L10\"><code>FirebaseJobServiceIntent.Action<\/code><\/a>. There are two methods that a <code>JobService<\/code> must override:<\/p>\n<ul>\n<li><code>OnStartJob<\/code> \u2013 Must be overridden and is invoked by the FirebaseJobDispatcher when it is time to run the job.<\/li>\n<\/ul>\n<p>This method will run on the UI thread of the application. If the work is a small, easy task (less than 16 milliseconds), it may run on the main thread. However, lengthy tasks such as disk access or network calls must run asynchronously. <code>OnStartJob<\/code> should return &#8220;true&#8221; if it\u2019s running work on another thread, while &#8220;false&#8221; should be returned if the all the work was performed within <code>OnStartJob<\/code> itself.<\/p>\n<ul>\n<li><code>OnStopJob<\/code> \u2013 Must be overridden and is called when the system has to prematurely terminate the job and provides the JobService a chance to perform any necessary cleanup. If the job should be rescheduled, it should return &#8220;true&#8221;.<\/li>\n<\/ul>\n<p>The following code shows a sample Firebase Job Dispatcher JobService:<\/p>\n<pre class=\"EnlighterJSRAW\">[Service(Name = \"FirebaseJobDispatcherSample.FibonacciJob\")]\n[IntentFilter(new[] { FirebaseJobServiceIntent.Action })]\npublic class FibonacciJob : Firebase.JobDispatcher.JobService\n{\n    public override bool OnStartJob(IJobParameters jobParameters)\n    {\n        \/\/ Called by the FirebaseJobDispatcher to start the job.\n        \/\/ Return TRUE if the job is doing work on another thread.\n        return true;\n    }\n\n    public override bool OnStopJob(IJobParameters jobParameters)\n    {\n        \/\/ Called by Android when it has to terminate the job.\n        \/\/ Return true if the job should be rescheduled, false if it shouldn\u2019t.\n        return false;\n    }\n}<\/pre>\n<p>All of the information that the FirebaseJobDispatcher requires to run the job, such as what class to use, any parameters that were passed to the job, and the criteria that were used to schedule the job are provided in the <code class=\"EnlighterJSRAW\">IJobParameters<\/code> parameter.<\/p>\n<h2>Instantiating and Running a JobService<\/h2>\n<p>The Firebase.JobDispatcher.Job.Builder is used to create a <code class=\"EnlighterJSRAW\">Firebase.JobDispatcher.Job<\/code> object. A job, at the very minimum, must identify which JobService will perform the work and have a tag, a string that helps identify the instance of the job. After the Job is created, it can be scheduled with the FirebaseJobDispatcher. This code shows one way to instantiate and schedule a <code class=\"EnlighterJSRAW\">Firebase.JobDispatcher.Job<\/code>:<\/p>\n<pre class=\"EnlighterJSRAW\">Firebase.JobDispatcher.Job job = dispatcher.NewJobBuilder()\n                                           .SetService(\"fibnonacci-calculation\")\n                                           .Build();\nint result = dispatcher.Schedule(job);<\/pre>\n<p>Parameters can be passed to a job through an <code class=\"EnlighterJSRAW\">Android.App.Bundle<\/code> object. Building on the previous example, this code will pass the integer value 25 to the job:<\/p>\n<pre class=\"EnlighterJSRAW\">int value = Int32.Parse(inputEditText.Text);\nBundle jobParameters = new Bundle();\njobParameters.PutInt(\u201cfibonacci_value_key\u201d, value);\n\nvar job = dispatcher.NewJobBuilder()\n                    .SetService(\"fibnonacci-calculation\")\n                    .SetExtras(jobParameters)\n                    .Build();\n\nint result = dispatcher.Schedule(job);<\/pre>\n<p>The JobService can access this parameter by reading the value from the <code class=\"EnlighterJSRAW\">IJobParameters<\/code> object that is passed in via <code class=\"EnlighterJSRAW\">OnStartJob<\/code>:<\/p>\n<pre class=\"EnlighterJSRAW\">public override bool OnStartJob(IJobParameters jobParameters)\n{\n    Int fibonacciValue = jobParameters.Extras.GetInt(FirebaseJobDispatcherHelpers.FibonacciValueKey, -1);\n\n    \/\/ Calculate the Fibonacci value for the value provided (on a background thread)\n\n    return true;\n}<\/pre>\n<p>For the curious, this next step shows how to set a trigger to run the job in the next 1-5 seconds once an unmetered network is connected to the device:<\/p>\n<pre class=\"EnlighterJSRAW \">int value = Int32.Parse(inputEditText.Text);\n\nBundle jobParameters = new Bundle();\njobParameters.PutInt(FirebaseJobDispatcherHelpers.FibonacciValueKey, value);\n\n\/\/ This job should run between 1 - 5 seconds after being scheduled.\nJobTrigger.ExecutionWindowTrigger trigger = Trigger.ExecutionWindow(1, 5);\n\nvar job = dispatcher.NewJobBuilder()\n                    .SetService(\"fibnonacci-calculation\")\n                    .SetExtras(jobParameters)\n                    .SetTrigger(trigger)\n                    .SetConstraints(Constraint.OnUnmeteredNetwork)\n                    .Build();\n\nint result = dispatcher.Schedule(job);\n<\/pre>\n<h2>Finishing the Work<\/h2>\n<p>Finally, when a JobService has finished its work, it must call its <code class=\"EnlighterJSRAW\">.JobFinished()<\/code> method. This method will signal to the FirebaseJobDispatcher that work is done and can now be cleaned up. This diagram shows how all these methods relate together:<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-37097 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/02-firebasejobdispatcherblog.png\" alt=\"JobService methods \" width=\"741\" height=\"694\" \/><\/p>\n<p>To perform background work and support older versions of Android, the Firebase Job Dispatcher is the library for you and it will help proof your future apps for Android 8.0 and higher. <a href=\"https:\/\/github.com\/topgenorth\/xamarin.android-firebasejobdispatcher-sample\">Try out a sample<\/a>\u00a0in your apps today!<\/p>\n<p><a href=\"https:\/\/forums.xamarin.com\/127598\/\">Disucss this post on the Xamarin forums!<\/a>\t\t<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Running tasks in the background is important for Android apps. It&#8217;s what helps keep an app responsive to the user and allows work to get done even when the user isn\u2019t actively engaged with the app. As an example, consider uploading a large file or trying to apply a filter to a picture. Both of [&hellip;]<\/p>\n","protected":false},"author":563,"featured_media":39167,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[5],"class_list":["post-37093","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developers","tag-android"],"acf":[],"blog_post_summary":"<p>Running tasks in the background is important for Android apps. It&#8217;s what helps keep an app responsive to the user and allows work to get done even when the user isn\u2019t actively engaged with the app. As an example, consider uploading a large file or trying to apply a filter to a picture. Both of [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/37093","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/users\/563"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/comments?post=37093"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/37093\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media\/39167"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media?parent=37093"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/categories?post=37093"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/tags?post=37093"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}