【Android 電量優化】電量優化 ( JobScheduler | JobService | AsyncTask )





一、JobScheduler 使用流程



JobScheduler 使用流程 :


① 獲取 JobScheduler 服務 : 從 Context 對象中 , 調用 getSystemService 方法跨進程獲取 ;

mJobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);

② 創建 JobInfo 任務信息 :

//創建一個任務
JobInfo jobInfo = new
        JobInfo.Builder(0,  // 任務 id 爲 0
        new ComponentName(mContext, BpJobService.class))
        .setRequiresCharging(true)  // 要求在充電時執行
        .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) // 非蜂窩網絡執行
        .setExtras(extras).build();

③ 提交任務 :

mJobScheduler.schedule(jobInfo);

④ 執行任務 : 在 JobService 的 onStartJob 方法中 , 會由系統在合適的時間 , 執行相關任務 ;

public class BpJobService extends JobService {
    @Override
    public boolean onStartJob(JobParameters params) {
        // 啓動 AsyncTask 異步任務處理工作
        new JobAsyncTask().execute(params);
        return false;
    }
    // ... 省略部分代碼
}





二、AsyncTask 簡介



在 JobScheduler 提交任務後 , 系統會在 JobService 中執行相應的任務 , 執行的時機由系統選擇 ;

系統回調 JobService 服務中的 onStartJob 方法時 , 由用戶自行執行相應的任務 , 一般是使用 AsyncTask 來執行相應任務 ;


1 . AsyncTask<JobParameters, Void, Void> 三個泛型解析

  • 泛型 11 : 異步任務開始時 , execute 方法傳入的參數類型
  • 泛型 22 : 異步任務執行時 , 進度值類型
  • 泛型 33 : 異步任務結束時 , 結果類型

2 . AsyncTask 44 個方法解析 :

  • onPreExecute : doInBackground 之前執行的方法, 一般在該方法中執行初始化操作 ( 主線程, 可以更新 UI )
  • doInBackground : 主要的耗時操作是在該方法中執行的 ( 非主線程, 不能更新 UI )
  • onProgressUpdate : 在 doInBackground 中調用了 publishProgress 方法, 就會回調該方法 , 一般情況下是在該方法中執行更新 UI 的操作 ( 主線程, 可以更新 UI )
  • onPostExecute : doInBackground 執行完畢後 , 調用 return 方法後 , 該方法會被調用 ( 主線程, 可以更新 UI )

執行順序 : onPreExecute -> doInBackground -> onProgressUpdate -> onPostExecute





三、JobScheduler 開發流程



1 . 任務管理類 : 開發 JobScheduleManager 管理類 , 該類負責與 Service 服務中的需求對接 , 接收 Service 服務中的添加任務的需求 , 將任務操作轉爲參數 , 並提交到系統 JobScheduler 中 ;

2 . 任務執行服務 : 開發 JobService 服務 , 該服務是執行具體的任務的類 , 在該類中 , 接收到系統調度的任務參數 , 在 onStartJob 方法中解析這些參數 , 並創建 AsyncTask 執行對應的任務 ;

3 . 添加任務 : 在一個第三方 Service 服務中 , 調用 JobScheduleManager 類添加任務 , 系統會自動回調分配執行任務 , 在 JobService 中的 onStartJob 方法中執行任務 ;





四、JobScheduler 代碼示例





1、JobScheduleManager 代碼示例


該類主要用於管理 JobScheduler , 初始化 JobScheduler , 處理添加任務的選項等操作 , 如任務執行時機 , 執行需求 等 ;

package kim.hsl.bp;

import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
import android.os.PersistableBundle;
import android.util.Log;

import java.util.List;

public class JobScheduleManager {
    public static final String TAG = "JobScheduleManager";

    /**
     * 將不緊急的任務調度到更合適的時機進行處理
     * 如充電時 , 如 WIFI 連接時
     * 1. 避免頻繁由於執行單次任務 , 喚醒硬件模塊 , 造成電量浪費
     * 2. 避免在不合適的時機執行耗電任務 , 如使用蜂窩網絡在不合適的時候更新軟件
     */
    private JobScheduler mJobScheduler;

    /**
     * 上下文對象
     */
    private Context mContext;

    /*
        單例模式
     */
    private static JobScheduleManager mInstance;
    private JobScheduleManager(){}
    public static JobScheduleManager getInstance(){
        if(mInstance == null){
            mInstance = new JobScheduleManager();
        }
        return mInstance;
    }

    public void init(Context context){
        this.mContext = context;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mJobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
        }
    }

    public void addJob(String currentJobData){
        if(mJobScheduler == null){
            return;
        }

        Log.i(TAG, "添加任務 : " + currentJobData);

        // 查找 id 爲 0 的 任務
        JobInfo pendingJob = null;

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
            pendingJob = mJobScheduler.getPendingJob(0);

        }else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            List<JobInfo> allPendingJobs = mJobScheduler.getAllPendingJobs();
            for(JobInfo info : allPendingJobs){
                if(info.getId() == 0){
                    pendingJob = info;
                    break;
                }
            }
        }

        // 獲取任務執行數據
        String historyJobData = "";
        if(pendingJob != null){
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                PersistableBundle extras = pendingJob.getExtras();
                historyJobData = extras.getString("JOB_DATA");
                mJobScheduler.cancel(0);
            }
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            PersistableBundle extras = new PersistableBundle();
            extras.putString("JOB_DATA", currentJobData + "$" + historyJobData);

            //創建一個任務
            JobInfo jobInfo = new
                    JobInfo.Builder(0,  // 任務 id 爲 0
                    new ComponentName(mContext, BpJobService.class))
                    .setRequiresCharging(true)  // 要求在充電時執行
                    .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) // 非蜂窩網絡執行
                    .setExtras(extras).build();

            // 將任務提交到隊列中
            mJobScheduler.schedule(jobInfo);
        }
    }
}


