Android

WorkManager (android developers)

 

WorkManager

 

https://developer.android.com/topic/libraries/architecture/workmanager#java

 

  1. Overview
  2. Getting started
  3. How-To Guides : Define work requests
  4. How-To Guides : Work States
  5. How-To Guides : Managing work
  6. How-To Guides : Observing intermediate Worker progress
  7. How-To Guides : Chaining Work
  8. Threading in Worker
  9. Support for long-running workers

 

 


 

Schedule tasks with WorkManager

 

WorkManager는 지연 가능한 비동기 작업을 쉽게 예약할 수 있는 API로, 앱이 종료되거나 기기가 다시 시작되더라도 실행될 수 있습니다.
WorkManager API는 FirebaseJobDispatcher, GcmNetworkManager, JobScheduler를 비롯하여 모든 이전 Android 백그라운드 예약 API를 대체할 수 있는 적합하고 권장되는 API입니다.
WorkManager는 API 수준 14 와 역호환되는 모던하고 일관된 API에 이전 기능을 통합하면서 배터리 수명도 개선합니다.

 

Features

 

1. 작업 제약조건

최적의 조건을 정의합니다.
예를 들면 와이파이 연결시에만 또는 디바이스가 아이들 상태일 때 또는 충분한 저장 공간이 있을 경우에만 동작하게 할 수 있습니다.

 

2. 강력한 예약 관리

WorkManager 는 유연한 예약 윈도우를 사용해서 한번만 실행하거나 반복적으로 실행되게 예약할 수 있습니다.
태그를 달거나 이름을 주어서 고유하고 대체할 수 있도록 할 수 있고 작업 그룹들을 모니터링하거나 취소할 수 있습니다.
예약된 작업은 내부적으로 관리되는 SQLite 데이터베이스에 저장되고 WorkManager 는 이 작업이 지속되고 리부팅시에는 재예약하도록 보장합니다.
WorkManager 는 전원절약기능과 Doze 모드와 같은 권장 사항을 준수하므로 배터리를 걱정할 필요가 없습니다.

 

3. 유연한 재시도 정책

경우에 따라 작업에 실패하기도 합니다. WorkManager에서는 구성 가능한 지수 백오프 정책을 비롯해 유연한 재시도 정책을 제공합니다.

 

4. 작업 체이닝

복잡한 관련 작업의 경우 유연하고 자연스러운 인터페이스를 사용하여 개별 작업을 함께 체이닝합니다.
그래서 순차적으로 실행할 작업과 동시에 실행할 작업을 제어할 수 있습니다.

 

WorkManager.getInstance(...)
    .beginWith(Arrays.asList(workA, workB))
    .then(workC)
    .enqueue();

 

각 작업의 경우, 작업의 입력 및 출력 데이터를 정의할 수 있습니다.
작업을 함께 체이닝하면 WorkManager에서 자동으로 한 작업의 출력 데이터를 다음 작업으로 전달합니다.

 

5. 내장 스레딩 상호 운용성

WorkManager에서는 RxJava 및 코루틴과 원활하게 통합되며 자체 비동기 API를 연결할 수 있는 유연성을 제공합니다.

 

지연 가능하고 안정적인 작업을 위해 WorkManager 사용

WorkManager는 앱이 종료되거나 기기가 다시 시작되더라도 지연 가능(즉시 실행될 필요가 없음)하고 안정적으로 실행되어야 하는 작업을 대상으로 설계되었습니다. 예:

– 백엔드 서비스에 로그 또는 분석을 전송
– 주기적으로 애플리케이션 데이터를 서버와 동기화

WorkManager는 앱 프로세스가 사라지더라도 안전하게 종료할 수 있는 진행 중인 백그라운드 작업 또는 즉시 실행해야 하는 작업에는 적합하지 않습니다.
적합한 솔루션을 알아보려면 백그라운드 처리 가이드를 검토하세요.

 


 

Getting started with WorkManager

 

Add the following dependencies to your app’s build.gradle file:

 

dependencies {
  def work_version = "2.4.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"
  }

 

 

 

Define the work

 

 

 


 

Observing intermediate Worker progress

 

 

Updating Progress

 

import android.content.Context;
import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

public class ProgressWorker extends Worker {

    private static final String PROGRESS = "PROGRESS";
    private static final long DELAY = 1000L;

    public ProgressWorker(
        @NonNull Context context,
        @NonNull WorkerParameters parameters) {
        super(context, parameters);
        // Set initial progress to 0
        setProgressAsync(new Data.Builder().putInt(PROGRESS, 0).build());
    }

    @NonNull
    @Override
    public Result doWork() {
        try {
            // Doing work.
            Thread.sleep(DELAY);
        } catch (InterruptedException exception) {
            // ... handle exception
        }
        // Set progress to 100 after you are done doing your work.
        setProgressAsync(new Data.Builder().putInt(PROGRESS, 100).build());
        return Result.success();
    }
}

 

 

Observing Progress

 

WorkManager.getInstance(getApplicationContext())
     // requestId is the WorkRequest id
     .getWorkInfoByIdLiveData(requestId)
     .observe(lifecycleOwner, new Observer<WorkInfo>() {
             @Override
             public void onChanged(@Nullable WorkInfo workInfo) {
                 if (workInfo != null) {
                     Data progress = workInfo.getProgress();
                     int value = progress.getInt(PROGRESS, 0)
                     // Do something with progress
             }
      }
});

 

 


 

Chaining Work

 

다음을 두 개를 이용해서 워크 체인을 만든다.

WorkManager.beginWith(OneTimeWorkRequest)
WorkManager.beginWith(List<OneTimeWorkRequest>)

