Jobscheduler的使用

通过ADB命令查看jobservice 信息:

adb shell dumpsys jobscheduler
JOB #u0a173/1: 67cd0a jie.com.oasystem/.job.MyJobService
    u0a173 tag=*job*/jie.com.oasystem/.job.MyJobService
    Source: uid=u0a173 user=0 pkg=jie.com.oasystem
    JobInfo:
      Service: jie.com.oasystem/.job.MyJobService
      PERIODIC: interval=+15m0s0ms flex=+5m0s0ms
      Requires: charging=false batteryNotLow=false deviceIdle=false
      Network type: NetworkRequest [ NONE id=0, [ Capabilities: INTERNET&NOT_RESTRICTED&TRUSTED&VALIDATED Uid: 10173] ]
      Backoff: policy=1 initial=+30s0ms
      Has early constraint
      Has late constraint
    Required constraints: TIMING_DELAY DEADLINE CONNECTIVITY [0xd0000000]
    Satisfied constraints: CONNECTIVITY DEVICE_NOT_DOZING BACKGROUND_NOT_RESTRICTED [0x12400000]
    Unsatisfied constraints: TIMING_DELAY DEADLINE [0xc0000000]
    Tracking: CONNECTIVITY TIME
    Network: 121
    Standby bucket: ACTIVE
    Enqueue time: -9m4s323ms
    Run time: earliest=+55s677ms, latest=+5m55s677ms
    Last run heartbeat: -130
    Ready: false (job=false user=true !pending=true !active=true !backingup=true comp=true)


一、JobScheduler诞生

Android 5.0系统以前,在处理一些特定情况下的任务,或者是为了应用的保活,我们通常是使用了Service常驻后台来满足我们的需求。当达到某个条件时触发该Service来进行相应任务的处理。或者仅仅是为了我们自己的应用不被系统回收销毁。这样做在满足了自己应用的需求的同时也消耗了部分硬件性能。对用户的体验上,和Android系统环境上都有不利的影响。而且在其它地方,大多数开发者都认为在后台永驻进程,是获取用户的隐私,是不合法的。然而在我国也许是因为开发商的需求大,迫切想要达到自己的目标,使用永驻的进程可以完成用户行为分析和推送等其它后台的业务。因此在开发的模式上采取了极端的个体主义思想。

Android5.0系统以后,Google为了优化Android系统,提高使用流畅度以及延长电池续航,加入了在应用后台/锁屏时,系统会回收应用,同时自动销毁应用拉起的Service的机制。同时为了满足在特定条件下需要执行某些任务的需求,google在全新一代操作系统上,采取了Job
(jobservice &
JobInfo)的方式,即每个需要后台的业务处理为一个job,通过系统管理job,来提高资源的利用率,从而提高性能,节省电源。这样又能满足APP开发商的要求,又能满足系统性能的要求。Jobscheduler由此应运而生

二、Jobscheduler特性 、适用

1.特性
1、支持在一个任务上组合多个条件
2、内置条件:设备待机、设备充电和连接网络
3、支持持续的job,这意味着设备重启后,之前被中断的job可以继续执行
4、支持设置job的最后执行期限
5、根据你的配置,可以设置job在后台运行还是在主线程中运行
2.适用
需要在Android设备满足某种场合才需要去执行处理数据:
1、应用具有可以推迟的非面向用户的工作(定期数据库数据更新)
2、应用具有当插入设备时希望优先执行的工作(充电时才希望执行的工作备份数据)
3、需要访问网络或 Wi-Fi 连接时需要进行的任务(如向服务器拉取内置数据)
4、希望作为一个批次定期运行的许多任务
3.特征
1、Job Scheduler只有在Api21或以上的系统支持。
2、Job Scheduler是将多个任务打包在一个场景下执行。
3、在系统重启以后,任务会依然保留在Job Scheduler当中,因此不需要监听系统启动状态重复设定。
4、如果在一定期限内还没有满足特定执行所需情况,Job Scheduler会将这些任务加入队列,并且随后会进行执行。

三、使用

Jobscheduler使用流程分四部分:

1.定义类继承JobService类,定义需要执行的任务(UI线程)
2.获取JobScheduler实例
3.构建JobInfo实例,指定JobService任务实现类以及执行条件
4.通过Jobscheduler假如到任务队列

