简单学习
最新库的版本以及更新日志
https://developer.android.google.cn/jetpack/androidx/releases/work?hl=zh-cn
首先导入库
下边的库看自己需求添加,并非所有的都需要,比如java和kotlin两种
dependencies {
def work_version = "2.2.0"
// (Java only)
implementation "androidx.work:work-runtime:$work_version"
// Kotlin + coroutines
implementation "androidx.work:work-runtime-ktx:$work_version"
// optional - RxJava2 support
implementation "androidx.work:work-rxjava2:$work_version"
// optional - GCMNetworkManager support
implementation "androidx.work:work-gcm:$work_version"
// optional - Test helpers
androidTestImplementation "androidx.work:work-testing:$work_version"
}
创建任务
继承Worker类,实现doWork方法,doWork() 方法在 WorkManager 提供的后台线程上同步运行
public class UploadWorker extends Worker {
public UploadWorker(
@NonNull Context context,
@NonNull WorkerParameters params) {
super(context, params);
}
@Override
public Result doWork() {
// Do the work here--in this case, upload the images.
uploadImages()
// Indicate whether the task finished successfully with the Result
return Result.success()
}
}
已成功完成:Result.success()
已失败:Result.failure()
需要稍后重试:Result.retry()
成功和失败,还可以传个Data数据,给下一个worker或者自己接收
val data = Data.Builder().apply {
//可以put一些基础数据类型
putString("key", "from My Worker");
}.build()
简单介绍下几种任务
- Worker
public abstract class Worker extends ListenableWorker
- CoroutineWorker [kotlin下用的]
abstract class CoroutineWorker(
appContext: Context,
params: WorkerParameters
) : ListenableWorker(appContext, params)
配置运行任务的方式和时间
worker弄好了,我们还要确定任务执行的方式,是一次性的,还是重复性的,系统也提供了对应的类
OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadWorker.class)
.build()
代码简单的写了这两种任务的实例
//任务运行的一些条件设置
var constraints=Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(false)
.setRequiresCharging(false)
.setRequiresDeviceIdle(false)
.setTriggerContentMaxDelay(10,TimeUnit.MINUTES)
.setTriggerContentUpdateDelay(10,TimeUnit.SECONDS)
.build()
//这个数据在Worker里可以通过getInputData()方法拿到
var input=Data.Builder()
.putString("url","www.xxx.xxx")
.putInt("retry",3)
.build()
var request= OneTimeWorkRequest.Builder(MyWoker::class.java)
.addTag("xxx")
.setConstraints(constraints)
.setInitialDelay(10,TimeUnit.SECONDS)
.setInputData(input)
.build()
var request2=PeriodicWorkRequest.Builder(MyWoker::class.java, 10,TimeUnit.MINUTES)
.setConstraints(constraints)
.setInitialDelay(10,TimeUnit.SECONDS)
.setInputData(input)
.build()
kotlin版本的
request= OneTimeWorkRequestBuilder()
.setConstraints(constraints)
.setInitialDelay(10,TimeUnit.SECONDS)
.setInputData(input)
.build()
request2= PeriodicWorkRequestBuilder(10,TimeUnit.MINUTES)
.setConstraints(constraints)
.setInitialDelay(10,TimeUnit.SECONDS)
.setInputData(input)
.build()
其他:
PeriodicWorkRequest的Builder还有一种构造方法,可以传入flex时间,不传的默认和repeat interval时间一样
public Builder(
@NonNull Class extends ListenableWorker> workerClass,
long repeatInterval,
@NonNull TimeUnit repeatIntervalTimeUnit,
long flexInterval,
@NonNull TimeUnit flexIntervalTimeUnit)
这个时间有啥用,看下文档
就是说你repeat时间是15分钟,flex时间是5分钟,那么其实就相当于每次延迟5分钟执行任务,当然不是5分钟以后立马执行,worker这东西时间不是确定的,它是在这个时间段执行
Note that flex intervals are ignored for certain OS versions (in particular, API 23).
//23的设备,这个字段无效
*
* [ before flex | flex ][ before flex | flex ]...
* [ cannot run work | can run work ][ cannot run work | can run work ]...
* \____________________________________/\____________________________________/...
* interval 1 interval 2 ...(repeat)
间隔任务时间是有限制的,你设置比这个小没用,它会自动改成这个最小值
/**
* The minimum interval duration for {@link PeriodicWorkRequest} (in milliseconds).
*/
public static final long MIN_PERIODIC_INTERVAL_MILLIS = 15 * 60 * 1000L; // 15 minutes.
/**
* The minimum flex duration for {@link PeriodicWorkRequest} (in milliseconds).
*/
public static final long MIN_PERIODIC_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes.
开始执行任务
上边request也有了,worker也有了,那么现在让request开始处理worker吧
首先获取WorkManager的实例,以前没有context参数,还得手动init,现在不用了,传个context,会自己判断有没有init来生成对应的实例
WorkManager.getInstance(applicationContext)
执行,有beginWith,then,enqueue操作
WorkManager.getInstance(applicationContext).beginWith(request).then(request3).then(request).enqueue()
WorkManager.getInstance(applicationContext).enqueue(arrayListOf(request,request3,request2))
beginWith,then只能执行一次性的任务
enqueue啥任务都行
参数可以是单个任务,也可以是多个任务【集合】
public final @NonNull WorkContinuation beginWith(@NonNull OneTimeWorkRequest work)
public abstract @NonNull WorkContinuation beginWith(@NonNull List work)
public final Operation enqueue(@NonNull WorkRequest workRequest)
public abstract Operation enqueue(@NonNull List extends WorkRequest> requests)
public final @NonNull WorkContinuation then(@NonNull OneTimeWorkRequest work)
public abstract @NonNull WorkContinuation then(@NonNull List work)
public abstract @NonNull Operation enqueue();
上边的这些还有另外一种UniqueWork
顾名思义,就是对于uniqueWorkName一样的任务只能有一个,那已经有咋办,由第二个参数来决定
public abstract @NonNull WorkContinuation beginUniqueWork(
@NonNull String uniqueWorkName,
@NonNull ExistingWorkPolicy existingWorkPolicy,
@NonNull List work)
3个看名字大概就知道啥意思了,如果没有存在的同名任务,那么添加一个新的,这时候这个参数无意义,大家都一样
replace:有老的,把老的取消删除
keep:有老的,就继续老的,新的其实就等于没用到
append:就是把现有的任务 添加到老的任务下边,您不能将 APPEND 与 PeriodicWorkRequest 一起使用。
public enum ExistingWorkPolicy {
/**
* If there is existing pending (uncompleted) work with the same unique name, cancel and delete
* it. Then, insert the newly-specified work.
*/
REPLACE,
/**
* If there is existing pending (uncompleted) work with the same unique name, do nothing.
* Otherwise, insert the newly-specified work.
*/
KEEP,
/**
* If there is existing pending (uncompleted) work with the same unique name, append the
* newly-specified work as a child of all the leaves of that work sequence. Otherwise, insert
* the newly-specified work as the start of a new sequence.
*/
APPEND
}
取消任务
tag 可以在workerRequest里设置,上边有
uuid 可以通过workerRequest ,getId拿到
至于uniqueWork对应上边的uniqueWork
查看任务的状态
下边是方法,先拿到你要找的任务
WorkManager.getInstance(applicationContext).getWorkInfosByTag("xxx").get()[0].state
public enum State {
/**
* Used to indicate that the {@link WorkRequest} is enqueued and eligible to run when its
* {@link Constraints} are met and resources are available.
*/
ENQUEUED,
/**
* Used to indicate that the {@link WorkRequest} is currently being executed.
*/
RUNNING,
/**
* Used to indicate that the {@link WorkRequest} has completed in a successful state. Note
* that {@link PeriodicWorkRequest}s will never enter this state (they will simply go back
* to {@link #ENQUEUED} and be eligible to run again).
*/
SUCCEEDED,
/**
* Used to indicate that the {@link WorkRequest} has completed in a failure state. All
* dependent work will also be marked as {@code #FAILED} and will never run.
*/
FAILED,
/**
* Used to indicate that the {@link WorkRequest} is currently blocked because its
* prerequisites haven't finished successfully.
*/
BLOCKED,
/**
* Used to indicate that the {@link WorkRequest} has been cancelled and will not execute.
* All dependent work will also be marked as {@code #CANCELLED} and will not run.
*/
CANCELLED;
/**
* Returns {@code true} if this State is considered finished.
*
* @return {@code true} for {@link #SUCCEEDED}, {@link #FAILED}, and * {@link #CANCELLED}
* states
*/
public boolean isFinished() {
return (this == SUCCEEDED || this == FAILED || this == CANCELLED);
}
}
删除完成的任务
包括,success,failed,cancelled
-
- Is finished (succeeded, failed, or cancelled)
-
- Has zero unfinished dependents
这些任务其实是写在用room库处理的数据库里的,就算worker执行完,任务信息也不会立马删除,还是在数据库了,只是状态变了而已,当然系统过一段时间会清理掉这些完成的任务的,具体多久不清楚
我们也可以手动删除已完成的任务,
workManager.pruneWork()
点击参考 源码研究
worker state 查看
进度设置,WorkManager 2.3.0-alpha01以上才有的
链接工作
比如,先有3个任务filter1,2,3,都一起做完了以后把结果告诉下个工作compress,现在看下compress咋获取到数据
WorkManager.getInstance(myContext)
// Candidates to run in parallel
.beginWith(Arrays.asList(filter1, filter2, filter3))
// Dependent work (only runs after all previous work in chain)
.then(compress)
.then(upload)
// Don't forget to enqueue()
.enqueue();
首先我们知道一个worker做完,可以在结果了插入数据
val output=Data.Builder().putString("path","out put data").build()
return Result.success(output)
如果filter1,2,3是同一个worker,output里的key都一样,那咋拿到这些value?
这里可以用到InputMerger,对output里的数据进行处理
WorkManager 提供两种不同类型的 InputMerger
:
-
OverwritingInputMerger
会尝试将所有输入中的所有键添加到输出中。如果发生冲突,它会覆盖先前设置的键。
举个例子:filter 返回的key都是path,那么compress里最后收到的只有一个path,就是最后执行的那个filter传出来的 -
ArrayCreatingInputMerger
会尝试合并输入,并在必要时创建数组
这种把value封装成一个集合了,可以去看下源码,比较简单,比如你是string,设置这个以后,在compress任务里拿到的value就是个stringArray了
OneTimeWorkRequestBuilder().setInputMerger(ArrayCreatingInputMerger::class.java).build()
//设置了inputMerger为ArrayCreatingInputMerger以后,数据就封装成集合了
val input=inputData;
val path=input.getStringArray("path")
默认不设置的话,就是OverwritingInputMerger,也就是key一样的,后边的数据会覆盖前边的
创建 OneTimeWorkRequest 链时,需要注意以下几点:
从属 OneTimeWorkRequest 仅在其所有父级 OneTimeWorkRequest 都成功完成(即返回 Result.success())时才会被解除阻塞(变为 ENQUEUED 状态)。
如果有任何父级 OneTimeWorkRequest 失败(返回 Result.failure()),则所有从属 OneTimeWorkRequest 也会被标记为 FAILED。
如果有任何父级 OneTimeWorkRequest 被取消,则所有从属 OneTimeWorkRequest 也会被标记为 CANCELLED
自定义 WorkManager 配置和初始化
前边讲过,新版库默认init了workerManager
public class WorkManagerInitializer extends ContentProvider {
@Override
public boolean onCreate() {
// Initialize WorkManager with the default configuration.
WorkManager.initialize(getContext(), new Configuration.Builder().build());
return true;
}
如果不满意,可以删掉,然后自己重写
先删掉这个ContentProvider
然后在application里继承Configuration.Provider,写自己的Configuration
class MyApplication extends Application implements Configuration.Provider {
@Override
public Configuration getWorkManagerConfiguration() {
return Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.build();
}
}
//下边是Configuration的属性,都可以通过builder里修改
public static final class Builder {
Executor mExecutor;
WorkerFactory mWorkerFactory;
Executor mTaskExecutor;
int mLoggingLevel = Log.INFO;
int mMinJobSchedulerId = IdGenerator.INITIAL_ID;
int mMaxJobSchedulerId = Integer.MAX_VALUE;
int mMaxSchedulerLimit = MIN_SCHEDULER_LIMIT;
然后看下下图,我们取消自动初始化以后,在application里实现Provider,这里就用到了
补充
- 如何停止worker
,比如下边你在执行一个循环任务的时候,可以加个条件,判断是否isStopped来决定任务是否继续下去
public Result doWork() {
for (int i = 0; i < 100; ++i) {
if (isStopped()) {
break;
}
try {
downloadSynchronously("https://www.google.com");
} catch (IOException e) {
return Result.failure();
}
}
return Result.success();
}
- kotlin下的CoroutineWorker 取消任务
CoroutineWorker 通过取消协程并传播取消信号来自动处理停工情况。您无需执行任何特殊操作来处理停工任务
class CoroutineDownloadWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
override val coroutineContext = Dispatchers.IO
override suspend fun doWork(): Result = coroutineScope {
val jobs = (0 until 100).map {
async {
downloadSynchronously("https://www.google.com")
}
}
// awaitAll will throw an exception if a download fails, which CoroutineWorker will treat as a failure
jobs.awaitAll()
Result.success()
}
}
- Constraints.Builder().setRequiresCharging(false) 这个boolean值的作用
,如果设置为true,那么必须插上电源这个任务才会运行。 这里插上电源,我试了下,必须是交流电。用数据线连接到电脑上的不算。
begin(a).then(b)的效果,a执行完,并且dowork()返回WorkerResult.SUCCESS ,b才会执行。
如果a返回了FAILED,那么b不会执行的,如果a返回retry,那么a就会等待时机再次尝试,直到返回成功,b才会执行。
测试中发现。点开start以后,我就打开应用程序,点了强制停止,然后发现过了一会它又成了运行状态了【强制停止不是灰色的,又可以点击了】
而且studio的日志里看到下边的东西
22:44:17.247 : Failed execv(/system/bin/dex2oat --runtime-arg -classpath --runtime-arg --debuggable --instruction-set=arm --instruction-set-features=smp,-div,-atomic_ldrd_strd --runtime-arg -Xrelocate --boot-image=/system/framework/boot.art --runtime-arg -Xms64m --runtime-arg -Xmx512m --instruction-set-variant=cortex-a53 --instruction-set-features=default --dex-file=/data/app/com.charliesong.demo0327-1/split_lib_dependencies_apk.apk --oat-file=/data/dalvik-cache/arm/data@[email protected]@[email protected]) because non-0 exit status
22:44:18.887 D/PackageManagerHelper: androidx.work.impl.background.systemjob.SystemJobService enabled
22:44:18.887 D/Schedulers: Created SystemJobScheduler and enabled SystemJobService
22:44:18.897 D/PackageManagerHelper: androidx.work.impl.background.firebase.FirebaseJobService could not be disabled
java.lang.IllegalArgumentException: Component class androidx.work.impl.background.firebase.FirebaseJobService does not exist in com.charliesong.demo0327
22:44:18.897 D/PackageManagerHelper: androidx.work.impl.background.systemalarm.SystemAlarmService disabled
22:44:18.907 I/InstantRun: starting instant run server: is main process
22:44:18.907 D/ForceStopRunnable: Application was force-stopped, rescheduling.
22:44:18.987 D/SystemJobService: onStartJob for c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:18.987 D/SystemJobScheduler: Scheduling work ID c4d275cb-10af-4c08-bc9b-0985f196cbd9 Job ID 40
22:44:18.987 D/SystemJobService: Job is already being executed by SystemJobService: c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:18.997 D/SystemJobScheduler: Scheduling work ID c4d275cb-10af-4c08-bc9b-0985f196cbd9 Job ID 41
22:44:18.997 D/SystemJobScheduler: Scheduling work ID ce1865ad-3d30-4e0d-8474-f6d64307a973 Job ID 42
22:44:18.997 /SystemJobScheduler: Scheduling work ID ce1865ad-3d30-4e0d-8474-f6d64307a973 Job ID 43
22:44:18.997 D/Processor: Processor: processing c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:19.017 D/WorkerWrapper: Worker result SUCCESS for c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:19.027 D/Processor: Processor c4d275cb-10af-4c08-bc9b-0985f196cbd9 executed; isSuccessful = true, reschedule = false
22:44:19.027 D/SystemJobService: c4d275cb-10af-4c08-bc9b-0985f196cbd9 executed on JobScheduler
22:44:19.027 /SystemJobScheduler: Scheduling work ID ce1865ad-3d30-4e0d-8474-f6d64307a973 Job ID 44
22:44:19.037 /SystemJobScheduler: Scheduling work ID ce1865ad-3d30-4e0d-8474-f6d64307a973 Job ID 45
22:44:39.017 /SystemJobService: onStartJob for c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:39.017 /SystemJobService: Job is already being executed by SystemJobService: c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:39.017 /Processor: Processor: processing c4d275cb-10af-4c08-bc9b-0985f196cbd9
22:44:39.027 com.charliesong.demo0327 E/WorkerWrapper: Status for c4d275cb-10af-4c08-bc9b-0985f196cbd9 is SUCCEEDED; not doing any work
22:44:39.027 /Processor: Processor c4d275cb-10af-4c08-bc9b-0985f196cbd9 executed; isSuccessful = false, reschedule = false
22:44:39.027 /SystemJobService: c4d275cb-10af-4c08-bc9b-0985f196cbd9 executed on JobScheduler
22:44:48.087 /SystemJobService: onStartJob for ce1865ad-3d30-4e0d-8474-f6d64307a973
22:44:48.087 /Processor: Processor: processing ce1865ad-3d30-4e0d-8474-f6d64307a973
22:44:48.087 /SystemJobService: Job is already being executed by SystemJobService: ce1865ad-3d30-4e0d-8474-f6d64307a973
22:44:48.107 /WorkerWrapper: Worker result SUCCESS for ce1865ad-3d30-4e0d-8474-f6d64307a973
22:44:48.117 /WorkerWrapper: Setting status to enqueued for 95339398-f24f-439f-aab0-3dfbbe787d45
22:44:48.117 /Processor: Processor ce1865ad-3d30-4e0d-8474-f6d64307a973 executed; isSuccessful = true, reschedule = false
22:44:48.117 /SystemJobService: ce1865ad-3d30-4e0d-8474-f6d64307a973 executed on JobScheduler
22:44:48.127 /SystemJobScheduler: Scheduling work ID 95339398-f24f-439f-aab0-3dfbbe787d45 Job ID 46
22:44:48.127 /SystemJobScheduler: Scheduling work ID 95339398-f24f-439f-aab0-3dfbbe787d45 Job ID 47
22:45:09.017 /SystemJobService: onStartJob for ce1865ad-3d30-4e0d-8474-f6d64307a973
22:45:09.017 /Processor: Processor: processing ce1865ad-3d30-4e0d-8474-f6d64307a973
22:45:09.017 /SystemJobService: Job is already being executed by SystemJobService: ce1865ad-3d30-4e0d-8474-f6d64307a973
22:45:09.027 com.charliesong.demo0327 E/WorkerWrapper: Status for ce1865ad-3d30-4e0d-8474-f6d64307a973 is SUCCEEDED; not doing any work
22:45:09.027 /Processor: Processor ce1865ad-3d30-4e0d-8474-f6d64307a973 executed; isSuccessful = false, reschedule = false
22:45:09.027 /SystemJobService: ce1865ad-3d30-4e0d-8474-f6d64307a973 executed on JobScheduler
22:45:10.017 /SystemJobService: onStartJob for ce1865ad-3d30-4e0d-8474-f6d64307a973
22:45:10.017 /Processor: Processor: processing ce1865ad-3d30-4e0d-8474-f6d64307a973
22:45:10.017 /SystemJobService: Job is already being executed by SystemJobService: ce1865ad-3d30-4e0d-8474-f6d64307a973
22:45:10.027 com.charliesong.demo0327 E/WorkerWrapper: Status for ce1865ad-3d30-4e0d-8474-f6d64307a973 is SUCCEEDED; not doing any work
22:45:10.027 /Processor: Processor ce1865ad-3d30-4e0d-8474-f6d64307a973 executed; isSuccessful = false, reschedule = false
22:45:10.027 /SystemJobService: ce1865ad-3d30-4e0d-8474-f6d64307a973 executed on JobScheduler
22:45:58.907 /SystemJobService: onStartJob for 95339398-f24f-439f-aab0-3dfbbe787d45
22:45:58.907 /Processor: Processor: processing 95339398-f24f-439f-aab0-3dfbbe787d45
22:45:58.917 /SystemJobService: Job is already being executed by SystemJobService: 95339398-f24f-439f-aab0-3dfbbe787d45
22:45:58.927 /WorkerWrapper: Worker result SUCCESS for 95339398-f24f-439f-aab0-3dfbbe787d45
22:45:58.927 /Processor: Processor 95339398-f24f-439f-aab0-3dfbbe787d45 executed; isSuccessful = true, reschedule = false
22:45:58.927 /SystemJobService: 95339398-f24f-439f-aab0-3dfbbe787d45 executed on JobScheduler
另外当start执行了N次以后,可以看到get到的也是N次的信息,除非卸载重装,否则start了n次tag为xxx的work,到时候getbytag就能获取N次
06-10 22:53:50.047 com.charliesong.demo0327 I/System.out: 0==============2140871e-f14a-498e-bc41-e6728b91a7ae====SUCCEEDED//true==[work1]
06-10 22:53:50.047 com.charliesong.demo0327 I/System.out: 1==============85ed6feb-b5d2-4c5b-a3c6-c005ea60054e====SUCCEEDED//true==[work1]
06-10 22:53:50.047 com.charliesong.demo0327 I/System.out: 2==============87b3b5d9-000d-4ef2-ba7e-2c005fc29da8====SUCCEEDED//true==[work1]
06-10 22:53:50.047 com.charliesong.demo0327 I/System.out: 3==============c4d275cb-10af-4c08-bc9b-0985f196cbd9====SUCCEEDED//true==[work1]
不过有点奇怪的是,我start里就有3个任务,可每次都有4条日志,前2条的work ID是一样的,就是job ID 不一样,不懂。
06-10 22:59:49.737 xxx D/SystemJobScheduler: Scheduling work ID bbef65fb-aca0-4b8f-96a1-50da7234e5c8 Job ID 8
06-10 22:59:49.737 xxx D/SystemJobScheduler: Scheduling work ID bbef65fb-aca0-4b8f-96a1-50da7234e5c8 Job ID 9
06-10 22:59:49.737 xxxx D/SystemJobScheduler: Scheduling work ID cb3f7b83-9b8b-4e8d-aeb3-915d2498ff3f Job ID 10
06-10 22:59:49.737 xxx D/SystemJobScheduler: Scheduling work ID cb3f7b83-9b8b-4e8d-aeb3-915d2498ff3f Job ID 11
测试
卸载重装app,然后开启一个任务
val oneTimeRequest= OneTimeWorkRequestBuilder().setConstraints(constraints).setInputData(data)
.addTag("worker2")
.build()
WorkManager.getInstance().beginWith(oneTimeRequest).enqueue()
class Worker2:Worker() {
override fun doWork(): WorkerResult {
println("start ${javaClass.simpleName}==================")
Thread.sleep(3000)
println("end ${javaClass.simpleName}================")
val date=SimpleDateFormat("yyyyMMDD_HHmmss", Locale.getDefault()).format(Date())
UtilFile.saveStringToTempFile("$date ${javaClass.simpleName} ${inputData.getString("title","")} \r\n")
return WorkerResult.SUCCESS
}
}
日志
21:18:45.793 D/SystemJobScheduler: Scheduling work ID 17d724d0-757e-44e3-8814-1b7a933a3f46 Job ID 0
21:18:45.793 D/SystemJobScheduler: Scheduling work ID 17d724d0-757e-44e3-8814-1b7a933a3f46 Job ID 1
21:18:45.793 D/GreedyScheduler: Starting tracking for 17d724d0-757e-44e3-8814-1b7a933a3f46
D/ConstraintTracker: NetworkStateTracker: initial state = [ Connected=true Validated=true Metered=false NotRoaming=true ]
21:18:45.803 D/NetworkStateTracker: Registering broadcast receiver
21:18:45.803 D/WorkConstraintsTracker: Constraints met for 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:45.803 D/GreedyScheduler: Constraints met: Scheduling work ID 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:45.803 D/WorkConstraintsTracker: Constraints met for 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:45.803 D/GreedyScheduler: Constraints met: Scheduling work ID 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:45.813 D/Processor: Processor: processing 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:45.813 D/Processor: Work 17d724d0-757e-44e3-8814-1b7a933a3f46 is already enqueued for processing
I/System.out: start Worker2==================
21:18:45.873 D/ViewRootImpl: MSG_RESIZED_REPORT: ci=Rect(0, 24 - 0, 0) vi=Rect(0, 0 - 0, 0) or=2
21:18:45.883 D/NetworkStateTracker: Network broadcast received
21:18:45.923 I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy@caf4359 time:15813324
21:18:46.803 D/SystemJobService: onStartJob for 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:46.803 D/Processor: Work 17d724d0-757e-44e3-8814-1b7a933a3f46 is already enqueued for processing
21:18:46.803 D/SystemJobService: Job is already being executed by SystemJobService: 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:48.843 I/System.out: end Worker2================
21:18:48.843 D/WorkerWrapper: Worker result SUCCESS for 17d724d0-757e-44e3-8814-1b7a933a3f46
21:18:48.853 D/Processor: Processor 17d724d0-757e-44e3-8814-1b7a933a3f46 executed; isSuccessful = true, reschedule = false
21:18:48.853 D/SystemJobService: 17d724d0-757e-44e3-8814-1b7a933a3f46 executed on JobScheduler
间隔时间重复执行某个work
看文章发现有个PeriodicWorkRequest可以执行重复的任务,试了下结果发现没重复啊,后来点击源码才看到,这玩意默认的最小间隔是15分钟。
fun startRepeateWork(){
var request=PeriodicWorkRequest.Builder(MyWorker::class.java,1,TimeUnit.MINUTES).addTag("hello").build()
WorkManager.getInstance().enqueue(request)
UtilNormal.saveUUID(this,request.id.toString())
}
fun getRepeatWorkStatus(){
var str=UtilNormal.getUUID(this)
if(TextUtils.isEmpty(str)){
return
}
WorkManager.getInstance().getStatusById(UUID.fromString(str))?.apply {
this.removeObserver(observerOnly)
this.observeForever(observerOnly)
}
}
val observerOnly=object:Observer{
override fun onChanged(t: WorkStatus?) {
t?.apply {
println("onChanged==============${t.id}====${t.state}//${t.state.isFinished}==${t.tags}")
}
}
}
今天在一个新项目里添加这个room库的时候,编译的时候挂了,说有重复的库
Error: Program type already present: android.support.v4.app.INotificationSideChannel
解决办法
在gradle.properties文件下添加
android.useAndroidX=true
android.enableJetifier=true
然后点击下边按钮,迁移为androidX库,完事就没问题了。
然后写了个任务,等到执行的时候总是挂
public class AutoPlayWork extends Worker{
public AutoPlayWork(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
//xxxx
return Result.success();
}
异常如下
WM-WorkerFactory: Could not instantiate com.mitac.lockdown.util.ScheduleTaskManager$AutoPlayWork
java.lang.NoSuchMethodException: [class android.content.Context, class androidx.work.WorkerParameters]
at java.lang.Class.getConstructor(Class.java:528)
at java.lang.Class.getDeclaredConstructor(Class.java:507)
at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:91)
点进去看下,也就是newInstance通过构造方法实例化一个对象失败了,无语
try {
Constructor extends ListenableWorker> constructor =
clazz.getDeclaredConstructor(Context.class, WorkerParameters.class);
worker = constructor.newInstance(
appContext,
workerParameters);
return worker;
} catch (Exception e) {
Logger.get().error(TAG, "Could not instantiate " + workerClassName, e);
}
解决办法:
首先出错原因,这个work类我是写在工具里里的,也即是说是个内部类,实例化的时候需要外部类的对象才行的,这个肯定没有的啊。
所以把这个类设置成静态的,或者把这个类拉到外边单独写
强制关闭前台其他应用
需要系统权限,我们的app就是系统级应用,所以可用,否则就需要root了,然后执行adb命令,这个网上有
//需要权限:
android:sharedUserId="android.uid.system"
ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
activityManager.forceStopPackage("com.android.scan");
注意
对于这种重复的任务,这种任务你如果不手动取消的话,那么任务会一直重复下去,即使app被杀死,下次启动任务还在的。
你如果每次打开app都执行下边的代码,那么你的app里就会有多个这样的重复任务了。
很明显不合理,所以
你需要每次执行前,先cancel掉旧的worker,
或者这个执行完做个本地标记,下次执行前先判断下有没有执行过
var request=PeriodicWorkRequest.Builder(MyWorker::class.java,1,TimeUnit.MINUTES).addTag("hello").build()
WorkManager.getInstance().enqueue(request)
可以通过worker的tag来找到对应的任务取消
getWorkInfosByTag(MyWorker::class.java.name)
WorkManager.getInstance(this).cancelAllWorkByTag(MyWorker::class.java.name)