둘 다 WorkContinuation 인스턴스를 반환한다.
WorkContinuation 은 다음 두개를 사용해서 의존적인 OneTimeWorkRequest 를 추가할 수 있다.

then(OneTimeWorkRequest)
then(List<OneTimeWorkRequest>) .

둘 다 WorkContinuation 인스턴스를 반환한다.

List<OneTimeWorkRequest> 은 병렬로 실행된다.

 

WorkManager.getInstance(myContext)
   // 병렬로 실행됨
   .beginWith(Arrays.asList(plantName1, plantName2, plantName3))
   // 의전적인 작업 (위 3개의 작업이 모두 끝나야 실행됨)
   .then(cache)
   .then(upload)
   // 작업을 큐에 넣어서 실행한다.
   .enqueue();

 

 

Input Mergers

 

OverwritingInputMerger

  1. 키 충돌이 있으면 이전 키값을 새로운 키값으로 교체
  2. List<Worer> 는 병렬 실행되므로 순서가 보장되지 않는다.

 

ArrayCreatingInputMerger : 입력값들을 배열로 만듦.

 

OneTimeWorkRequest cache = new OneTimeWorkRequest.Builder(PlantWorker.class)
       .setInputMerger(ArrayCreatingInputMerger.class)
       .setConstraints(constraints)
       .build();
  1. 키와 배열로 이루어짐.
  2. 키 충돌이 있다면 배열로 묶임.

 


 

Threading in Worker

 

WorkManager 의 Executor 설정 변경

WorkManager.initialize(
    context,
    new Configuration.Builder()
        .setExecutor(Executors.newFixedThreadPool(8))
        .build());

 

웹사이트를 순차적으로 다운받는 예제

public class DownloadWorker extends Worker {

    public DownloadWorker(Context context, WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public Result doWork() {
        for (int i = 0; i < 100; ++i) {
            try {
                downloadSynchronously("https://www.google.com");
            } catch (IOException e) {
                return Result.failure();
            }
        }

        return Result.success();
    }

}

 

 

웹사이트를 다운받는 중에 중단된 경우 해결책

public class DownloadWorker extends Worker {

    public DownloadWorker(Context context, WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    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();
    }
}

 

 


 

Support for long-running workers

 

WorkManager 2.3.0-alpha02 부터 long running workers 를 지원한다.
WorkManager 는 OS 에게 작업이 실행되는 동안 프로세스가 유지되어야 한다고 신호를 보낸다.
이런 Workers 들은 10분 넘게 살아남을 수 있다.

내부적으로 WorkManager 는 foreground 서비스를 실행한다. 그리고 WorkRequest 를 처리한다.

setForegroundAsync() API 를 사용하면 WorkRequest 가 중요하다고 알린다.

2.3.0-alpha03 부터 WorkManager 는 Worker 를 취소하기 위해서 PendingIntent 를 만들 수 있다.
(createCancelPendingIntent() 사용)

ListenableWorker 또는 Worker 를 사용하면 setForegroundAsync() API 를 호출할 수 있다.
(ListenableFuture<Void> 반환)

setForegroundAsync() 를 호출해서 진행중인 알림을 업데이트할 수 있다.

 

public class DownloadWorker extends Worker {
    private static final String KEY_INPUT_URL = "KEY_INPUT_URL";
    private static final String KEY_OUTPUT_FILE_NAME = "KEY_OUTPUT_FILE_NAME";

    private NotificationManager notificationManager;

    public DownloadWorker(
        @NonNull Context context,
        @NonNull WorkerParameters parameters) {
            super(context, parameters);
            notificationManager = (NotificationManager)
                context.getSystemService(NOTIFICATION_SERVICE);
    }

    @NonNull
    @Override
    public Result doWork() {
        Data inputData = getInputData();
        String inputUrl = inputData.getString(KEY_INPUT_URL);
        String outputFile = inputData.getString(KEY_OUTPUT_FILE_NAME);
        // Mark the Worker as important
        String progress = "Starting Download";
        setForegroundAsync(createForegroundInfo(progress));
        download(inputUrl, outputFile);
        return Result.success();
    }

    private void download(String inputUrl, String outputFile) {
        // Downloads a file and updates bytes read
        // Calls setForegroundInfoAsync() periodically when it needs to update
       // the ongoing Notification
    }

    @NonNull
    private ForegroundInfo createForegroundInfo(@NonNull String progress) {
        // Build a notification using bytesRead and contentLength

        Context context = getApplicationContext();
        String id = context.getString(R.string.notification_channel_id);
        String title = context.getString(R.string.notification_title);
        String cancel = context.getString(R.string.cancel_download);
        // This PendingIntent can be used to cancel the worker
        PendingIntent intent = WorkManager.getInstance(context)
                .createCancelPendingIntent(getId());

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            createChannel();
        }

        Notification notification = new NotificationCompat.Builder(context, id)
                .setContentTitle(title)
                .setTicker(title)
                .setSmallIcon(R.drawable.ic_work_notification)
                .setOngoing(true)
                // Add the cancel action to the notification which can
                // be used to cancel the worker
                .addAction(android.R.drawable.ic_delete, cancel, intent)
                .build();

        return new ForegroundInfo(notification);
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private void createChannel() {
        // Create a Notification channel
    }
}

 

알림에 중간 진행 상황 표시.
DoWork() 에서 setProgressAsync(Data data) 를 사용하면
WorkManager 에서 observe 해서 getProgress() 할 수 있다.

https://developer.android.com/topic/libraries/architecture/workmanager/how-to/intermediate-progress

 

Related posts

Leave a Comment