{"id":34401,"date":"2017-11-27T13:04:18","date_gmt":"2017-11-27T21:04:18","guid":{"rendered":"https:\/\/blog.xamarin.com\/?p=34401"},"modified":"2019-04-04T08:59:35","modified_gmt":"2019-04-04T15:59:35","slug":"replacing-services-jobs-android-oreo-8-0","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/xamarin\/replacing-services-jobs-android-oreo-8-0\/","title":{"rendered":"Replacing Services With JobScheduler in Android Oreo 8.0"},"content":{"rendered":"<p>\t\t\t\tBefore Android 8.0, Android apps would start a service that ran almost indefinitely, even when the app was in the background. This service can be handy for the app, and easy for the developer to implement, but may also have an impact on device experience.<\/p>\n<p>Imagine a\u00a0scenario where many applications want to perform work\u00a0when a\u00a0device is plugged in to charge. When this happens, Android dispatches the <code>android.intent.action.ACTION_POWER_CONNECTED<\/code> intent. All apps registered to respond to that intent will start up, demanding RAM, CPU time, and bandwidth. Consequentially, it is possible these simultaneous demands may exceed the available space on the device and cause it to slow down. At this point, all a user knows is that they plugged in their device and it started exhibiting sluggish, unresponsive behavior and may assume something is wrong with their phone or memory.<img decoding=\"async\" class=\"wp-image-34410 alignright\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/jobscheduler-1.png\" alt=\"\" width=\"185\" \/><\/p>\n<h2>Android and the JobScheduler<\/h2>\n<p>Android 8.0 has introduced <a href=\"https:\/\/developer.android.com\/about\/versions\/oreo\/background.html\">new background execution limits<\/a> that dramatically change how services work.\u00a0When applications move into the background, any services they start will be granted several minutes to complete their work before the operating system terminates them. The <a href=\"https:\/\/developer.android.com\/topic\/performance\/scheduling.html#js\">Android Framework JobScheduler<\/a> is one possible way to address this change for Xamarin.Android apps targeting Android 5.0 (API level 21) or higher.<\/p>\n<p>The Android Framework\u00a0<a href=\"https:\/\/developer.android.com\/reference\/android\/app\/job\/JobScheduler.html\"><em>JobScheduler<\/em><\/a>\u00a0is an API designed to run jobs&mdash;discrete, distinct units of work&mdash;in the background of various apps. The JobScheduler schedules which jobs will run at appropriate times according to conditions that can be set by the application. Going back to our original example above,\u00a0the JobScheduler\u00a0may schedule the jobs so that they run one after another, instead of all at once.<\/p>\n<h3>Scheduling<\/h3>\n<p>The ability to schedule and queue\u00a0jobs make the JobScheduler\u00a0perfect for tasks that are traditionally handled by long-running services, such as:<\/p>\n<ul>\n<li>Downloading a file, but only when the device is connected to an unmetered (free) network.<\/li>\n<li>When charging a device, make a series of thumbnail images from a collection of larger images.<\/li>\n<li>If connected to the network, call a method on a web service using an exponential backoff algorithm between unsuccessful web service calls.<\/li>\n<\/ul>\n<h3>Classes<\/h3>\n<p>There are three important classes\u00a0in\u00a0the JobScheduler API:<\/p>\n<ul>\n<li><strong>JobScheduler<\/strong>: A system service that will run jobs scheduled by apps.<\/li>\n<li><strong>JobService<\/strong>: A class extended by applications and contains the code that runs as part of a job. The code for each job is contained in a class that extends the JobService class and requests the\u00a0<code>android.permission.BIND_JOB_SERVICE<\/code> permission.<\/li>\n<li><strong>JobInfo<\/strong>: Holds information about the job that the JobScheduler needs in order to run a JobService. The JobInfo will tell the JobScheduler which JobService type to use and which conditions must be met before the job can run. It also contains any parameters that the app must pass to the JobService. A JobInfo object is not directly instantiated, instead it is created by using a <strong>JobInfo.Builder<\/strong>.<\/li>\n<\/ul>\n<h3>Methods<\/h3>\n<p>A JobService sub-class must override two methods:<\/p>\n<ul>\n<li><strong>OnStartJob<\/strong>: A system invokes when the job starts and runs on the main thread of the app. \nIf the work is a small, easy task (less than 16 milliseconds), it will run on the main thread. Lengthy tasks such as disk access or network calls must run asynchronously.\u00a0<code>OnStartJob<\/code> should return <code>\"true\"<\/code> if it is running work on another thread, while <code>\"false\"<\/code> should be returned if the all the work was performed within\u00a0<code>OnStartJob<\/code> itself.<\/li>\n<li>\u00a0<strong>OnStopJob<\/strong>: Called when the system has to prematurely terminate the job and provide the JobService a chance to perform any necessary cleanup. If the job should be rescheduled, it will return <code>\"true\"<\/code>.<\/li>\n<\/ul>\n<h2>Applying JobScheduler<\/h2>\n<p>The following code shows a sample JobService type:<\/p>\n<pre class=\"\">[Service(Name = \"JobScheduleSample.FibonacciJob\", Permission = \"android.permission.BIND_JOB_SERVICE\")]\npublic class FibonacciJob : JobService\n{\n   public override bool OnStartJob(JobParameters jobParams)\n   {\n      \/\/ Called by the operating system when starting the service.\n      \/\/ Start up a thread, do work on the thread.\n      return true;\n   }\n\n   public override bool OnStopJob(JobParameters jobParams)\n   {\n      \/\/ Called by Android when it has to terminate a running service.\n      return false; \/\/ Don't reschedule the job.\n   }\n}\n<\/pre>\n<h3>Create a JobInfo Object<\/h3>\n<p>In order to schedule a job, it&#8217;s necessary for an app to use a JobInfo.JobBuilder to create a JobInfo object. The JobInfo.JobBuilder has a fluent interface and is used to collect meta-data, such as the type of JobService to instantiate and any conditions that should be met before the job is run. This snippet shows how to create a JobInfo class to run the FibonacciJob (from the example above), but only when the device is connected to an &#8220;unmetered&#8221; (free) network. The job should run between one and five seconds from being scheduled:<\/p>\n<pre class=\"\">Java.Lang.Class javaClass = Java.Lang.Class.FromType(typeof(FibonacciJob);\nComponentName component = new ComponentName(context, javaClass);\n\nJobInfo.Builder builder = new JobInfo.Builder(context, component)\n                                     .SetMinimumLatency(1000)   \/\/ Wait at least 1 second\n                                     .SetOverrideDeadline(5000) \/\/ But no longer than 5 seconds\n                                     .SetRequiredNetworkType(NetworkType.Unmetered);\nJobInfo jobInfo = builder.Build();\n<\/pre>\n<p>In the previous example, the <code>context<\/code> is any Android Context, such as an Activity. The parameter is a unique integer that identifies the job service to the JobScheduler.<\/p>\n<p>The following C# extension method should help with creating a ComponentName for a given JobService subclass:<\/p>\n<pre class=\"&quot;\">public static ComponentName GetComponentNameForJob(this Context context) where T : JobService, new()\n{\n   Class javaClass = Class.FromType(typeof(T));\n   return new ComponentName(context, javaClass);\n}\n<\/pre>\n<h3>Schedule a Job<\/h3>\n<p>Once a\u00a0JobInfo created is created, the app can schedule a job. This code snippet will get a reference to the JobScheduler service and ask it to schedule the job identified in the <code>jobInfo<\/code>\u00a0object:<\/p>\n<pre>JobScheduler jobScheduler = (JobScheduler)GetSystemService(JobSchedulerService);\nint result = jobScheduler.Schedule(jobInfo);\nif (result == JobScheduler.ResultSuccess)\n{\n   \/\/ The job was scheduled.\n}\nelse\n{\n   \/\/ Couldn't schedule the job.\n}\n<\/pre>\n<h3>JobParameters and .SetExtras<\/h3>\n<p>It&#8217;s also possible for the app to pass parameters\u00a0to a service by packaging them up in a Bundle and then calling the <code>JobInfo.SetExtras<\/code> method. The Bundle is included with the JobParameters object passed to the JobService when\u00a0OnStartJob is invoked. For example, this snippet will pass a value to a job:<\/p>\n<pre class=\"\">\/\/ Bundle up parameters\nvar jobParameters = new PersistableBundle();\nextras.PutLong(\"some_long_value\", 10L);\n\n\/\/ Put the Bundle with the parameters into the jobInfo.\nJobInfo.Builder builder = new JobInfo.Builder(context, component)\n                                     .SetExtras(extras);\nJobInfo jobInfo = builder.Build();\n<\/pre>\n<p>To access this value, the JobService should read the .Extras property on the JobParameter object:<\/p>\n<pre class=\"\">public override bool OnStartJob(JobParameters jobParams)\n{\n   long theValue = jobParams.Extras.GetLong(\"some_long_value\", -1);\n\n   \/\/ Job does something with the value.\n}\n<\/pre>\n<h3>JobFinished<\/h3>\n<p>Finally, when a JobService has finished its\u00a0work (regardless of which thread the work is running on), it should call its own <code>JobFinished()<\/code> method. It&#8217;s important to call this method because it tells the JobScheduler that the work is done and it&#8217;s safe to release any wake locks that were acquired for the JobService in the first place. This diagram illustrates how the JobService methods relate to each other and how they would be used:<\/p>\n<p><img decoding=\"async\" class=\"aligncenter wp-image-34409\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/jobscheduler-2.png\" alt=\"The relationship of JobService method calls.\" width=\"325\" height=\"321\" \/><\/p>\n<h2>Wrapping Up<\/h2>\n<p>Now that you&#8217;ve seen the basics of the JobScheduler API,\u00a0try using it in your Xamarin.Android application to replace a task done in a background service. This is a great opportunity to enhance the experience your user has with your applications <em>and<\/em> update to Android Oreo 8.0 all at once! You can find a <a href=\"https:\/\/github.com\/topgenorth\/xamarin.android-jobschedulersample\">simple sample on GitHub<\/a> that shows the JobScheduler API in action. The sample calculates a Fibonacci number in a job and then passes that number back to an Activity.<\/p>\n<p><a href=\"https:\/\/forums.xamarin.com\/108772\/replacing-services-with-jobs-in-android-oreo-8-0\">Discuss this post in the forums.<\/a>\t\t<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Before Android 8.0, Android apps would start a service that ran almost indefinitely, even when the app was in the background. This service can be handy for the app, and easy for the developer to implement, but may also have an impact on device experience. Imagine a\u00a0scenario where many applications want to perform work\u00a0when a\u00a0device [&hellip;]<\/p>\n","protected":false},"author":563,"featured_media":34410,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[5,4],"class_list":["post-34401","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developers","tag-android","tag-xamarin-platform"],"acf":[],"blog_post_summary":"<p>Before Android 8.0, Android apps would start a service that ran almost indefinitely, even when the app was in the background. This service can be handy for the app, and easy for the developer to implement, but may also have an impact on device experience. Imagine a\u00a0scenario where many applications want to perform work\u00a0when a\u00a0device [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/34401","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=34401"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/34401\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media?parent=34401"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/categories?post=34401"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/tags?post=34401"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}