android: job service

使用jobService遇到一些坑,这里记录下:

Job在android中的地位还是很重要的,代码位于android/app/job 和activity、service并列,(不知道另外两个broadcast和provide放在哪里?)

public abstract class JobService extends Service
 

public abstract class JobService extends Service {
    public static final String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE";
    private JobServiceEngine mEngine;

    /** @hide */
    public final IBinder onBind(Intent intent) {
        if (mEngine == null) {
            mEngine = new JobServiceEngine(this) {
                @Override
                public boolean onStartJob(JobParameters params) {
                    return JobService.this.onStartJob(params);
                }

                @Override
                public boolean onStopJob(JobParameters params) {
                    return JobService.this.onStopJob(params);
                }
            };
        }
        return mEngine.getBinder();
    }
    public final void jobFinished(JobParameters params, boolean wantsReschedule) {
        mEngine.jobFinished(params, wantsReschedule);
    }

    public abstract boolean onStartJob(JobParameters params);
    public abstract boolean onStopJob(JobParameters params);

}

JobService是JobScheduler callback的入口点

/**
 * JobService 是 Entry point for the callback from the android.app.job.JobScheduler.
 * This is the base class that handles asynchronous requests that were previously scheduled

  JobService是个abstract class,用户需要继承且实现onStartJob、onStopJob
 * You are responsible for overriding {JobService#onStartJob(JobParameters)}, which is where
 * you will implement your job logic.

 // 为防止阻塞主线程,把执行逻辑放在另外的线程
 * This service executes each incoming job on a {android.os.Handler} running on your
 * application's main thread. This means that you must offload your execution logic to
 * another thread/handler/{android.os.AsyncTask} of your choosing. Not doing so will result
 * in blocking any future callbacks from the JobManager - specifically
 * {onStopJob(android.app.job.JobParameters)}, which is meant to inform you that the
 * scheduling requirements are no longer being met.
 */

onStartJob

public abstract boolean onStartJob(JobParameters params);

添加的具体逻辑在这里执行;

return true表示job还要执行直到调用jobFinished或者执行条件不满足如和配置的网络类型等

return false表示job执行完毕
/**
* Called to indicate that the job has begun executing.  Override this method with the
* logic for your job.  Like all other component lifecycle callbacks, this method executes
* on your application's main thread.

* Return {true} from this method if your job needs to continue running.  If you
* do this, the job remains active until you call
* {jobFinished(JobParameters, boolean)} to tell the system that it has completed
* its work, or until the job's required constraints are no longer satisfied.
 For
* example, if the job was scheduled using
* {JobInfo.Builder#setRequiresCharging(boolean) setRequiresCharging(true)},
* it will be immediately halted by the system if the user unplugs the device from power,
* the job's {onStopJob(JobParameters)} callback will be invoked, and the app
* will be expected to shut down all ongoing work connected with that job.

* The system holds a wakelock on behalf of your app as long as your job is executing.
* This wakelock is acquired before this method is invoked, and is not released until either
* you call {jobFinished(JobParameters, boolean)}, or after the system invokes
* { #onStopJob(JobParameters)} to notify your job that it is being shut down
* prematurely.

* Returning {false} from this method means your job is already finished.  The
* system's wakelock for the job will be released, and {onStopJob(JobParameters)}
* will not be invoked.
*
*/

onStopJob

当job获得执行时间但条件不满足时调用该函数

/**
* This method is called if the system has determined that you must stop execution of your job
* even before you've had a chance to call {jobFinished(JobParameters, boolean)}.
*
* This will happen if the requirements specified at schedule time are no longer met. For
* example you may have requested WiFi with
* { android.app.job.JobInfo.Builder#setRequiredNetworkType(int)}, yet while your
* job was executing the user toggled WiFi. Another example is if you had specified
* {android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
* idle maintenance window. You are solely responsible for the behavior of your application
* upon receipt of this message; your app will likely start to misbehave if you ignore it.
* Once this method returns, the system releases the wakelock that it is holding on
* behalf of the job.
*/
public abstract boolean onStopJob(JobParameters params);

jobFinished

/**
* Call this to inform the JobScheduler that the job has finished its work.  When the
* system receives this message, it releases the wakelock being held for the job.
* You can request that the job be scheduled again by passing {true} as
* the wantsReschedule parameter.
*/
public final void jobFinished(JobParameters params, boolean wantsReschedule) {
                mEngine.jobFinished(params, wantsReschedule);
}

JobParameters

JobParameters包含标示任务的参数,系统处理该参数
/**
 * Contains the parameters used to configure/identify your job. You do not create this object
 * yourself, instead it is handed in to your application by the System.

 */
public class JobParameters implements Parcelable {
    private final int jobId;
}

JobInfo

/**
 * Container of data passed to the {android.app.job.JobScheduler} fully encapsulating the
 * parameters required to schedule work against the calling application. These are constructed
 * using the {JobInfo.Builder}.
 * You must specify at least one sort of constraint on the JobInfo object that you are creating.
 * The goal here is to provide the scheduler with high-level semantics about the work you want to
 * accomplish. Doing otherwise with throw an exception in your app.
 */
public class JobInfo implements Parcelable {}

JobInfo.Builder builder = new JobInfo.Builder(JOB_ID,new ComponentName(packageName, className));
builder.setPeriodic(5000);
builder.setPersisted(true);
builder.setRequiresCharging(true);

