使用AlarmManager、IntentService和PendingIntent相互配合,創走周期性的后台任務,實現一個完全可用的后台服務還需要手動執行以下操作。
計划一個周期性任務
檢查周期性任務的運行狀態
檢查網絡是否可用
在實際場景下,還有更多想法需要實現,例如請求失敗,是否還需要稍后重試機制。或者是只允許應用使用不限量的網絡連接...
在系統控制方面,本章實現的后台服務也存在一些問題,它無法在某種情況停下來,除非手動停止。
JobScheduler:除了實現常規后台任務之外,JobScheduler還支持按場景、按條件運行后台服務。
JobScheduler的使用:
創建一個類繼承JobService,並覆蓋onStartJob(JobParameters parms)方法和onStopJob(JobParameters params)。
public class PollService extends JobService { @Override public boolean onStartJob(JobParameters params) { return false; } @Override public boolean onStopJob(JobParameters params) { return false; } }
Android准備好執行任務時,服務就會啟動,此時會在主線程上收到onStartJob()方法調用。該方法返回false結果表示:"交代的任務我已全力去做,現在做完了。”返回 true 結果則表示:“任務收到,正在做,但是還沒有做完。”
JobService在執行的時候需要單開線程,可以使用AsyncTask按如下方式創建新線程。
private PollTask mCurrentTask; @Override public boolean onStartJob(JobParameters parms){ mCurrentTask = new PollTask(); mCurrentTask.execute(parms); return true; } private class PollTask extends AsyncTask<JobParameters,Void,Void> { @Override protected Void doInBackground(JobParameters... params) { JobParameters jobParams = params[0]; //執行任務的邏輯 jobFinished(jobParams, false); return null; } }
任務執行完畢后,就可以調用jobFinished(JobParameters, boolean)方法通知結果,如果該方法的第二個參數傳入true的話,就等於說:“事情這次做不完了,請計划在下次某個時間繼續吧。”
onStopJob(JobParameters)方法適合在中斷任務時調用,用戶通常需要服務在有WIFI連接時才運行,如果在調用JobFinished()之前(任務完成之前),手機就沒了Wifi,onStopJob(...) 方法就會被調用,也就是說,一切任務就立即停止了。
@Override public boolean onStopJob(JobParameters params) { if (mCurrentTask != null) { mCurrentTask.cancel(true); } return true; }
調用 onStopJob(...) 方法就是表明,服務馬上就要停掉了。不要抱有幻想,請立即停止手頭上的一切事情。這里,返回 true 表示:“任務應該計划在下次繼續。”返回 false 表示:“不管怎樣,事情就到此結束吧,不要計划下次了。”
使用JobService,必須在Manifest配置清單中添加權限:
<service android:name=".PollService" android:permission="android.permission.BIND_JOB_SERVICE" //添加的權限控制只有JobScheduler才能運行它。 android:exported="true"/>
通過JobScheduler檢查是否已計划好了任務:
final int JOB_ID = 1; JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); boolean hasBeenScheduled = false; for (JobInfo jobInfo : scheduler.getAllPendingJobs()) { if (jobInfo.getId() == JOB_ID) { hasBeenScheduled = true; } }
PoolService的運行:
final int JOB_ID = 1; JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo jobInfo = new JobInfo.Builder(JOB_ID, new ComponentName(context, PollService.class)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setPeriodic(1000 * 60 * 15) .setPersisted(true) .build();
scheduler.schedule(jobInfo);
上述代碼計划任務每15分鍾運行一次,但前提條件是有Wi-Fi或有可用的不限流量網絡。調用 setPersisted(true) 方法可保證服務在設備重啟后也能按計划運行。