wake_lock虽然好用,但是也会导致大量高频次的CPU唤醒及操作,最好把这些操作集中处理。因此,系统提供给我们更好的API去使用:JobScheduler
JobSchedule的用处:
单独访问1000次,和将1000次放到一起访问;前者更加耗电,因为会重复的激活网络开启状态
JobSchedule可以根据传入的一些参数做些决策(最小延时、最多延时、网络类型)
当满足条件时,再去执行任务
兼容:
5.0以后用JobSchedule
5.0 以前用GCM–谷歌的GooglePlay servicemanager(国内屏蔽)
拓展:Doze 深度休眠
setBackoffCriteria(long initialBackoffMillis,int backoffPolicy)
//重置/退避策略,当一个任务调度失败的时候执行什么样的策略。initialBackoffMillis:第一尝试重试的等待时间间隔ms,
backoffPolicy:对应的退避策略。比如等待的间隔呈指数增长
setPeriodic
设备重启后,任务是否保留,需要权限:android.permission.RECEIVE_BOOT_COMPLETED
long intervalMillis 执行周期,每隔一段时间间隔任务最多可以执行一次
long flexMillis 在周期执行的末端有一个flexMillis长度的窗口期,任务就可以在这个窗口期执行
setRequiresCharging 是否需要充电
setRequiresDeviceIdle 是否需要本设备处于空闲状态
addTriggerContentUri 监听uri对应的String发生改变
setTriggerContentMaxDelay
设置content发生变化一直到任务被执行中间的最大延迟时间
setTriggerContentUpdateDelay 设置content发生变化一直到任务被执行中间的延迟,在延迟时间内content改变,延迟时间会重新计算
JobSchedule.cancel(jobId) 取消jobId对应的任务,如果任务已经在执行了,是不能取消的
getAllPendingJobs 获得所有(该进程的)正在等待的任务
JobService中的方法:
jobFinnished 任务完成时执行
在前面使用WeakLock执行下载任务防止中断,当多个下载任务,可能造成屏幕频繁唤醒,比较消耗电量,这里可以用JobScheduler进行批量执行下载任务
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends JobService {
private static final String LOG_TAG = "MyJobService";
@Override
public void onCreate() {
super.onCreate();
Log.i(LOG_TAG, "MyJobService created");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(LOG_TAG, "MyJobService destroyed");
}
@Override
public boolean onStartJob(JobParameters params) {
// This is where you would implement all of the logic for your job. Note that this runs
// on the main thread, so you will want to use a separate thread for asynchronous work
// (as we demonstrate below to establish a network connection).
// If you use a separate thread, return true to indicate that you need a "reschedule" to
// return to the job at some point in the future to finish processing the work. Otherwise,
// return false when finished.
Log.i(LOG_TAG, "Totally and completely working on job " + params.getJobId());
// First, check the network, and then attempt to connect.
if (isNetworkConnected()) {
new SimpleDownloadTask() .execute(params);
return true;
} else {
Log.i(LOG_TAG, "No connection on job " + params.getJobId() + "; sad face");
}
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
// Called if the job must be stopped before jobFinished() has been called. This may
// happen if the requirements are no longer being met, such as the user no longer
// connecting to WiFi, or the device no longer being idle. Use this callback to resolve
// anything that may cause your application to misbehave from the job being halted.
// Return true if the job should be rescheduled based on the retry criteria specified
// when the job was created or return false to drop the job. Regardless of the value
// returned, your job must stop executing.
Log.i(LOG_TAG, "Whelp, something changed, so I'm calling it on job " + params.getJobId());
return false;
}
/**
* Determines if the device is currently online.
*/
private boolean isNetworkConnected() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
/**
* Uses AsyncTask to create a task away from the main UI thread. This task creates a
* HTTPUrlConnection, and then downloads the contents of the webpage as an InputStream.
* The InputStream is then converted to a String, which is logged by the
* onPostExecute() method.
*/
private class SimpleDownloadTask extends AsyncTask<JobParameters, Void, String> {
protected JobParameters mJobParam;
@Override
protected String doInBackground(JobParameters... params) {
// cache system provided job requirements
mJobParam = params[0];
try {
InputStream is = null;
// Only display the first 50 characters of the retrieved web page content.
int len = 50;
URL url = new URL("https://www.google.com");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000); //10sec
conn.setConnectTimeout(15000); //15sec
conn.setRequestMethod("GET");
//Starts the query
conn.connect();
int response = conn.getResponseCode();
Log.d(LOG_TAG, "The response is: " + response);
is = conn.getInputStream();
// Convert the input stream to a string
Reader reader = new InputStreamReader(is, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
} catch (IOException e) {
return "Unable to retrieve web page.";
}
}
@Override
protected void onPostExecute(String result) {
jobFinished(mJobParam, false);
Log.i(LOG_TAG, result);
}
}
}
public void execut(View view) {
wakelock_text.setText("正在下载....");
//优化
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
for (int i = 0; i < 500; i++) {
JobInfo jobInfo = new JobInfo.Builder(i, serviceComponent)
.setMinimumLatency(5000)//5秒 最小延时、
.setOverrideDeadline(60000)//maximum最多执行时间
// .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)//免费的网络---wifi 蓝牙 USB
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)//任意网络---wifi
.build();
jobScheduler.schedule(jobInfo);
}
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class TestJobService extends JobService {
private static final String TAG = "SyncService";
private final LinkedList<JobParameters> jobParamsMap = new LinkedList<JobParameters>();
JobSchedulerSettingActivity mActivity;
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service created");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Service destroyed");
}
/**
* When the app's Activity is created, it starts this service. This is so that the
* activity and this service can communicate back and forth. See "setUiCalback()"
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Messenger callback = intent.getParcelableExtra("messenger");
Message m = Message.obtain();
m.what = JobSchedulerSettingActivity.MSG_SERVICE_OBJ;
m.obj = this;
try {
callback.send(m);
} catch (RemoteException e) {
Log.e(TAG, "Error passing service object back to activity.");
}
return START_NOT_STICKY;
}
@Override
public boolean onStartJob(JobParameters params) {
// We don't do any real 'work' in this sample app. All we'll
// do is track which jobs have landed on our service, and
// update the UI accordingly.
jobParamsMap.add(params);
if (mActivity != null) {
mActivity.onReceivedStartJob(params);
//执行任务
}
Log.i(TAG, "on start job: " + params.getJobId());
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
// Stop tracking these job parameters, as we've 'finished' executing.
jobParamsMap.remove(params);
if (mActivity != null) {
mActivity.onReceivedStopJob();
}
Log.i(TAG, "on stop job: " + params.getJobId());
return true;
}
public void setUiCallback(JobSchedulerSettingActivity activity) {
mActivity = activity;
}
/**
* Send job to the JobScheduler.
*/
public void scheduleJob(JobInfo info) {
Log.d(TAG, "Scheduling job");
JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
tm.schedule(info);//jobscheduleService
}
/**
* Not currently used, but as an exercise you can hook this
* up to a button in the UI to finish a job that has landed
* in onStartJob().
*/
public boolean callJobFinished() {
JobParameters params = jobParamsMap.poll();
if (params == null) {
return false;
} else {
jobFinished(params, false);
return true;
}
}
}
JobSchedulerSettingActivity
public class JobSchedulerSettingActivity extends AppCompatActivity {
private static final String TAG = "Lsn10Activity";
public static final int MSG_UNCOLOUR_START = 0;
public static final int MSG_UNCOLOUR_STOP = 1;
public static final int MSG_SERVICE_OBJ = 2;
// UI fields.
int defaultColor;
int startJobColor;
int stopJobColor;
private TextView mShowStartView;
private TextView mShowStopView;
private TextView mParamsTextView;
private EditText mDelayEditText;
private EditText mDeadlineEditText;
private RadioButton mWiFiConnectivityRadioButton;
private RadioButton mAnyConnectivityRadioButton;
private CheckBox mRequiresChargingCheckBox;
private CheckBox mRequiresIdleCheckbox;
ComponentName mServiceComponent;
/**
* Service object to interact scheduled jobs.
*/
TestJobService mTestService;
private static int kJobId = 0;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.lsn10_activity);
Resources res = getResources();
defaultColor = res.getColor(R.color.none_received);
startJobColor = res.getColor(R.color.start_received);
stopJobColor = res.getColor(R.color.stop_received);
// Set up UI.
mShowStartView = findViewById(R.id.onstart_textview);
mShowStopView = findViewById(R.id.onstop_textview);
mParamsTextView = findViewById(R.id.task_params);
mDelayEditText = findViewById(R.id.delay_time);
mDeadlineEditText = findViewById(R.id.deadline_time);
mWiFiConnectivityRadioButton = findViewById(R.id.checkbox_unmetered);
mAnyConnectivityRadioButton = findViewById(R.id.checkbox_any);
mRequiresChargingCheckBox = findViewById(R.id.checkbox_charging);
mRequiresIdleCheckbox = findViewById(R.id.checkbox_idle);
mServiceComponent = new ComponentName(this, TestJobService.class);
// Start service and provide it a way to communicate with us.
Intent startServiceIntent = new Intent(this, TestJobService.class);
startServiceIntent.putExtra("messenger", new Messenger(mHandler));
startService(startServiceIntent);
}
@SuppressLint("HandlerLeak")
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UNCOLOUR_START:
mShowStartView.setBackgroundColor(defaultColor);
break;
case MSG_UNCOLOUR_STOP:
mShowStopView.setBackgroundColor(defaultColor);
break;
case MSG_SERVICE_OBJ:
mTestService = (TestJobService) msg.obj;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mTestService.setUiCallback(JobSchedulerSettingActivity.this);
}
break;
}
}
};
private boolean ensureTestService() {
if (mTestService == null) {
if (mTestService == null) {
Toast.makeText(JobSchedulerSettingActivity.this, "Service null, never got callback?",
Toast.LENGTH_SHORT).show();
return false;
}
}
return true;
}
/**
* UI onclick listener to schedule a job. What this job is is defined in
* TestJobService#scheduleJob().
*/
@SuppressLint("NewApi")
public void scheduleJob(View v) {
if (!ensureTestService()) {
return;
}
JobInfo.Builder builder = new JobInfo.Builder(kJobId++, mServiceComponent);
String delay = mDelayEditText.getText().toString();
if (!TextUtils.isEmpty(delay)) {
builder.setMinimumLatency(Long.valueOf(delay) * 1000);
}
String deadline = mDeadlineEditText.getText().toString();
if (!TextUtils.isEmpty(deadline)) {
builder.setOverrideDeadline(Long.valueOf(deadline) * 1000);
}
boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked();
boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isChecked();
if (requiresUnmetered) {
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
} else if (requiresAnyConnectivity) {
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
}
builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked());//设置为空闲状态触发
builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked());// 手机是否处于充电状态
mTestService.scheduleJob(builder.build());
}
/**
* cancel All jobs
*
* @param v
*/
@SuppressLint("NewApi")
public void cancelAllJobs(View v) {
JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
tm.cancelAll();
}
/**
* UI onclick listener to call jobFinished() in our service.
*/
@SuppressLint("NewApi")
public void finishJob(View v) {
if (!ensureTestService()) {
return;
}
mTestService.callJobFinished();
mParamsTextView.setText("");
}
/**
* Receives callback from the service when a job has landed
* on the app. Colours the UI and post a message to
* uncolour it after a second.
*/
@SuppressLint("NewApi")
public void onReceivedStartJob(JobParameters params) {
KeyboardView keyboardView;
mShowStartView.setBackgroundColor(startJobColor);
Message m = Message.obtain(mHandler, MSG_UNCOLOUR_START);
mHandler.sendMessageDelayed(m, 1000L);// uncolour in 1 second.
mParamsTextView.setText("Executing: " + params.getJobId() + " " + params.getExtras());
}
/**
* Receives callback from the service when a job that
* previously landed on the app must stop executing.
* Colours the UI and post a message to uncolour it after a
* second.
*/
public void onReceivedStopJob() {
mShowStopView.setBackgroundColor(stopJobColor);
Message m = Message.obtain(mHandler, MSG_UNCOLOUR_START);
mHandler.sendMessageDelayed(m, 2000L); // uncolour in 1 second.
mParamsTextView.setText("");
}
}