使用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>