1. 程式人生 > >android 7 JobScheduler實現APP保活

android 7 JobScheduler實現APP保活

JobScheduler:當一系列預置的條件被滿足時,JobScheduler API為你的應用執行一個操作,例如當裝置接通電源介面卡或者連線到WIFI,在API 21 ( Android 5.0(Lollipop) )中,google提供了一個新叫做JobScheduler API的元件來處理這樣的場景。 
在API 24 ( Android 7.0(N) )的新特性中,Google對於新裝置功耗要求越來越嚴格,對於APP的限制也越來約多,想繼續像Android5.0、6.0一樣簡單處理定時執行執行週期性作業,需要做出一些程式碼調整,針對該需求,總結了2種解決方案供參考。

  • setPeriodic 
    setPeriodic:按時間間隔執行週期性作業,在Android 5、6平臺版本下可以間隔任何時間執行,在Android7.0平臺版本上需設定定期作業的間隔時間>=15分鐘時才能執行。

  • setMinimumLatency 
    設定作業延遲執行的時間,與setPeriodic不可同時執行,可配置setOverrideDeadline設定作業最大延遲執行時間。

  • setRequiredNetworkType 
    設定作業只有在滿足指定的網路條件時才會被執行

/** 預設條件,不管是否有網路這個作業都會被執行 */
public static final int NETWORK_TYPE_NONE = 0;
/** 任意一種網路這個作業都會被執行 */
public static final int NETWORK_TYPE_ANY = 1;
/** 不是蜂窩網路( 比如在WIFI連線時 )時作業才會被執行 */
public static final int NETWORK_TYPE_UNMETERED = 2; /** 不在漫遊時作業才會被執行 */ public static final int NETWORK_TYPE_NOT_ROAMING = 3;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

解決方案1

使用Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)建立JobScheduler物件,配置排程工作setMinimumLatency等引數,延遲執行Job Service,在JobSchedulerService服務的onStartJob方法中啟動保活服務,再建立一個新的JobScheduler任務,並結束當前JobScheduler任務。

如果正在執行需要很長時間的任務,則下一個任務將安排在當前任務完成時間+ PROVIDED_TIME_INTERVAL

  • 建立JobScheduler 
    設定setExtras引數,將需要保活的服務名稱傳遞至Job Service
 //API大於24
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    //7.0+
                    mJobScheduler = (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
                    JobInfo.Builder builder = new JobInfo.Builder(1,
                            new ComponentName(mContext.getPackageName(), JobSchedulerService.class.getName()));
                    builder.setMinimumLatency(2 * 60 * 1000);
                    PersistableBundle persiBundle = new PersistableBundle();
                    persiBundle.putString("servicename", service.getName());
                    builder.setExtras(persiBundle);
                    if (mJobScheduler.schedule(builder.build()) <= 0) {
                        //If something goes wrong
                    }
                }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 執行JobSchedulerService 工作任務 
    在onStartJob方法中,執行startService方法啟動保活服務,建立一個新的JobScheduler物件,手動呼叫jobFinished(params, false)結束作業,並將返回值設為true
onStartJob的返回值區別:
(1) false:框架認為你作業已經執行完畢了,那麼下一個作業就立刻展開了
(2) true:框架將作業結束狀態交給你去處理。因為我們可能會非同步的通過執行緒等方式去執行工作,這個時間肯定不能放在主執行緒裡面去控制,這時候需要手動呼叫jobFinished(JobParameters params, boolean needsReschedule)方法去告訴框架作業結束了,其中needsReschedule表示是否重複執行
  • 1
  • 2
  • 3
public boolean onStartJob(JobParameters params) {
        try {
            Log.d("JobSchedulerService","start~~"+ System.currentTimeMillis());
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                Log.d("JobSchedulerService", "7.0 handleMessage task running");
                String servicename = params.getExtras().getString("servicename");
                Class service = getClassLoader().loadClass(servicename);
                if (service != null) {
                    Log.d("JobSchedulerService", "7.0 handleMessage task running ~~2~~"+service.hashCode());
                    //判斷保活的service是否被殺死
                    if (!isMyServiceRunning(service)) {
                        //重啟service
                        startService(new Intent(getApplicationContext(), service));
                    }
                }
                //建立一個新的JobScheduler任務
                scheduleRefresh(servicename);
                jobFinished(params, false);
                Log.d("JobSchedulerService","7.0 handleMessage task end~~"+ System.currentTimeMillis());
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • scheduleRefresh
private void scheduleRefresh(String serviceName) {
        JobScheduler mJobScheduler = (JobScheduler)getApplicationContext()
                .getSystemService(JOB_SCHEDULER_SERVICE);
        //jobId可根據實際情況設定        
        JobInfo.Builder mJobBuilder =
                new JobInfo.Builder(0,
                        new ComponentName(getPackageName(),
                                JobSchedulerService.class.getName()));

        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            mJobBuilder.setMinimumLatency(2* 60 * 1000).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
            PersistableBundle persiBundle = new PersistableBundle();
            persiBundle.putString("servicename", serviceName);
            mJobBuilder.setExtras(persiBundle);
        }

        if (mJobScheduler != null && mJobScheduler.schedule(mJobBuilder.build())
                <= JobScheduler.RESULT_FAILURE) {
            //Scheduled Failed/LOG or run fail safe measures
            Log.d("JobSchedulerService", "7.0 Unable to schedule the service FAILURE!");
        }else{
            Log.d("JobSchedulerService", "7.0 schedule the service SUCCESS!");
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 判斷保活Service是否在執行
    public boolean isMyServiceRunning(Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

解決方案(作業間隔時間>=15分鐘)

設定作業的間隔時間大於15分鐘,採用setPeriodic方法實現定時執行週期性作業。

private static final long REFRESH_INTERVAL = 15 * 60 * 1000;

  • Job Scheduler程式碼
public static void scheduleJob(Context context) {
    ComponentName serviceComponent = new ComponentName(context, PAJobService.class);
    JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, serviceComponent);
    builder.setPeriodic(15 * 60 * 1000, 5 * 60 *1000);

    JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
    int ret = jobScheduler.schedule(builder.build());
    if (ret == JobScheduler.RESULT_SUCCESS) {
        Log.d(TAG, "Job scheduled successfully");
    } else {
        Log.d(TAG, "Job scheduling failed");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • JobService服務
public class PAJobService extends JobService {
    private static final String TAG = PRE_TAG + PAJobService.class.getSimpleName();
    private LocationManager mLocationManager;

    public boolean onStartJob(JobParameters params) {
        Log.d(TAG, "onStartJob");
        Toast.makeText(getApplicationContext(), "Job Started", Toast.LENGTH_SHORT).show();
        return false;
    }

    public boolean onStopJob(JobParameters params) {
        Log.d(TAG, "onStopJob");
        return false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15