性能優化10_JobScheduler

Android性能優化彙總

一 JobScheduler

wake_lock雖然好用,但是也會導致大量高頻次的CPU喚醒及操作,最好把這些操作集中處理。因此,系統提供給我們更好的API去使用:JobScheduler
JobSchedule的用處
單獨訪問1000次,和將1000次放到一起訪問;前者更加耗電,因爲會重複的激活網絡開啓狀態
JobSchedule可以根據傳入的一些參數做些決策(最小延時、最多延時、網絡類型)
當滿足條件時,再去執行任務

兼容:
5.0以後用JobSchedule
5.0 以前用GCM–谷歌的GooglePlay servicemanager(國內屏蔽)
拓展:Doze 深度休眠

二 API

  • 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 任務完成時執行

三 Demo

在前面使用WeakLock執行下載任務防止中斷,當多個下載任務,可能造成屏幕頻繁喚醒,比較消耗電量,這裏可以用JobScheduler進行批量執行下載任務

1. 自定義一個JobService

@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);
        }
    }
}

2. 執行JobScheduler任務

 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);
            }

        }

    }

3. 代碼

JobSchedulerActivity

四 JobScheduler任務監聽

  • 定義一個Handler
  • 將Handler封裝進Messenger傳入要執行的JobService
  • JobService的onStartCommand中intent獲取Handler
  • Handler 對象發送消息
    TestJobService
@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("");
    }
}

代碼:JobSchedulerSettingActivity

發佈了231 篇原創文章 · 獲贊 75 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章