①定义JobService派生类:
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends JobService {
    public static final int MYJOBSERVICE_ID =1;
    private JobParameters parameters;
    private Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("wangtjingjie","onCreate... ");

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("wangtjingjie","onStartCommand... ");

        this.context =getApplicationContext();
        return START_STICKY;
    }

    @Override
    public boolean onStartJob(final JobParameters params) {

        Log.d("wangtjingjie","onstartjob... ");

        this.parameters = params;
        //返回true,表示该工作耗时,同时工作处理完成后需要调用onStopJob销毁(JobFinished)
        //jobFinished(JobParameters params, boolean needsRescheduled)告知系统,该任务已经处理完成。
        //如果返回值为true,我们需要手动调用jobFinished来停止该任务
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//
                DBOpenHelper openHelper =new DBOpenHelper(context);
                Person person = new Person();
//                Random random =new Random(100);

                person.setName("wangjingjie" + random.nextInt());
                person.setIdcardNum("idcardNum" + random.nextInt());
                person.setCreatetime("createtime" + random.nextInt());
                person.setId("10000" + random.nextInt());
                openHelper.writePerson(person);
//                Log.d("wangtjingjie","jobService 插入数据:"+random.nextInt());
//                Message message = Message.obtain();
//                message.obj = params;
//                message.what = 1;
//                handler.sendMessage(message);
//            }
//        }).start();
        OkUtils.getRequest(Constant.BANNER_URL, context, new LinkedHashMap<String, String>(), new JsonCallback<ResponseBean<List<BannerBean>>>() {
            @Override
            public void onSuccess(Response<ResponseBean<List<BannerBean>>> response) {
                List<BannerBean> banneBeanList=response.body().getData();
                Log.d("wangjingjie", "onSuccess: "+banneBeanList.toString());
            }
        });
        //返回false,表示任务不需要很长时间,到return时已完成任务处理
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        //有并且仅有onstartJob返回值为true时,才会调用onStopJob来销毁job
        //返回false来销毁这个工作
        return false;
    }

    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            Toast.makeText(getApplicationContext(), "JobService  task running", Toast.LENGTH_SHORT).show();
            jobFinished((JobParameters) msg.obj, true);
            return true;
        }
    });
}

1、OnStartJob(JobParameters params)

当开始一个任务时,onstartjob(jobparameters params)
是必须使用的方法,因为它是系统用来触发已经安排的工作(job)的。
从上边的用例代码可以看到,该方法返回一个布尔值。不同的返回值对应了不同的处理方式。

如果返回值是false,该系统假定任何任务运行不需要很长时间并且到方法返回时已经完成。
如果返回值是true,那么系统假设任务是需要一些时间并且是需要在我们自己应用执行的。 当给定的任务完成时需要通过调用
jobFinished(JobParameters params, boolean
needsRescheduled)告知系统,该任务已经处理完成。 如果返回值为true,我们需要手动调用jobFinished来停止该任务

2、JobFinished

void jobFinished (JobParameters params, boolean needsReschedule)
Callback to inform the JobManager you’ve finished executing. This can
be called from any thread, as it will ultimately be run on your
application’s main thread. When the system receives this message it
will release the wakelock being held.
回调通知已完成执行的JobManager。这可以从任何线程调用,因为它最终将在应用程序的主线程上运行。当系统收到该消息时,它将释放正在保存的唤醒。

JobParameters params
– onStartJob(JobParameters). 传入的param需要和onStartJob中的param一致

boolean needsReschedule
– True if this job should be rescheduled according to the back-off criteria specified at schedule-time. False otherwise.
如果这项工作应按照计划时间指定的停止条件进行重新安排,则传入true。 否则的话传入false。
说人话就是让系统知道这个任务是否应该在最初的条件下被重复执行(稍后会介绍这个布尔值的用处)

3、OnStopJob(JobParameters params)

当收到取消请求时,onStopJob(JobParameters params)是系统用来取消挂起的任务的。
重要的是要注意到,如果onStartJob(JobParameters params)返回
false,当取消请求被接收时,该系统假定没有目前运行的工作。换句话说,它根本就不调用onStopJob(JobParameters
params)。那此时就需要我们手动调用jobFinished (JobParameters params, boolean
needsReschedule)方法了。

