Android 省电的秘密之 JobScheduler

1,951 阅读3分钟
原文链接: www.jianshu.com

JobScheduler是Android L版本新引入的API,JobScheduler,顾名思义,是用来调度工作。工作被调度的条件包括网络变化,充电插拔,周期执行等。使用场景包括wifi条件下数据下载上传等等。谷歌为什么要引入这个新的API呢?是为了省电而制定的一种规范。想想如果每个开发者都利用这个API进行wifi网络下数据上传,数据上传的操作将会被统一到同一个时间点,批量处理,这样比许多应用单独唤醒要省电的多。

下面展示一个小例子
主MainActivity
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); 代表免费的网络,通常就是指wifi了

public class MainActivity extends Activity {
    JobScheduler js;
    JobInfo.Builder builder;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        js=(JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        builder=new Builder(1, new ComponentName(this, DemoService.class));
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
        js.schedule(builder.build());
    }
}

需要被调度的Job
当wifi可用时,这个DemoService 就会执行onStartJob

public class DemoService extends JobService{

    @Override
    public boolean onStartJob(JobParameters params) {
        // TODO Auto-generated method stub
        final JobParameters mJobParameters=params;
       AsyncTask<Void, Void, Void> mTask = new AsyncTask<Void, Void, Void>() {

        @Override
        protected Void doInBackground(Void... params) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            // TODO Auto-generated method stub
            Toast.makeText(wenfengService.this, "hello", 1000).show();
            jobFinished(mJobParameters, true);
            super.onPostExecute(result);
        }

        };
        mTask.execute();
        return true;

    }

    @Override
    public boolean onStopJob(JobParameters params) {
        // TODO Auto-generated method stub
        Toast.makeText(this, "bye", 1000).show();
        return true;
    }

}

清单

<service
  android:name=".DemoService "
  android:permission="android.permission.BIND_JOB_SERVICE"
  android:exported="true"/>

onStartJob.png


onStartJob函数常常有两种场景
1.不耗时的操作,这时你应该返回false
2.耗时的操作例如数据下载等,这是你应该开启一个新线程(因为JobService是跑在主线程的),并且返回true

如果调度是任务执行失败了,怎么办?

任务失败的情况有很多,例如下载失败了,例如下载过程wifi断掉了。
没问题,google提供了方便的重新调度的方法。
例如如果下载过程中,wifi断掉了,JobService会回调onStopJob函数,这是只需要把函数的返回值设置为true就可以了。当wifi重新连接后,JobService会重新回调onStartJob函数。
而如果下载失败了,例如上面的例子中的AsyncTask执行失败,怎么办呢?我们只需要在AsyncTask的onPostExecute中执行jobFinished(mJobParameters, true),这里的true代表任务要在wifi条件重新满足情况下重新调度。经典的写法如下。
开始调度

@Override
public boolean onStartJob(final JobParameters params) {
  mDownloadArtworkTask = new DownloadArtworkTask(this) {
    @Override
    protected void onPostExecute(Boolean success) {
      jobFinished(params, !success);
    }
  };
  mDownloadArtworkTask.execute();
  return true;
}

停止调度

@Override
public boolean onStopJob(final JobParameters params) {
  if (mDownloadArtworkTask != null) {
    mDownloadArtworkTask.cancel(true);
  }
  return true;
}

如果调度的任务老是执行失败,怎么办?

为了省电的考虑,失败的任务在执行条件满足的情况下,要延时一段时间才能执行。而且随着失败次数的增多,延时会越长。举个例子,因为wifi断开而执行失败的任务,在wifi连上后不会马上执行,延时一段时间才能执行。
延时时间=30s*失败次数。

下图是JobScheduler在framework层的时序图,下一章将用adb指令直观查看JobScheduler的情况,不容错过。有问题可以留言哟,大家一起探讨!


JobScheduler内部时序图