Android WorkManager 에 대한 간단 한 설명
16024 단어 AndroidWorkManager
WorkManager API 는 지연 가능 한 비동기 작업 을 쉽게 지정 할 수 있 습 니 다.작업 을 만 들 고 WorkManager 에 게 즉시 실행 하거나 적당 한 시간 에 실행 할 수 있 도록 합 니 다.WorkManager 는 장치 API 의 등급 과 응용 프로그램 상태 등 요소 에 따라 적당 한 방식 으로 작업 을 실행 합 니 다.WorkManager 가 프로그램 이 실 행 될 때 작업 을 수행 하면 프로그램 프로 세 스 의 새 스 레 드 에서 실 행 됩 니 다.프로그램 이 실행 되 지 않 으 면 WorkManager 는 장치 API 등급 과 포 함 된 의존 항목 에 따라 배경 작업 을 적당 한 방식 으로 배정 하고 JobScheduler,Firebase JobDispatcher 또는 AlarmManager 를 사용 할 수 있 습 니 다.장치 논리 로 장치 가 어떤 기능 을 가지 고 있 는 지 확인 하고 적당 한 API 를 선택 할 필요 가 없습니다.반대로 WorkManager 에 게 맡 기 면 가장 좋 은 방법 을 선택 할 수 있 습 니 다.
Note:WorkManager 는 응용 프로그램 이 시스템 을 종료 하 더 라 도 작업 을 실행 할 수 있 도록 해 야 합 니 다.예 를 들 어 응용 데 이 터 를 서버 에 업로드 하 는 것 입 니 다.프로그램 이 백 엔 드 프로 세 스 를 종료 하면 작업 을 안전하게 종료 할 수 있 는 경우 에는 ThreadPools 를 사용 하 는 것 을 추천 합 니 다.
기능:
기본 기능
2.소스 코드 에 대한 간단 한 분석
android.arch.work:work-runtime-1.0.0-beta03
WorkerManager 의 구체 적 인 실현 클래스 는 WorkManager Impl 입 니 다.
WorkManager 는 다른 방법 으로***Runnable 클래스 를 만들어 실행 합 니 다.
다음은 전체적인 가방 구조 입 니 다.
Enqueue Runnable 을 예 로 들 면...
@Override
public void run() {
try {
if (mWorkContinuation.hasCycles()) {
throw new IllegalStateException(
String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
}
boolean needsScheduling = addToDatabase();
if (needsScheduling) {
final Context context =
mWorkContinuation.getWorkManagerImpl().getApplicationContext();
PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);
scheduleWorkInBackground();
}
mOperation.setState(Operation.SUCCESS);
} catch (Throwable exception) {
mOperation.setState(new Operation.State.FAILURE(exception));
}
}
/**
* Schedules work on the background scheduler.
*/
@VisibleForTesting
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
주로 Schedulers 클래스 에서 실 행 됩 니 다.
/**
* Schedules {@link WorkSpec}s while honoring the {@link Scheduler#MAX_SCHEDULER_LIMIT}.
*
* @param workDatabase The {@link WorkDatabase}.
* @param schedulers The {@link List} of {@link Scheduler}s to delegate to.
*/
public static void schedule(
@NonNull Configuration configuration,
@NonNull WorkDatabase workDatabase,
List<Scheduler> schedulers) {
if (schedulers == null || schedulers.size() == 0) {
return;
}
...
if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);
// Delegate to the underlying scheduler.
for (Scheduler scheduler : schedulers) {
scheduler.schedule(eligibleWorkSpecsArray);
}
}
}
다음은 Scheduler 의 하위 클래스 를 살 펴 보 겠 습 니 다.마지막 으로 Worker Wrapper 포장 류 를 만 들 고 우리 가 정의 하 는 Worker 류 를 실행 합 니 다.
@WorkerThread
@Override
public void run() {
mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
mWorkDescription = createWorkDescription(mTags);
runWorker();
}
private void runWorker() {
if (tryCheckForInterruptionAndResolve()) {
return;
}
mWorkDatabase.beginTransaction();
try {
mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);
if (mWorkSpec == null) {
Logger.get().error(
TAG,
String.format("Didn't find WorkSpec for id %s", mWorkSpecId));
resolve(false);
return;
}
// running, finished, or is blocked.
if (mWorkSpec.state != ENQUEUED) {
resolveIncorrectStatus();
mWorkDatabase.setTransactionSuccessful();
return;
}
// Case 1:
// Ensure that Workers that are backed off are only executed when they are supposed to.
// GreedyScheduler can schedule WorkSpecs that have already been backed off because
// it is holding on to snapshots of WorkSpecs. So WorkerWrapper needs to determine
// if the ListenableWorker is actually eligible to execute at this point in time.
// Case 2:
// On API 23, we double scheduler Workers because JobScheduler prefers batching.
// So is the Work is periodic, we only need to execute it once per interval.
// Also potential bugs in the platform may cause a Job to run more than once.
if (mWorkSpec.isPeriodic() || mWorkSpec.isBackedOff()) {
long now = System.currentTimeMillis();
if (now < mWorkSpec.calculateNextRunTime()) {
resolve(false);
return;
}
}
mWorkDatabase.setTransactionSuccessful();
} finally {
mWorkDatabase.endTransaction();
}
// Merge inputs. This can be potentially expensive code, so this should not be done inside
// a database transaction.
Data input;
if (mWorkSpec.isPeriodic()) {
input = mWorkSpec.input;
} else {
InputMerger inputMerger = InputMerger.fromClassName(mWorkSpec.inputMergerClassName);
if (inputMerger == null) {
Logger.get().error(TAG, String.format("Could not create Input Merger %s",
mWorkSpec.inputMergerClassName));
setFailedAndResolve();
return;
}
List<Data> inputs = new ArrayList<>();
inputs.add(mWorkSpec.input);
inputs.addAll(mWorkSpecDao.getInputsFromPrerequisites(mWorkSpecId));
input = inputMerger.merge(inputs);
}
WorkerParameters params = new WorkerParameters(
UUID.fromString(mWorkSpecId),
input,
mTags,
mRuntimeExtras,
mWorkSpec.runAttemptCount,
mConfiguration.getExecutor(),
mWorkTaskExecutor,
mConfiguration.getWorkerFactory());
// Not always creating a worker here, as the WorkerWrapper.Builder can set a worker override
// in test mode.
if (mWorker == null) {
mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback(
mAppContext,
mWorkSpec.workerClassName,
params);
}
if (mWorker == null) {
Logger.get().error(TAG,
String.format("Could not create Worker %s", mWorkSpec.workerClassName));
setFailedAndResolve();
return;
}
if (mWorker.isUsed()) {
Logger.get().error(TAG,
String.format("Received an already-used Worker %s; WorkerFactory should return "
+ "new instances",
mWorkSpec.workerClassName));
setFailedAndResolve();
return;
}
mWorker.setUsed();
// Try to set the work to the running state. Note that this may fail because another thread
// may have modified the DB since we checked last at the top of this function.
if (trySetRunning()) {
if (tryCheckForInterruptionAndResolve()) {
return;
}
final SettableFuture<ListenableWorker.Result> future = SettableFuture.create();
// Call mWorker.startWork() on the main thread.
mWorkTaskExecutor.getMainThreadExecutor()
.execute(new Runnable() {
@Override
public void run() {
try {
mInnerFuture = mWorker.startWork();
future.setFuture(mInnerFuture);
} catch (Throwable e) {
future.setException(e);
}
}
});
// 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 {
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());
} else {
resolveIncorrectStatus();
}
}
안 드 로 이 드 x.work.impl.utils.future.Settable Future 를 사용 하고 addListener 방법 을 호출 했 습 니 다.이 리 셋 방법 은 set 를 호출 할 때 실 행 됩 니 다.
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 {
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());
핵심 Worker 클래스 를 살 펴 보 겠 습 니 다.
@Override
public final @NonNull ListenableFuture<Result> startWork() {
mFuture = SettableFuture.create();
getBackgroundExecutor().execute(new Runnable() {
@Override
public void run() {
Result result = doWork();
mFuture.set(result);
}
});
return mFuture;
}
이 를 통 해 알 수 있 듯 이 doWork()를 호출 한 후에 작업 이 set 방법 을 실 행 했 습 니 다.이 때 addListener 방법 을 되 돌려 줍 니 다.addListener 리 셋 은 현재 작업 의 상 태 를 판단 하 는 데 사 용 됩 니 다.따라서 작업 이 중단 되면 캡 처 된 이상 정 보 를 보 여 줍 니 다.
예 를 들 어 하나의 작업 을 호출 하 는 cancel 방법 은 아래 의 정 보 를 보 여 줍 니 다.
1. 2019-02-02 15:35:41.682 30526-30542/com.outman.study.workmanagerdemo I/WM-WorkerWrapper: Work [ id=3d775394-e0d7-44e3-a670-c3527a3245ee, tags={ com.outman.study.workmanagerdemo.SimpleWorker } ] was cancelled
2. java.util.concurrent.CancellationException: Task was cancelled.
3. at androidx.work.impl.utils.futures.AbstractFuture.cancellationExceptionWithCause(AbstractFuture.java:1184)
4. at androidx.work.impl.utils.futures.AbstractFuture.getDoneValue(AbstractFuture.java:514)
5. at androidx.work.impl.utils.futures.AbstractFuture.get(AbstractFuture.java:475)
6. at androidx.work.impl.WorkerWrapper$2.run(WorkerWrapper.java:264)
7. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
8. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
9. at java.lang.Thread.run(Thread.java:764)
이상 은 제 간단 한 분석 입 니 다.아직 말 하지 않 은 것 이 많 습 니 다.나중에 시간 이 있 으 면 계속 할 것 입 니 다.옳지 않 은 비판 지적 을 환영 합 니 다.여러분 의 학습 에 도움 이 되 기 를 바 랍 니 다.여러분 들 도 저 희 를 많이 응원 해 주시 기 바 랍 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.