問題
在使用WorkManage時,故意不設置Future的結果,查看其超時機制原理,發現并不是一個固定的時間段,有時候會幾分鐘,有時候會立即超時,從而判定任務失敗。
那么它的超時機制究竟依賴于什么呢?
探究
其實WorkManager的超時機制就是使用了Future的超時機制。
最終依賴CallbackToFutureAdapter#finalize實現:
1.切入點:WorkerWrapper#runWorker
runExpedited.addListener(new Runnable() {
@Override
public void run() {
try {
runExpedited.get();
Logger.get().debug(TAG,
String.format("Starting work for %s", mWorkSpec.workerClassName));
// Call mWorker.startWork() on the main thread.
mInnerFuture = mWorker.startWork();
future.setFuture(mInnerFuture);
} catch (Throwable e) {
future.setException(e);
}
}
}, mWorkTaskExecutor.getMainThreadExecutor());
// Avoid synthetic accessors.
final String workDescription = mWorkDescription;
future.addListener(new Runnable() {
@Override
@SuppressLint("SyntheticAccessor")
public void run() {
try {
// If the ListenableWorker returns a null result treat it as a failure.
ListenableWorker.Result result = future.get();
if (result == null) {
Logger.get().error(TAG, String.format(
"%s returned a null result. Treating it as a failure.",
mWorkSpec.workerClassName));
} else {
Logger.get().debug(TAG, String.format("%s returned a %s result.",
mWorkSpec.workerClassName, result));
mResult = result;
}
} catch (CancellationException exception) {
// Cancellations need to be treated with care here because innerFuture
// cancellations will bubble up, and we need to gracefully handle that.
Logger.get().info(TAG, String.format("%s was cancelled", workDescription),
exception);
} catch (InterruptedException | ExecutionException exception) {
Logger.get().error(TAG,
String.format("%s failed because it threw an exception/error",
workDescription), exception);
} finally {
onWorkFinished();
}
}
}, mWorkTaskExecutor.getBackgroundExecutor());
上述代碼中最重要的代碼就是:
// 開始任務
mInnerFuture = mWorker.startWork();
通過Future監聽上述任務狀態
future.addListener(new Runnable() {
@Override
@SuppressLint("SyntheticAccessor")
public void run() {
try {
// If the ListenableWorker returns a null result treat it as a failure.
ListenableWorker.Result result = future.get();
if (result == null) {
Logger.get().error(TAG, String.format(
"%s returned a null result. Treating it as a failure.",
mWorkSpec.workerClassName));
} else {
Logger.get().debug(TAG, String.format("%s returned a %s result.",
mWorkSpec.workerClassName, result));
mResult = result;
}
} catch (CancellationException exception) {
// Cancellations need to be treated with care here because innerFuture
// cancellations will bubble up, and we need to gracefully handle that.
Logger.get().info(TAG, String.format("%s was cancelled", workDescription),
exception);
} catch (InterruptedException | ExecutionException exception) {
Logger.get().error(TAG,
String.format("%s failed because it threw an exception/error",
workDescription), exception);
} finally {
onWorkFinished();
}
}
}, mWorkTaskExecutor.getBackgroundExecutor());
為了探究其超時機制,在Work中注釋掉了設置Future的Result的代碼,以此來觀察WorkManager的超時機制,或者是Future的超時機制。
public ListenableFuture<Result> startWork() {
return CallbackToFutureAdapter.getFuture(completer -> {
// 省略業務代碼
// 注釋掉了設置Future的Result的代碼
// return completer.set(Result.success());
// 省略業務代碼
});
我們發現超時后一定會走到 如下catch中,并打印日志如下
catch (InterruptedException | ExecutionException exception)
Work [ id=1bd795a1-59bd-44b2-920f-2467a1696082, tags={ AllDataWorker, tag_all_data_work_request } ] failed because it threw an exception/error , >> androidx.concurrent.futures.CallbackToFutureAdapter$FutureGarbageCollectedException: The completer object was garbage collected - this future would otherwise never complete. The tag was: AllDataWorker$1@ef1fdd2
然后設置了此任務的狀態為Failure
Worker result FAILURE for Work [ id=1bd795a1-59bd-44b2-920f-2467a1696082, tags={ AllDataWorker, tag_all_data_work_request } ]
2.上溯來源,查看future#addListener的實現
@Override
public final void addListener(Runnable listener, Executor executor) {
checkNotNull(listener);
checkNotNull(executor);
Listener oldHead = listeners;
if (oldHead != Listener.TOMBSTONE) {
Listener newNode = new Listener(listener, executor);
do {
newNode.next = oldHead;
if (ATOMIC_HELPER.casListeners(this, oldHead, newNode)) {
return;
}
oldHead = listeners; // re-read
} while (oldHead != Listener.TOMBSTONE);
}
executeListener(listener, executor);
}
3.繼續上溯 executeListener的調用,通過debug追蹤。最終追溯到CallbackToFutureAdapter#finalize
@Override
protected void finalize() {
SafeFuture<T> localFuture = future;
// Complete the future with an error before any cancellation listeners try to set the
// future.
// Also avoid allocating the exception if we know we won't actually be able to set it.
if (localFuture != null && !localFuture.isDone()) {
localFuture.setException(
new FutureGarbageCollectedException(
"The completer object was garbage collected - this future would "
+ "otherwise never "
+ "complete. The tag was: "
+ tag));
}
if (!attemptedSetting) {
ResolvableFuture<Void> localCancellationFuture = cancellationFuture;
if (localCancellationFuture != null) {
// set is idempotent, so even if this was already invoked it won't run
// listeners twice
localCancellationFuture.set(null);
}
}
}
}
上述代碼中的FutureGarbageCollectedException正是我們最初捕獲的catch,并打印的日志。
new FutureGarbageCollectedException(
"The completer object was garbage collected - this future would "
+ "otherwise never "
+ "complete. The tag was: "
+ tag));
說明
至此,我們完全明白了超時機制是由CallbackToFutureAdapter#finalize實現的。
對于CallbackToFutureAdapter#finalize方法,我們看下ChatGPT怎么說:
ChatGPT解釋CallbackToFutureAdapter#finalize
按照慣例,來張鳴人的圖片,一起加油吧!