要注意的是,工作服务需要在应用程序的主线程上运行。这意味着,须使用另一个线程处理程序,或运行时间更长的任务异步任务用以不阻塞主线程。

简单的来讲,我们可以在上面JobSchedulerService类中创建一个Handler或者AsyncTask来处理需要进行的Job。

public class JobSchedulerService extends JobService {
 
    @Override
    public boolean onStartJob(JobParameters params) {
        // 返回true,表示该工作耗时,同时工作处理完成后需要调用jobFinished销毁
        mJobHandler.sendMessage(Message.obtain(mJobHandler, 1, params));
        return true;
    }
 
    @Override
    public boolean onStopJob(JobParameters params) {
        mJobHandler.removeMessages(1);
        return false;
    }
    
    // 创建一个handler来处理对应的job
    private Handler mJobHandler = new Handler(new Handler.Callback() {
        // 在Handler中,需要实现handleMessage(Message msg)方法来处理任务逻辑。
        @Override
        public boolean handleMessage(Message msg) {
            Toast.makeText(getApplicationContext(), "JobService task running", Toast.LENGTH_SHORT).show();
            // 调用jobFinished
            jobFinished((JobParameters) msg.obj, false);
            return true;
        }
    });
} 

异步任务也是可行的

public class JobSchedulerService extends JobService {
 
    private JobParameters mJobParameters
 
    @Override
    public boolean onStartJob(JobParameters params) {
        // 返回true,表示该工作耗时,同时工作处理完成后需要调用jobFinished销毁
        mJobParameters = params;
        mTask.execute();
        return true;
    }
 
    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
 
    private AsyncTask<Void, Void, Void> mTask = new AsyncTask<Void, Void, Void>() {
 
        @Override
        protected Void doInBackground(Void... params) {
            // TODO Auto-generated method stub
            return null;
        }
 
        @Override
        protected void onPostExecute(Void result) {
            // TODO Auto-generated method stub
            Toast.makeText(wenfengService.this, "finish job", 1000).show();
            jobFinished(mJobParameters, true);
            super.onPostExecute(result);
        }
    } 
}

任务失败的情况有很多,例如下载失败了,例如下载过程wifi断掉了。
例如如果下载过程中,wifi断掉了,JobService会回调onStopJob函数,这是只需要把函数的返回值设置为true就可以了。当wifi重新连接后,JobService会重新回调onStartJob函数。
而如果下载失败了,例如上面的例子中的mJobHandler执行失败,怎么办呢?我们只需要在Handler的handleMessage中执行jobFinished(mJobParameters,
true),这里的true代表任务要在wifi条件重新满足情况下重新调度。

4.清单文件中注册service

<service
            android:name=".service.MyJobService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="true"
            android:enabled="true"/>
②创建JobScheduler对象
JobScheduler jobscheDuler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
③构建JobInfo实例,指定JobService任务实现类以及执行条件

JobInfo

Constants:

BACKOFF_POLICY_EXPONENTIAL 、BACKOFF_POLICY_LINEAR 与setBackoffCriteria
(long initialBackoffMillis, int backoffPolicy)中backoffPolicy对应

DEFAULT_INITIAL_BACKOFF_MILLIS 默认的执行延迟时间

MAX_BACKOFF_DELAY_MILLIS 最大的执行延迟时间

NETWORK_TYPE_ANY 网络状态联网就行setRequiredNetworkType

NETWORK_TYPE_NONE 默认的网络连接状态setRequiredNetworkType

NETWORK_TYPE_NOT_ROAMING 移动网络连接情况setRequiredNetworkType

NETWORK_TYPE_UNMETERED WIFI连接情况setRequiredNetworkType

JobInfo.Builder

JobInfo.Builder(int jobId, ComponentName jobService) Initialize a new
Builder to construct a JobInfo.

JobInfo.Builder接收两个参数

jobId : 要运行的任务的标识符
jobService : Service组件的类名

下面简要的介绍部分builder中的方法:(截止Android API 25)

addTriggerContentUri(JobInfo.TriggerContentUri uri):添加一个TriggerContentUri,该Uri将利用ContentObserver来监控一个Content Uri,当且仅当其发生变化时将触发任务的执行。为了持续监控content的变化,你需要在最近的任务触发后再调度一个新的任务(需要注意的是触发器URI不能与setPeriodic(long)或setPersisted(boolean)组合使用。要持续监控内容更改,需要在完成JobService处理最近的更改之前,调度新的JobInfo,观察相同的URI。因为设置此属性与定期或持久化Job不兼容,这样做会在调用build()时抛出IllegalArgumentException异常。)