2、JobService 與 AsyncTask 代碼示例


JobService 與 AsyncTask 代碼示例 :


注意 JobService 的兩個方法 onStartJob , onStopJob 的調用時機 , 與返回值含義 ;

注意 AsyncTask 定義時三個泛型的含義 , onPreExecute , doInBackground , onProgressUpdate , onPostExecute 四個方法的調用時機 ;

package kim.hsl.bp;

import android.app.job.JobParameters;
import android.app.job.JobService;
import android.os.AsyncTask;
import android.os.Build;
import android.os.PersistableBundle;
import android.util.Log;

import androidx.annotation.RequiresApi;

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class BpJobService extends JobService {
    public static final String TAG = "Battery_Performance.BpJobService";

    /**
     *
     * @param params
     * @return
     *      true 任務正要被執行, 需要開始執行任務
     *      false 任務執行完畢
     */
    @Override
    public boolean onStartJob(JobParameters params) {
        // 啓動 AsyncTask 異步任務處理工作
        new JobAsyncTask().execute(params);
        return false;
    }

    /**
     *
     * @param params
     * @return
     */
    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }

    /**
     * AsyncTask<JobParameters, Void, Void> 三個泛型解析
     * -- 1. 異步任務開始時 , execute 方法傳入的參數類型
     * -- 2. 異步任務執行時 , 進度值類型
     * -- 3. 異步任務結束時 , 結果類型
     */
    class JobAsyncTask extends AsyncTask<JobParameters, Void, Void> {

        /**
         * doInBackground 之前執行的方法, 一般在該方法中執行初始化操作
         * ( 主線程, 可以更新 UI )
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        /**
         * 主要的耗時操作是在該方法中執行的
         * ( 非主線程, 不能更新 UI )
         * @param jobParameters
         * @return
         */
        @Override
        protected Void doInBackground(JobParameters... jobParameters) {
            JobParameters parameters = jobParameters[0];
            PersistableBundle extras = parameters.getExtras();
            String jobData = extras.getString("JOB_DATA");
            Log.i(TAG, "JobAsyncTask 執行 : " + jobData);
            return null;
        }

        /**
         * 在 doInBackground 中調用了 publishProgress 方法, 就會回調該方法
         * 一般情況下是在該方法中執行更新 UI 的操作
         * ( 主線程, 可以更新 UI )
         * @param values
         */
        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        /**
         * doInBackground 執行完畢後 , 調用 return 方法後 , 該方法會被調用
         * ( 主線程, 可以更新 UI )
         * @param aVoid
         */
        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
        }
    }
}



3、AndroidManifest.xml 配置


主要是配置 AlarmManagerService 服務 和 BpJobService 服務 ;

注意爲 BpJobService 服務聲明 android.permission.BIND_JOB_SERVICE 權限 ;

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="kim.hsl.bp">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name=".BatteryReceiver" >
            <intent-filter>
                <!-- 充電線插上 -->
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
                <!-- 充電線拔出 -->
                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
            </intent-filter>
        </receiver>

        <receiver android:name=".WifiReceiver" >
            <intent-filter>
                <!-- 網絡狀態改變 -->
                <action android:name="android.intent.action.CONNECTIVITY_CHANGE" />
            </intent-filter>
        </receiver>

        <!-- WeakLock 保持 CPU 喚醒的 Service 服務 -->
        <service
            android:name=".WeakLockService"
            android:process=":weaklock" />

        <!-- AlarmManager 保持 CPU 喚醒的 Service 服務 -->
        <service
            android:name=".AlarmManagerService"
            android:process=":alrmmanager" />

        <!-- JobScheduler 服務 -->
        <service
            android:name=".BpJobService"
            android:process=":jobservice"
            android:permission="android.permission.BIND_JOB_SERVICE"/>

    </application>

</manifest>


4、執行結果


執行結果 :

2020-07-07 15:00:58.189 11091-11091/kim.hsl.bp:alrmmanager I/Battery_Performance.AlarmManagerService: AlarmManagerService onCreate

2020-07-07 15:01:19.056 11091-11091/kim.hsl.bp:alrmmanager I/Battery_Performance.AlarmManagerService: receiver ACTION
2020-07-07 15:01:19.057 11091-11091/kim.hsl.bp:alrmmanager I/Battery_Performance.JobScheduleManager: 添加任務 : ACTION(1594105279057)
2020-07-07 15:01:19.239 11158-11214/kim.hsl.bp:jobservice I/Battery_Performance.BpJobService: JobAsyncTask 執行 : ACTION(1594105279057)$

2020-07-07 15:02:38.985 11091-11091/kim.hsl.bp:alrmmanager I/Battery_Performance.AlarmManagerService: receiver ACTION
2020-07-07 15:02:38.986 11091-11091/kim.hsl.bp:alrmmanager I/Battery_Performance.JobScheduleManager: 添加任務 : ACTION(1594105358986)
2020-07-07 15:02:38.997 11158-11214/kim.hsl.bp:jobservice I/Battery_Performance.BpJobService: JobAsyncTask 執行 : ACTION(1594105358986)$
2020-07-07 15:03:19.058 11091-11091/kim.hsl.bp:alrmmanager I/Battery_Performance.AlarmManagerService: receiver ACTION




五、源碼及資源下載



源碼及資源下載地址 :

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章