性能优化(8.4)-在Android Lollipop中使用JobScheduler

主目录见::Android高级进阶知识(这是总目录索引)
[译]Using the JobScheduler API on Android Lollipop
谷歌官网的例子:JobScheduler

 今天还是打算以上面一篇英文文章为基础,同时你也可以参考谷歌官网的JobScheduler例子来了解它的使用方式。JobScheduler作为系统服务的形式出现,任务的调配统一由系统在特定的条件下进行协调,是android 5.0及之后一个相对于AlarmManager+wakeLock而言比较节能的方案,也是谷歌推荐的一个做法。

一.目标

这篇文章作为电量优化的一个方案出现,我们有理由去了解怎么使用它还有它的原理,今天我们目标是:
1.了解JobScheduler的使用方法;
2.能在实际场景中使用JobScheduler。

二.JobScheduler

这篇教程里面,你将学会如何在 Android Lollipop中使用JobScheduler APIJobScheduler的API允许开发者在条件满足的情况下创建一些后台任务。

介绍

在Android开发中,在某些场景中,你可能希望在之后的某个时间节点或者在某些条件下执行你的任务,比如你的设备正在充电的情况下或者连到wifi网络的情况下。幸亏有了Android 21也就是Android Lollipop,谷歌提供了一个新的组件JobScheduler来满足这些场景。

当一系列的前置条件被满足之后,JobScheduler API就会执行你应用的某个操作。跟AlarmManager不同的是,它的执行时间是不确定的。另外,JobScheduler API会将任务打包一起执行。这允许你的应用执行某项任务的时候不需要考虑时间控制引起的电量消耗。

这篇文章中,你将通过执行一个简单的后台任务来学习更多关于JobScheduler APIJobService类的知识。这篇文章里面涉及的代码见GITHUB.

1.创建Job Service

首先,你需要创建一个基于最低版本为Android 21的Android工程,因为JobScheduler API是在最新Android版本才被添加进来的,而且在写这篇文章的时候,还没有向后兼容的support 库。

这里假设你用的是Android Studio,当你点击完成创建新的工程,你将得到一个"Hello World"应用的骨架。第一步要做的就是你要在这个工程基础上创建一个新的Java 类。为了尽可能简单,这里就把这个类命名为JobSchedulerService 继承于JobService 类,而且需要创建onStartJob(JobParameters params)onStopJob(JobParameters params).

public class JobSchedulerService extends JobService {
    @Override
    public boolean onStartJob(JobParameters params) {
        return false;
    }
 
    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}

onStartJob(JobParameters params)是用在任务开始执行的时候,因为这是系统用来触发已经计划执行的任务。你可以看到,这个方法返回一个boolean类型的值。如果你返回的是false,那么系统就会认为要执行的任务耗费的时长不长,且在方法返回的时候已经完成了。如果返回的是true,那么系统会认为要执行的任务需要的时长比较长,执行任务的重担也会落在你的身上,开发者就必须通过jobFinished(JobParameters params, boolean needsRescheduled)方法来告诉系统你的任务是什么时候结束的。

当系统接收到cancel 的请求,那么onStopJob(JobParameters params)方法就会被用来取消正在等待执行的任务。需要注意的是当onStartJob(JobParameters params)方法返回为false的时候,当接收到cancel 请求的时候,系统会任务当前没有任务正在执行,换句话说就是,onStopJob(JobParameters params)不会被调用。

有一点需要特别注意的是你的 job service 是运行在主线程的,这就意味着你必须使用另外的线程,或者Handler,或者一个异步任务来执行耗时长的任务以避免阻塞主线程。因为多线程已经超出本教程的范围了,所以我们在JobSchedulerService类中就简单地用一个Handler来执行我们的任务。

private Handler mJobHandler = new Handler( new Handler.Callback() {
    @Override
    public boolean handleMessage( Message msg ) {
        Toast.makeText( getApplicationContext(), 
            "JobService task running", Toast.LENGTH_SHORT )
            .show();
        jobFinished( (JobParameters) msg.obj, false );
        return true;
    }
} );

在这个Handler中,你实现了它的handleMessage(Message msg)方法并且在方法里执行你的代码逻辑。在这个例子中,我们尽量保持简单,只是弹出了一个Toast消息,这个地方也就是你放自己代码逻辑的地方,比如同步数据。

当任务完成,你必须调用jobFinished(JobParameters params, boolean needsRescheduled)来通知系统你已经完成了任务的执行并且可以执行下一个操作了。如果你不这么做的话,那么你的任务就会执行一次并且应用不会被允许执行额外的任务。

jobFinished(JobParameters params, boolean needsRescheduled)方法中的两个参数,第一个参数JobParameters 是从JobService类中的onStartJob(JobParameters params)方法传递过来的,第二个参数的boolean值是让系统知道是否在原有的条件下重新执行任务。理解这个boolean值是很有用的,因为他决定了在某些情况下任务不能被执行完成应该怎么处理,比如网络请求失败。

