WorkManager是爲了那些可延後執行的任務而設計,這些任務不須要當即執行,可是須要保證任務能被執行,即便應用退出或者設備重啓。例如:java
WorkManager不是爲某些進程內的後臺任務設計的,這些任務會在app進程退出時被中止,也不是那些須要當即執行的任務。android
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()數據庫
public class UploadWorker extends Worker {
public UploadWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
//business logic
return Result.success();
}
}
複製代碼
Result返回結果有三種:api
Worker定義了具體的任務,WorkRequest定義瞭如何執行以及什麼時候執行任務。若是是一次性的任務,能夠用O呢TimeWorkRequest,若是是週期性的任務,可使用PeriodicWorkRequest。數組
OneTimeWorkRequest uploadReq = new OneTimeWorkRequest.Builder(UploadWorker.class).build();
複製代碼
PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(UploadWorker.class, 10, TimeUnit.SECONDS).build();
複製代碼
調用WorkManager的enqueue方法bash
WorkManager.getInstance(ctx).enqueue(uploadReq);
複製代碼
任務的具體執行時機依賴於WorkRequest設置的約束,以及系統的優化。服務器
經過自定義WorkRequest能夠解決如下場景:網絡
給任務增長約束,表示何時該任務能執行。多線程
例如,能夠指定任務只有在設備空閒或者鏈接到電源時才能執行。app
Constraints constraints = new Constraints.Builder()
//當本地的contenturi更新時,會觸發任務執行(api需大於等於24,配合JobSchedule)
.addContentUriTrigger(Uri.EMPTY, true)
//當content uri變動時,執行任務的最大延遲,配合JobSchedule
.setTriggerContentMaxDelay(10, TimeUnit.SECONDS)
//當content uri更新時,執行任務的延遲(api>=26)
.setTriggerContentUpdateDelay(100, TimeUnit.SECONDS)
//任務的網絡狀態:無網絡要求,有網絡鏈接,不限量網絡,非移動網絡,按流量計費的網絡
.setRequiredNetworkType(NetworkType.NOT_ROAMING)
//電量足夠才能執行
.setRequiresBatteryNotLow(true)
//充電時才能執行
.setRequiresCharging(false)
//存儲空間足夠才能執行
.setRequiresStorageNotLow(false)
//設備空閒才能執行
.setRequiresDeviceIdle(true)
.build();
複製代碼
當設置了多個約束,只有這些條件都知足時,任務纔會執行。
當任務在運行時,若是約束條件不知足,WorkManager會終止任務。這些任務會在下一次約束條件知足時重試。
若是任務沒有約束或者約束條件知足時,系統可能會馬上執行這些任務。若是不但願任務當即執行,能夠指定這些任務延遲必定時間再執行。
OneTimeWorkRequest uploadReq = new OneTimeWorkRequest.Builder(UploadWorker.class).setInitialDelay(10, TimeUnit.SECONDS).build();
複製代碼
若是須要WorkManager重試任務,可讓任務返回Result.retry()。
任務會被從新調度,而且會有一個默認的補償延遲和策略。補償延遲指定了任務被重試的一個最小的等待時間。補充策略定義了補償延遲在接下來的幾回重試中會如何增長。默認是指數增長的。
OneTimeWorkRequest uploadReq = new OneTimeWorkRequest.Builder(UploadWorker.class).setInitialDelay(10, TimeUnit.SECONDS)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10 ,TimeUnit.SECONDS)
.build();
複製代碼
任務可能須要傳入數據做爲輸入參數或者返回結果數據。例如,一個上傳圖片的任務須要圖片的URI,可能也須要圖片上傳後的地址。
輸入和輸出的值以鍵-值對的形式存儲在Data對象中。
Data data = new Data.Builder().putString("key1", "a").build();
OneTimeWorkRequest uploadReq = new OneTimeWorkRequest.Builder(UploadWorker.class)
.setInputData(data)
.build();
複製代碼
Wroker類調用Worker.getInputData()來獲取輸入參數。
Data類也能夠做爲輸出。在Worker中返回Data對象,經過調用Result.success(data)或Result.failure(data)。
public class UploadWorker extends Worker {
public UploadWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
//business logic
Data data = new Data.Builder().putString("image-url","http://xxxx.png").build();
return Result.success(data);
}
}
複製代碼
對任何的WorkRequest對象,經過給一組任務賦值一個標籤就能夠在邏輯上把它們變成一個組。這樣就能夠操做特定標籤的所有任務。
例如,WorkManager.cancelAllWorkByTag(String)取消了全部該標籤的任務;WorkManager.getWorkInfosByTagLiveData(String)返回了一個LiveData包含了該標籤下的所有任務的狀態列表
OneTimeWorkRequest uploadReq = new OneTimeWorkRequest.Builder(UploadWorker.class)
.addTag("upload")
.build();
複製代碼
在任務的生命週期中,會通過各類狀態:
當把任務放入隊列中,WorkManager容許檢查它們的狀態。這些信息能夠經過WorkInfo對象獲取,包含了任務的id,tag,當前的State和輸出的數據。
有如下幾個方法獲取WorkInfo:
上述方法返回的LiveData能夠經過註冊一個監聽器觀察WorkInfo的變化。
WorkInfo workInfo = WorkManager.getInstance(this).getWorkInfoById(UUID.fromString("uuid")).get();
WorkManager.getInstance(this).getWorkInfoByIdLiveData(UUID.fromString("uuid")).observe(this, new Observer<WorkInfo>() {
@Override
public void onChanged(WorkInfo workInfo) {
}
});
複製代碼
2.3.0-alpha01版本的WorkManager增長了設置和觀察任務的進度的支持。若是應用在前臺時任務在運行,進度信息能夠展現給用戶,經過API返回的WorkInfo的LiveData。
ListenableWorker如今支持setProgressAsync(),可以保存中間進度。這些API使得開發者可以設置進度,以便在UI上可以展現出來。進度用Data類型表示,這是一個可序列化的屬性容器(相似輸入和輸出,受一樣的限制)。
進度信息只有在ListenableWorker運行時才能被觀察和更新。當ListenableWorker結束時設置進度會被忽略。經過調用getWorkInfoBy..()或者getWorkInfoBy...LiveData()接口來觀察進度信息。這些方法能返回WorkInfo的對象實例,它們有一個新的getProgress()方法能返回Data對象。
開發者使用ListenableWorker或者Worker,setProgressAsync()接口會返回一個ListenableFuture;更新進度是異步的,包含了存儲進度信息到數據庫。在Kotlin中,可使用CoroutineWorker對象的setProgress()擴展方法來更新進度信息。
public class ProgressWorker extends Worker {
public ProgressWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
setProgressAsync(new Data.Builder().putInt("progress", 0).build());
}
@NonNull
@Override
public Result doWork() {
setProgressAsync(new Data.Builder().putInt("progress", 100).build());
return Result.success();
}
}
複製代碼
觀察進度信息比較簡單。可使用getWorkInfoBy...()或getWorkInfoBy...LiveData()方法,獲取一個WorkInfo的引用。
WorkRequest progress = new OneTimeWorkRequest.Builder(ProgressWorker.class).addTag("progress").build();
WorkManager.getInstance(this).getWorkInfoByIdLiveData(progress.getId()).observe(this, new Observer<WorkInfo>() {
@Override
public void onChanged(WorkInfo workInfo) {
int progress = workInfo.getProgress().getInt("progress", 0);
}
});
複製代碼
WorkManager容許建立和入隊一連串的任務,能夠指定多個依賴的任務,以及它們的執行順序。若是要以一個特定的順序執行多個任務時會很是有用。
要建立一連串的任務,可使用WorkManager.beginWith(OneTimeWorkRequest)或者WorkManager.beginWith(List),它們會返回一個WorkContinuation實例。
一個WorkContinuation實例以後能夠用來添加依賴的OneTimeWorkRequest,經過調用WorkContainuation.then(OneTimeWorkRequest)或WorkContinuation.then(List)。
每一個WorkContinuation.then(...)的調用,會返回一個新的WorkContinuation實例。若是添加了OneTimeRequest的列表,這些請求有可能會串行地運行。
最終,能夠用WorkContinuation.enqueue()方法把WorkContinuation鏈放入隊列。
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();
複製代碼
當使用鏈式的OneTimeWorkRequest,父OneTimeWorkRequest的輸出會做爲子任務的輸入。因此上例中的filter1,filter2和filter3的輸出會做爲compress任務的輸入。
爲了管理來自多個父任務的輸入,WorkManager使用InputMerger進行輸入合併。
WorkManager提供了兩種不一樣類型的InputMerger:
OneTimeWorkRequest compress =
new OneTimeWorkRequest.Builder(CompressWorker.class)
.setInputMerger(ArrayCreatingInputMerger.class)
.setConstraints(constraints)
.build();
複製代碼
當建立一個OneTimeWorkRequest任務鏈時,有幾件事要記住:
若是再也不須要入隊的任務執行,能夠取消它。取消一個單獨的WorkRequest最簡單的方法是使用id並調用WorkManager.cancenWorkById(UUID)。
WorkManager.cancelWorkById(workRequest.getId());
複製代碼
在底層,WorkManager會檢查任務的狀態。若是這個任務已經完成,沒有任何事情發生。不然,這個任務的狀態會轉移到CANCELED 而且這個任務之後不會再運行。任何依賴這個任務的其餘WorkRequest都會被標記爲CANCELED。
另外,若是當前任務正在運行,這個任務會觸發ListenableWorker.onStopped()的回調。重寫這個方法來處理任何可能的清理工做。
也能夠用標籤來取消任務,經過調用WorkManager.cancelAllWorkByTag(String)。注意,這個方法會取消全部有這個標籤的任務。另外,也能夠調用WorkManager.cancelUniqueWork(String)取消帶有該獨特名字的所有任務。
有幾種狀況,運行中的任務會被WorkManager終止:
在這些狀況下,任務會觸發ListenableWorker.onStopped()的回調。你應該執行任務清理和配合地終止任務,以防系統會關閉應用。好比,在此時應該關閉開啓的數據庫和文件句柄,或者在更早的時間裏作這些事情。另外,不管什麼時候想要判斷任務是否被終止了能夠查詢ListenableWorker.isStopped()。即便您經過在調用onStopped()以後返回一個結果來表示您的工做已經完成,WorkManager也會忽略這個結果,由於這個任務已經被認爲是結束了。
你的應用優點會須要週期性地運行某些任務。好比,應用可能會週期性地備份數據,下載新的數據,或者上傳到日誌到服務器。
使用PeriodicWorkRequest來執行那些須要週期性地運行的任務。
PeriodicWorkRequest不能被連接。若是任務須要連接,考慮使用OneTimeWorkRequest。
Constraints constraints = new Constraints.Builder()
.setRequiresCharging(true)
.build();
PeriodicWorkRequest saveRequest =
new PeriodicWorkRequest.Builder(SaveImageFileWorker.class, 1, TimeUnit.HOURS)
.setConstraints(constraints)
.build();
WorkManager.getInstance(myContext)
.enqueue(saveRequest);
複製代碼
週期間隔是兩次重複執行的最小時間。任務實際執行的時間取決於任務設置的約束和系統的優化。
觀察PeriodicWorkRequest的狀態的方法跟OneTimeWorkRequest同樣。
惟一任務是一個有用的概念,它保證了某一時刻只能有一個帶有特定名稱的任務鏈。不像id是由WorkManager自動生成的,惟一名稱是可讀的,而且是開發者指定的。也不像tag,惟一名稱只能跟一個任務鏈關聯。
能夠經過調用WorkManager.enqueueUniqueWork()或者WorkManager.enqueueUniqueWork()來建立一個惟一任務隊列。第一個參數是惟一名字—用於識別WorkRequest。第二個參數是衝突的解決策略,指定了若是已經存在一個未完成的同名任務鏈時WorkManager採起的措施:
若是有一個任務不須要屢次放入隊列時,惟一任務會頗有用。例如,若是你的應用須要同步數據到網絡,能夠入隊一個命名爲「sync」的事件,而且若是已經有這個名字的任務了,那麼新的任務應該被忽略。若是你須要逐漸地創建一個很長的任務鏈,惟一任務隊列也頗有用。例如,一個相片編輯應用可能會讓用戶撤銷一長串編輯動做。每一個撤銷操做可能會耗時一段時間,可是它們必須按正確的順序執行。在這個狀況下,這個應用能夠建立一個「undo」的任務鏈,並把每一個新的操做放在最後。
若是要建立一個惟一任務鏈,可使用WorkManager.beginUniqueWork()而不是beginWith()。
WorkManager提供了work-test工件在Android設備上爲任務進行單元測試。
爲了使用work-test工件,須要在build.gradle中添加androidTestImplementation依賴。
androidTestImplementation "androidx.work:work-testing:2.3.0-alpha01"
複製代碼
work-testing提供了一個測試模式下的WorkManager的特殊實現,它是用WorkManagerTestInitHelper進行初始化。
work-testing工件提供了一個SynchronousExecutor使得能更簡單地用同步方式進行測試,不須要去處理多線程,鎖或佔用。
在build.gradle中編輯依賴
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation "androidx.work:work-testing:2.2.0"
複製代碼
單元測試類setup
@Before
public void setup() {
Context context = ApplicationProvider.getApplicationContext();
Configuration config = new Configuration.Builder()
// Set log level to Log.DEBUG to
// make it easier to see why tests failed
.setMinimumLoggingLevel(Log.DEBUG)
// Use a SynchronousExecutor to make it easier to write tests
.setExecutor(new SynchronousExecutor())
.build();
// Initialize WorkManager for instrumentation tests.
WorkManagerTestInitHelper.initializeTestWorkManager(context, config);
}
複製代碼
WorkManager在測試模式下已經初始化,能夠開始測試任務。
public class TestWorker extends Worker {
public TestWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
Data input = getInputData();
if (input.size() == 0) {
return Result.failure();
} else {
return Result.success(input);
}
}
}
複製代碼
測試模式下的使用跟正常應用中使用十分相似。
package com.example.hero.workmgr;
import android.content.Context;
import android.util.Log;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.work.Configuration;
import androidx.work.Data;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;
import androidx.work.testing.SynchronousExecutor;
import androidx.work.testing.WorkManagerTestInitHelper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
/** * Instrumented test, which will execute on an Android device. * * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> */
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Before
public void setup() {
Context context = ApplicationProvider.getApplicationContext();
Configuration config = new Configuration.Builder()
// Set log level to Log.DEBUG to
// make it easier to see why tests failed
.setMinimumLoggingLevel(Log.DEBUG)
// Use a SynchronousExecutor to make it easier to write tests
.setExecutor(new SynchronousExecutor())
.build();
// Initialize WorkManager for instrumentation tests.
WorkManagerTestInitHelper.initializeTestWorkManager(context, config);
}
@Test
public void testWorker() throws Exception {
Data input = new Data.Builder().put("a", 1).put("b", 2).build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(TestWorker.class).setInputData(input).build();
WorkManager mgr = WorkManager.getInstance(ApplicationProvider.getApplicationContext());
mgr.enqueue(request).getResult().get();
//該接口其實獲得的是一個StatusRunnable,從數據庫中查詢到WorkInfo後會調用SettableFuture.set(),而後get()會返回對應的WorkInfo
WorkInfo workInfo = mgr.getWorkInfoById(request.getId()).get();
assertThat(workInfo.getState(), is(WorkInfo.State.ENQUEUED));
workInfo = mgr.getWorkInfoById(request.getId()).get();
assertThat(workInfo.getState(), is(WorkInfo.State.RUNNING));
workInfo = mgr.getWorkInfoById(request.getId()).get();
assertThat(workInfo.getState(), is(WorkInfo.State.SUCCEEDED));
Data output = workInfo.getOutputData();
assertThat(output.getInt("a", -1), is(1));
}
}
複製代碼
WorkManagerTestInitHelper提供一個TestDriver實例,它可以模擬初始化延遲,ListenableWorker須要的約束條件和循環任務的週期等。
任務能夠設置初始化延遲。用TestDriver設置任務所須要的初始化延遲,就不須要等待這個時間到來,這樣能夠測試任務的延遲是否有效。
@Test
public void testDelay() throws Exception {
Data input = new Data.Builder().put("a", 1).put("b", 2).build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(TestWorker.class).setInputData(input).setInitialDelay(10, TimeUnit.SECONDS).build();
WorkManager mgr = WorkManager.getInstance(ApplicationProvider.getApplicationContext());
TestDriver driver = WorkManagerTestInitHelper.getTestDriver(ApplicationProvider.getApplicationContext());
mgr.enqueue(request).getResult().get();
driver.setInitialDelayMet(request.getId());
WorkInfo workInfo = mgr.getWorkInfoById(request.getId()).get();
assertThat(workInfo.getState(), is(WorkInfo.State.ENQUEUED));
workInfo = mgr.getWorkInfoById(request.getId()).get();
assertThat(workInfo.getState(), is(WorkInfo.State.RUNNING));
workInfo = mgr.getWorkInfoById(request.getId()).get();
assertThat(workInfo.getState(), is(WorkInfo.State.SUCCEEDED));
}
複製代碼
TestDriver能夠調用setAllConstraintsMet設置全部的約束都知足條件。
@Test
public void testConstraint() throws Exception {
Data input = new Data.Builder().put("a", 1).put("b", 2).build();
Constraints constraints = new Constraints.Builder().setRequiresDeviceIdle(true).build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(TestWorker.class).setInputData(input).setConstraints(constraints).build();
WorkManager mgr = WorkManager.getInstance(ApplicationProvider.getApplicationContext());
TestDriver driver = WorkManagerTestInitHelper.getTestDriver(ApplicationProvider.getApplicationContext());
mgr.enqueue(request).getResult().get();
driver.setAllConstraintsMet(request.getId());
WorkInfo workInfo = mgr.getWorkInfoById(request.getId()).get();
assertThat(workInfo.getState(), is(WorkInfo.State.ENQUEUED));
workInfo = mgr.getWorkInfoById(request.getId()).get();
assertThat(workInfo.getState(), is(WorkInfo.State.RUNNING));
workInfo = mgr.getWorkInfoById(request.getId()).get();
assertThat(workInfo.getState(), is(WorkInfo.State.SUCCEEDED));
}
複製代碼
TestDriver提供了一個setPeriodDelayMet來表示間隔已經達到。
@Test
public void testPeriod() throws Exception {
Data input = new Data.Builder().put("a", 1).put("b", 2).build();
PeriodicWorkRequest request = new PeriodicWorkRequest.Builder(TestWorker.class, 10, TimeUnit.SECONDS).setInputData(input).build();
WorkManager mgr = WorkManager.getInstance(ApplicationProvider.getApplicationContext());
TestDriver driver = WorkManagerTestInitHelper.getTestDriver(ApplicationProvider.getApplicationContext());
mgr.enqueue(request).getResult().get();
driver.setPeriodDelayMet(request.getId());
WorkInfo workInfo = mgr.getWorkInfoById(request.getId()).get();
assertThat(workInfo.getState(), is(WorkInfo.State.ENQUEUED));
workInfo = mgr.getWorkInfoById(request.getId()).get();
assertThat(workInfo.getState(), is(WorkInfo.State.RUNNING));
workInfo = mgr.getWorkInfoById(request.getId()).get();
//循環任務完成後,狀態仍會變成ENQUEUED(WorkerWrapper中的handleResult()的邏輯)
assertThat(workInfo.getState(), is(WorkInfo.State.ENQUEUED));
}
複製代碼
從2.1.0版本開始,WorkManager提供了新的API,能更方便的測試Worker,ListenableWorker,以及ListenableWorker的變體(CoroutineWorker 和RxWorker)。
以前,爲了測試任務,須要使用WorkManagerTestInitHelper來初始化WorkManager。在2.1.0中,不必定要使用它。若是隻是爲了測試任務中的業務邏輯,不再須要使用WorkManagerTestInitHelper。
爲了測試ListenableWorker和它的變體,可使用TestListenableWorkerBuilder。這個建造器能夠建立一個ListenableWorker的實例,用來測試任務中的業務邏輯。
package com.example.hero.workmgr;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.concurrent.futures.ResolvableFuture;
import androidx.work.ListenableWorker;
import androidx.work.WorkerParameters;
import com.google.common.util.concurrent.ListenableFuture;
public class SleepWorker extends ListenableWorker {
private ResolvableFuture<Result> mResult;
private Handler mHandler;
private final Object mLock;
private Runnable mRunnable;
public SleepWorker(@NonNull Context appContext, @NonNull WorkerParameters workerParams) {
super(appContext, workerParams);
mResult = ResolvableFuture.create();
mHandler = new Handler(Looper.getMainLooper());
mLock = new Object();
}
@NonNull
@Override
public ListenableFuture<Result> startWork() {
mRunnable = new Runnable() {
@Override
public void run() {
synchronized (mLock) {
mResult.set(Result.success());
}
}
};
mHandler.postDelayed(mRunnable, 1000L);
return mResult;
}
@Override
public void onStopped() {
super.onStopped();
if (mRunnable != null) {
mHandler.removeCallbacks(mRunnable);
}
synchronized (mLock) {
if (!mResult.isDone()) {
mResult.set(Result.failure());
}
}
}
}
複製代碼
爲了測試SleepWorker,先用TestListenableWorkerBuilder建立了一個Worker的實例。這個建立器也能夠用來設置標籤,輸入和嘗試運行次數等參數。
@Test
public void testSleepWorker() throws Exception{
//直接建立了一個worker實例,調用它的方法
ListenableWorker worker = TestListenableWorkerBuilder.from(ApplicationProvider.getApplicationContext(), SleepWorker.class).build();
ListenableWorker.Result result = worker.startWork().get();
assertThat(result, is(ListenableWorker.Result.success()));
}
複製代碼
有一個任務以下:
public class Sleep extends Worker {
public Sleep(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
try {
Thread.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}
return Result.success();
}
}
複製代碼
使用TestWorkerBuilder進行測試。TestWorkerBuilder容許指定運行任務的線程池。
@Test
public void testThreadSleepWorker() throws Exception {
Sleep woker = (Sleep) TestWorkerBuilder.from(ApplicationProvider.getApplicationContext(), Sleep.class,
Executors.newSingleThreadExecutor()).build();
ListenableWorker.Result result = woker.doWork();
assertThat(result, is(ListenableWorker.Result.success()));
}
複製代碼