setBackoffCriteria(long initialBackoffMillis, int
backoffPolicy):特殊:设置回退/重试的策略,详细的可以参阅Google API。
类似网络原理中的冲突退避,当一个任务的调度失败时需要重试,所采取的策略。第一个参数时第一次尝试重试的等待间隔,单位为毫秒,预设的参数有:DEFAULT_INITIAL_BACKOFF_MILLIS
30000 、MAX_BACKOFF_DELAY_MILLIS
18000000。第二个参数是对应的退避策略,预设的参数有:BACKOFF_POLICY_EXPONENTIAL
二进制退避。等待间隔呈指数增长 BACKOFF_POLICY_LINEAR。

setExtras(PersistableBundle extras):设置可选附件。这是持久的,所以只允许原始类型。

setMinimumLatency(long minLatencyMillis):
这个函数能用以设置任务的延迟执行时间(毫秒),相当于post delay。

setOverrideDeadline(long maxExecutionDelayMillis): 这个方法让用以设置任务最晚的延迟时间
。如果到了规定的时间时其他条件还未满足,任务也会被启动。

setPeriodic(long time):设置任务运行的周期(每X毫秒,运行一次)。衍生在运行一些权限检查的时候如果可以使用job
scheduler的话,可以这样来循环检查权限的开启,但是目前的触发条件没有权限部分的触发。

setPeriodic(long intervalMillis, long
flexMillis):设置在Job周期末的一个flex长度的窗口,任务都有可能被执行 require API LEVEL 24

setPersisted(boolean isPersisted): 这个方法告诉系统当设备重启之后任务是否还要继续执行。

setRequiredNetworkType(int networkType):
这个方法让这个任务只有在满足指定的网络条件时才会被执行。默认条件是JobInfo.NETWORK_TYPE_NONE,这意味着不管是否有网络这个任务都会被执行。另外两个可选类型,一种是JobInfo.NETWORK_TYPE_ANY,它表明需要任意一种网络才使得任务可以执行。另一种是JobInfo.NETWORK_TYPE_UNMETERED,它表示设备不是蜂窝网络(
比如在WIFI连接时 )时任务才会被执行。

setRequiresCharging(boolean requiresCharging):
只有当设备在充电时这个任务才会被执行。这个也并非只是插入充电器,而且还要在电池处于健康状态的情况下才会触发,一般来说是手机电量>15%

setRequiresDeviceIdle(boolean
requiresDeviceIdle):指定Job在空闲状态才能运行。设备处于屏幕关闭或dreaming状态(类似window的休眠动画状态)71分钟后,执行工作

setTransientExtras(Bundle extras):设置可选的临时附加功能。(Android O Developer
Preview)这里指定了需要Android O 系统,可以看出,Google还是在继续完善job
scheduler的,期待能够代替传统的service组件成为主流。

setTriggerContentMaxDelay(long
durationMs):设置从第一次检测到内容更改到Job之前允许的最大总延迟(以毫秒为单位)。说人话就是设置从content变化到任务被执行,中间的最大延迟。
同样的require API 24

setTriggerContentUpdateDelay(long
durationMs):设置从content变化到任务被执行中间的延迟。如果在延迟期间content发生了变化,延迟会重新计算

setExtras(PersistableBundle extra):Bundle

要注意的是:

1、设置延迟时间 setMinimumLatency(long minLatencyMillis)和设置最终期限时间
setOverrideDeadline(long
maxExecutionDelayMillis)的两个方法不能同时与setPeriodic(long
time)同时设置,也就是说,在设置延迟和最终期限时间时是不能设置重复周期时间的。还有在具体开发过程中需要注意各个方法的API兼容情况。

2、setRequiredNetworkType(int networkType), setRequiresCharging(boolean
requireCharging) 和 setRequiresDeviceIdle(boolean
requireIdle)这几个方法可能会使得任务无法执行,除非调用setOverrideDeadline(long
time)设置了最大延迟时间,使得任务在为满足条件的情况下也会被执行。