Handler实例被创建, 你就可以继续实现onStartJob(JobParameters params)方法和onStopJob(JobParameters params)方法来控制你的任务。你会注意到,下面代码片段里onStartJob(JobParameters params)方法返回了true,这是因为你将使用Handler实例来控制你的操作,也就意味着代码逻辑的执行会超过onStartJob(JobParameters params)的结束。通过返回true,你就可以让系统知道,你会自己调用jobFinished(JobParameters params, boolean needsRescheduled)方法来结束任务。同时你注意到数字1被传递给Handler实例,这是你用来标识任务的标记。

@Override
public boolean onStartJob(JobParameters params) {
    mJobHandler.sendMessage( Message.obtain( mJobHandler, 1, params ) );
    return true;
}
@Override
public boolean onStopJob(JobParameters params) {
    mJobHandler.removeMessages( 1 );
    return false;
}

一旦完成了JobSchedulerService类的java部分,你就必须在AndroidManifest.xml中添加一个service节点来使你的应用有权限来绑定(bind)和使用(use)JobService这个类。

<service android:name=".JobSchedulerService"
    android:permission="android.permission.BIND_JOB_SERVICE" />

2.创建Job Scheduler

当编写完JobSchedulerService类之后,我们就可以来看你的应用是怎么和JobScheduler API进行交互的,第一件事就是创建一个叫做mJobSchedulerJobScheduler对象,并且通过获取系统服务JOB_SCHEDULER_SERVICE来初始化它。 在例子里面,这步操作写在MainActivity中。

mJobScheduler = (JobScheduler) 
    getSystemService( Context.JOB_SCHEDULER_SERVICE );

当你想要创建你的计划任务的时候,你必须通过JobInfo.Builder来构建你的JobInfo对象,然后传给你的JobScheduler服务。为了创建JobInfo对象,JobInfo.Builder接收两个参数,第一个参数是为了标识你的任务的,第二个参数是service的组件名字(component name)。

JobInfo.Builder builder = new JobInfo.Builder( 1,
        new ComponentName( getPackageName(), 
            JobSchedulerService.class.getName() ) );

这个builder允许你在任务执行之前设置不同的控制选项,下面的代码片段展示了你的程序将会被每三秒执行一次。

builder.setPeriodic( 3000 );

其他的方法包括:

  • setMinimumLatency(long minLatencyMillis):这个方法是让你设置任务的延迟执行时间,这个方法不能与setPeriodic(long time)方法合用,不然会抛出异常。
  • setOverrideDeadline(long maxExecutionDelayMillis):这个方法设置任务的最后执行时间。甚至其他条件为满足,你的任务也会在这个规定时间启动。跟setMinimumLatency(long time)方法一样,这个方法也不能与setPeriodic(long time)方法合用,不然会抛出异常。
  • setPersisted(boolean isPersisted):这个函数告诉系统是否你的任务在设备重启的时候要继续存在。
  • setRequiredNetworkType(int networkType):这个函数告诉系统你的任务是否在特定的网络类型下才执行。默认情况下是JobInfo.NETWORK_TYPE_NONE,意思就是说,不管你的网络连接与否任务都会被执行。另外两个类型,一种是JobInfo.NETWORK_TYPE_ANY,只有在有网络连接的情况下才会执行你的任务,还有一种是JobInfo.NETWORK_TYPE_UNMETERED,它允许你的任务在不是蜂窝网络例如wifi连接的时候才执行.
  • setRequiresCharging(boolean requiresCharging):这个函数是告诉系统只有在充电的情况下才会执行你的任务。
  • setRequiresDeviceIdle(boolean requiresDeviceIdle):这个函数让你的任务在用户未使用设备且有一段时间未使用的情况下才执行即设备处于空闲状态。

需要注意的是setRequiredNetworkType(int networkType), setRequiresCharging(boolean requireCharging)setRequiresDeviceIdle(boolean requireIdle)可能让你的任务不会被执行,除非你也设置了setOverrideDeadline(long time),这样你的条件不满足任务还是会被执行,只要满足最后的执行时间。一旦你的前置条件被设置,你就可以创建JobInfo对象然后将它传给JobScheduler对象:

if( mJobScheduler.schedule( builder.build() ) <= 0 ) {
    //If something goes wrong
}

你会注意到schedule方法返回一个integer,如果方法执行失败,方法就会返回0或者小于0的数, 如果执行成功的话,那么就会返回JobInfo.Builder中设置的任务的唯一标识。

如果你要停止特定的一个任务或者所有的任务,你可以调用JobScheduler对象中的cancel(int jobId)方法和cancelAll()方法:

mJobScheduler.cancelAll();

到这里,你应该会使用JobSchedulerAPI来批量执行你的任务和一些后台任务了。

总结

这篇文章中,你已经学会了如何实现JobService的子类和用Handler对象来执行一个后台任务了。而且你也学会了用JobInfo.Builder来设置一些执行条件来决定任务的执行与否。掌握了这些之后,你就可以减少你应用程序执行所消耗的电量。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容