Builder setPeriodic(long intervalMillis)
Builder setPeriodic(long intervalMillis, long flexMillis)
Builder setMinimumLatency(long minLatencyMillis)
Builder setOverrideDeadline(long maxExecutionDelayMillis)
/*Set whether or not to persist this job across device reboots*/
Builder setPersisted(boolean isPersisted)
Builder setRequiredNetworkType(@NetworkType int networkType)
Builder setRequiresBatteryNotLow(boolean batteryNotLow)
Builder setRequiresCharging(boolean requiresCharging)
Builder setRequiresDeviceIdle(boolean requiresDeviceIdle)

//生成Info
JobInfo info = builder.build();

JobService的使用

public class CloudUpdateJobService extends JobService {
    private static String TAG = "CloudUpdate";
1. override onStartJob and onStopJob
    @Override
    public boolean onStartJob(final JobParameters params) {
        Log.i(TAG, "job is scheduled");
        2. 用户逻辑在一个单独的thread
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "running in defined job");
                3. 完成后显示jobFinished
                jobFinished(params, false);
            }
        }).start();
        return true;
    }

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

    public static boolean scheduleJob(Context context, long delay){
        Log.i(TAG, "call into scheduleJob");
        4. 获得系统服务:jobscheduler
        JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
        5. 为什么要取消?防止多个,每次提交job前都要取消相同的job
        jobScheduler.cancel(17493201);
        ComponentName cn = new ComponentName(context.getPackageName(), CloudUpdateJobService.class.getName());
        Log.i(TAG, "package name: " + context.getPackageName());
        Log.i(TAG, "class name: by class.getName:   " + CloudUpdateJobService.class.getName());
        Log.i(TAG, "class name: by class:           " + CloudUpdateJobService.class);
        6.创建JobInfo.Builder,参数是job_id和 ComponentName[通过ComponentName和用户实现的JobService关联]
        7.向JobInfo.Builder赋值
        8.由JobInfo.Builder创建JobInfo
        JobInfo jobInfo = new JobInfo.Builder(17493201, cn)
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
                .setPeriodic(10000, 5000)
                .build();
        Log.i(TAG, "after build JobInfo then submit");
        9.调用系统服务jobScheduler的接口schedule
        if (jobScheduler.schedule(jobInfo) <= JobScheduler.RESULT_FAILURE) {
            Log.i(TAG, "scheduleJob 17493201 failed");
            return false;
        }
        return true;
    }
}

使用中遇到的问题

Builder setPeriodic(5000)使用5S周期方式,一直没看到输出,不知道哪里出错了

1.是ComponentName错了吗?和Class name有什么区别?

/**
 * Identifier for a specific application component
 * ({@link android.app.Activity}, {@link android.app.Service},
 * {@link android.content.BroadcastReceiver}, or
 * {@link android.content.ContentProvider}) that is available.  Two
 * pieces of information, are required to identify
 * a component: the package (a String) it exists in, and the class (a String)
 * name inside of that package.
 *
 */

new ComponentName(context.getPackageName(), CloudUpdateJobService.class.getName());

new ComponentName(context, CloudUpdateJobService.class);

从打印的log看两种方法都没有问题,但奇怪的是两个构造函数中表示className的方式不同。

2. dumpsys jobscheduler看下job是否在?

首先明确jobscheduler是个系统服务

service list | grep jobscheduler
jobscheduler: [android.app.job.IJobScheduler]

3. dumpsys jobscheduler | grep android_learning                                                                                                                                                   
 看对应的jobcom.xiaomi.aiasst.android_learning/.CloudUpdateJobServices是否执行 : 是否有START-P

JOB #u0a214/17493201: 7ad602a com.xiaomi.aiasst.android_learning/.CloudUpdateJobService
    u0a214 tag=*job*/com.xiaomi.aiasst.android_learning/.CloudUpdateJobService
    Source: uid=u0a214 user=0 pkg=com.xiaomi.aiasst.android_learning
      Service: com.xiaomi.aiasst.android_learning/.CloudUpdateJobService
  #u0a214/17493201 from u0a214 idle: com.xiaomi.aiasst.android_learning [RUN_ANY_IN_BACKGROUND allowed] RUNNABLE
  #u0a214/17493201 from u0a214: com.xiaomi.aiasst.android_learning RUNNABLE
     -6h23m44s681ms START-P: #u0a212/17493201 com.xiaomi.aiasst.android_learning/.CloudUpdateJobService
     -6h23m44s631ms    STOP: #u0a212/17493201 com.xiaomi.aiasst.android_learning/.CloudUpdateJobService app called jobFinished
    

怎样合理配置jobInfo

setPeriodic(long intervalMillis) -> setPeriodic(long intervalMillis, long flexMillis) 问题有所改善,因为两个参数的函数有个执行的大概限制,而一个参数的函数没有任何限制,不知道合适执行完全有系统决定

 setMinimumLatency(60 * 1000) // 任务最少延迟时间

 //如果设置了Deadline就能log输出,调试时可以这么用
 setOverrideDeadline(120 * 1000) // 任务deadline,当到期没达到指定条件也会开始执行

另外,周期型job, 大多也没有用period的方式,一般是执行单个job后在schedule一次。
 

有关配置文件AndroidManifest.xml

要加上 android:permission="android.permission.BIND_JOB_SERVICE" >
<service
    android:name=".CloudUpdateJobService"
    android:permission="android.permission.BIND_JOB_SERVICE" >
</service>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值