构建一个JobInfo对象设置预置的条件,然后通过如下所示的代码将它发送到的JobScheduler中。

开启一个JobScheduler任务:

mJobScheduler.schedule(JobInfo job)

在schedule时,会返回一个int类型的值来标记这次任务是否执行成功,如果返回小于0的错误码,这表示该次任务执行失败,反之则成功(成功会返回该任务的id,这里可以使用这个id来判断哪些任务成功了)。所以在返回值小于0的时候就需要我们手动去处理一些事情了。

    if(mJobScheduler.schedule(JobInfo job) < 0){
        // do something when schedule goes wrong
    }

最后如果需要停止一个任务,就通过JobScheduler中,cancel(int jobId)来实现(所以之前在Builer中的指定id又有了重要作用);如果想取消所有的任务,可以调用JobScheduler对象的cancelAll()来实现。

④JobScheduler使用

public class SchedulerAcitvity extends Activity {  
 
    private static final String TAG = "SchedulerAcitvity";  
 
    public static final String MESSENGER_INTENT_KEY = TAG + ".MESSENGER_INTENT_KEY";
    public static final String WORK_DURATION_KEY = TAG + ".WORK_DURATION_KEY";
    public static final int MSG_JOB_START = 0;
    public static final int MSG_JOB_STOP = 1;
    public static final int MSG_ONJOB_START = 2;
    public static final int MSG_ONJOB_STOP = 3;
 
    private int mJobId = 0;// 执行的JobId
 
    ComponentName mServieComponent;// 这就是我们的jobservice组件了
    private IncomingMessageHandler mHandler;// 用于来自服务的传入消息的处理程序。
    
