簡單學習
最新庫的版本以及更新日志
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<MyWoker>()
.setConstraints(constraints)
.setInitialDelay(10,TimeUnit.SECONDS)
.setInputData(input)
.build()
request2= PeriodicWorkRequestBuilder<MyWoker>(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的設備,這個字段無效
* <p><pre>
* [ 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<OneTimeWorkRequest> 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<OneTimeWorkRequest> work)
public abstract @NonNull Operation enqueue();
上邊的這些還有另外一種UniqueWork
顧名思義,就是對于uniqueWorkName一樣的任務只能有一個,那已經有咋辦,由第二個參數來決定
public abstract @NonNull WorkContinuation beginUniqueWork(
@NonNull String uniqueWorkName,
@NonNull ExistingWorkPolicy existingWorkPolicy,
@NonNull List<OneTimeWorkRequest> 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<CompressWorker>().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
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
然后在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@app@com.charliesong.demo0327-1@split_lib_dependencies_apk.apk@classes.dex) 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<Worker2>().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<WorkStatus>{
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: <init> [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"
<uses-permission android:name="android.permission.FOCE_STOP_PACKAGES" />
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)