    // UI
    private EditText mEt_Delay;// 设置delay时间
    private EditText mEt_Deadline;// 设置最长的截止时间
    private EditText mEt_DurationTime;// setPeriodic周期
    private RadioButton mRb_WiFiConnectivity;// 设置builder中的是否有WiFi连接
    private RadioButton mRb_AnyConnectivity;// 设置builder中的是否有网络即可
    private CheckBox mCb_RequiresCharging;// 设置builder中的是否需要充电
    private CheckBox mCb_RequiresIdle;// 设置builder中的是否设备空闲
    private Button mBtn_StartJob;// 点击开始任务的按钮
    private Button mBtn_StopAllJob;// 点击结束所有任务的按钮
    
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.durian_main);
 
        mHandler = new IncomingMessageHandler(this);
        mServieComponent = new ComponentName(this, MyJobService.class);// 获取到我们自己的jobservice,同时启动该service
 
        // 设置UI
        mEt_Delay = (EditText) findViewById(R.id.delay_time);
        mEt_DurationTime = (EditText) findViewById(R.id.duration_time);
        mEt_Deadline = (EditText) findViewById(R.id.deadline_time);
        mRb_WiFiConnectivity = (RadioButton) findViewById(R.id.checkbox_unmetered);
        mRb_AnyConnectivity = (RadioButton) findViewById(R.id.checkbox_any);
        mCb_RequiresCharging = (CheckBox) findViewById(R.id.checkbox_charging);
        mCb_RequiresIdle = (CheckBox) findViewById(R.id.checkbox_idle);
        mBtn_StartJob = (Button)findViewById(R.id.button_start_job);
        mBtn_StopAllJob = (Button)findViewById(R.id.button_start_job);
 
        mBtn_StartJob.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                scheduleJob();
                }  
        }); 
 
        mBtn_StopAllJob.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                cancelAllJobs();
                }  
        });
    }
    
    @Override
    protected void onStart() {
        super.onStart();
        // 启动服务并提供一种与此类通信的方法。
        Intent startServiceIntent = new Intent(this, MyJobService.class);
        Messenger messengerIncoming = new Messenger(mHandler);
        startServiceIntent.putExtra(MESSENGER_INTENT_KEY, messengerIncoming);
        startService(startServiceIntent);
    }
    
    @Override
    protected void onStop() {
        // 服务可以是“开始”和/或“绑定”。 在这种情况下,它由此Activity“启动”
        // 和“绑定”到JobScheduler(也被JobScheduler称为“Scheduled”)。
        // 对stopService()的调用不会阻止处理预定作业。
        // 然而,调用stopService()失败将使它一直存活。
        stopService(new Intent(this, MyJobService.class));
        super.onStop();
    }
    
    // 当用户单击SCHEDULE JOB时执行。
    public void scheduleJob() {
        //开始配置JobInfo
        JobInfo.Builder builder = new JobInfo.Builder(mJobId++, mServiceComponent);
 
        //设置任务的延迟执行时间(单位是毫秒)
        String delay = mEt_Delay.getText().toString();
        if (!TextUtils.isEmpty(delay)) {
            builder.setMinimumLatency(Long.valueOf(delay) * 1000);
        }
        //设置任务最晚的延迟时间。如果到了规定的时间时其他条件还未满足,你的任务也会被启动。
        String deadline = mEt_Deadline.getText().toString();
        if (!TextUtils.isEmpty(deadline)) {
            builder.setOverrideDeadline(Long.valueOf(deadline) * 1000);
        }
        boolean requiresUnmetered = mRb_WiFiConnectivity.isChecked();
        boolean requiresAnyConnectivity = mRb_AnyConnectivity.isChecked();
 
        //让你这个任务只有在满足指定的网络条件时才会被执行
        if (requiresUnmetered) {
            builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
        } else if (requiresAnyConnectivity) {
            builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
        }
 
        //你的任务只有当用户没有在使用该设备且有一段时间没有使用时才会启动该任务。
        builder.setRequiresDeviceIdle(mCb_RequiresIdle.isChecked());
        //告诉你的应用,只有当设备在充电时这个任务才会被执行。
        builder.setRequiresCharging(mCb_RequiresCharging.isChecked());
 
        // Extras, work duration.
        PersistableBundle extras = new PersistableBundle();
        String workDuration = mEt_DurationTime.getText().toString();
        if (TextUtils.isEmpty(workDuration)) {
            workDuration = "1";
        }
        extras.putLong(WORK_DURATION_KEY, Long.valueOf(workDuration) * 1000);
 
        builder.setExtras(extras);
 
        // Schedule job
        Log.d(TAG, "Scheduling job");
        JobScheduler mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        // 这里就将开始在service里边处理我们配置好的job
        mJobScheduler.schedule(builder.build());
 
        //mJobScheduler.schedule(builder.build())会返回一个int类型的数据
        //如果schedule方法失败了,它会返回一个小于0的错误码。否则它会返回我们在JobInfo.Builder中定义的标识id。
    }
 
    // 当用户点击取消所有时执行
    public void cancelAllJobs() {
        JobScheduler mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        mJobScheduler.cancelAll();
        Toast.makeText(MainActivity.this, R.string.all_jobs_cancelled, Toast.LENGTH_SHORT).show();
    }
 
    /**
    * {@link Handler}允许您发送与线程相关联的消息。
    * {@link Messenger}使用此处理程序从{@link MyJobService}进行通信。
    * 它也用于使开始和停止视图在短时间内闪烁。
    */
    private static class IncomingMessageHandler extends Handler {
 
        // 使用弱引用防止内存泄露
        private WeakReference<SchedulerAcitvity> mActivity;
 
        IncomingMessageHandler(SchedulerAcitvity activity) {
            super(/* default looper */);
            this.mActivity = new WeakReference<>(activity);
        }
 
        @Override
        public void handleMessage(Message msg) {
            SchedulerAcitvity mSchedulerAcitvity = mActivity.get();
            if (mSchedulerAcitvity == null) {
                // 活动不再可用,退出。
                return;
            }
            
            // 获取到两个View,用于之后根据Job运行状态显示不同的运行状态(颜色变化)
            View showStartView = mSchedulerAcitvity.findViewById(R.id.onstart_textview);
            View showStopView = mSchedulerAcitvity.findViewById(R.id.onstop_textview);
 
            Message m;
            switch (msg.what) {
                 // 当作业登录到应用程序时,从服务接收回调。 打开指示灯(上方View闪烁)并发送一条消息,在一秒钟后将其关闭。
                 case MSG_JOB_START:
                    // Start received, turn on the indicator and show text.
                    // 开始接收,打开指示灯(上方View闪烁)并显示文字。
                    showStartView.setBackgroundColor(getColor(R.color.start_received));
                    updateParamsTextView(msg.obj, "started");
 
                    // Send message to turn it off after a second.
                    // 发送消息,一秒钟后关闭它。
                    m = Message.obtain(this, MSG_ONJOB_START);
                    sendMessageDelayed(m, 1000L);
                    break;
 
                // 当先前执行在应用程序中的作业必须停止执行时,
                // 从服务接收回调。 打开指示灯并发送一条消息,
                // 在两秒钟后将其关闭。
                case MSG_JOB_STOP:
                    // Stop received, turn on the indicator and show text.
                    // 停止接收,打开指示灯并显示文本。
                    showStopView.setBackgroundColor(getColor(R.color.stop_received));
                    updateParamsTextView(msg.obj, "stopped");
 
                    // Send message to turn it off after a second.
                    // 发送消息,一秒钟后关闭它。
                    m = obtainMessage(MSG_ONJOB_STOP);
                    sendMessageDelayed(m, 2000L);
                    break;
                case MSG_ONJOB_START:
                    showStartView.setBackgroundColor(getColor(R.color.none_received));
                    updateParamsTextView(null, "job had started");
                    break;
                case MSG_ONJOB_STOP:
                    showStopView.setBackgroundColor(getColor(R.color.none_received));
                    updateParamsTextView(null, "job had stoped");
                    break;
            }
        } 
 
        // 更新UI显示
        // @param jobId jobId
        // @param action 消息
        private void updateParamsTextView(@Nullable Object jobId, String action) {
            TextView paramsTextView = (TextView) mActivity.get().findViewById(R.id.task_params);
            if (jobId == null) {
                paramsTextView.setText("");
                return;
            }
            String jobIdText = String.valueOf(jobId);
            paramsTextView.setText(String.format("Job ID %s %s", jobIdText, action));
        }
 
        private int getColor(@ColorRes int color) {
            return mActivity.get().getResources().getColor(color);
        }
    }
}  

⑤Jobservice

public class MyJobService extends JobService {
 
    private static final String TAG = MyJobService.class.getSimpleName();
 
    private Messenger mActivityMessenger;
 
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "Service created");
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "Service destroyed");
    }
 
    // 当应用程序的MainActivity被创建时,它启动这个服务。
    // 这是为了使活动和此服务可以来回通信。 请参见“setUiCallback()”
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        mActivityMessenger = intent.getParcelableExtra(MESSENGER_INTENT_KEY);
        return START_NOT_STICKY;
    }
 
    @Override
    public boolean onStartJob(final JobParameters params) {
        // The work that this service "does" is simply wait for a certain duration and finish
        // the job (on another thread).
 
        // 该服务做的工作只是等待一定的持续时间并完成作业(在另一个线程上)。
        sendMessage(MSG_JOB_START, params.getJobId());
        // 当然这里可以处理其他的一些任务
        // TODO something else
        
        // 获取在activity里边设置的每个任务的周期,其实可以使用setPeriodic()
        long duration = params.getExtras().getLong(WORK_DURATION_KEY);
 
        // 使用一个handler处理程序来延迟jobFinished()的执行。
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                sendMessage(MSG_JOB_STOP, params.getJobId());
                jobFinished(params, false);
            }
        }, duration);
        Log.i(TAG, "on start job: " + params.getJobId());
 
        // 返回true,很多工作都会执行这个地方,我们手动结束这个任务
        return true;
    }
 
    @Override
    public boolean onStopJob(JobParameters params) {
        // 停止跟踪这些作业参数,因为我们已经完成工作。
        sendMessage(MSG_JOB_STOP, params.getJobId());
        Log.i(TAG, "on stop job: " + params.getJobId());
 
        // 返回false来销毁这个工作
        return false;
    }
 
    private void sendMessage(int messageID, @Nullable Object params) {
        // 如果此服务由JobScheduler启动,则没有回调Messenger。
        // 它仅在MainActivity在Intent中使用回调函数调用startService()时存在。
        if (mActivityMessenger == null) {
            Log.d(TAG, "Service is bound, not started. There's no callback to send a message to.");
            return;
        }
 
        Message m = Message.obtain();
        m.what = messageID;
        m.obj = params;
        try {
            mActivityMessenger.send(m);
        } catch (RemoteException e) {
            Log.e(TAG, "Error passing service object back to activity.");
        }
    }
}

参考文献:
https://blog.csdn.net/pkorochi/article/details/86542229

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值