Android 에서 백그라운드 서비스, 브로드캐스트등을을 하기위해서 Thread, Async, JobScheduler,알람매니져등이 있지만 최근에는 WorkManager를 이용하기를 권장한다.
WorkManager는 주로 백단 에서 작업해야하는 것들에 대해 사용하는데, 앱이 종료되거나 다시시작되더 WorkManager가 작업을 다시 시작해주기 때문에 안정적인 서비스 개시가 가능하도록 해준다.
그렇다고 모든 서비스, 쓰레드에 WorkManager를 쓰라는건 아니고 , 즉각실행해야 될거는 coroutines(또는 rxjava등) , 정확한 시간에 가동되야 하는거는 AlarmManager, 기기가 다시 시작되어도 실행되어야 할 백그라운드 작업등은 WorkManager를 쓰라는 얘기다.
WorkerManager 는 API레벨과 앱상태같은 요건에 근거해서 내부적으로 적절한 방법으로 백그라운작업을 어떻게 할지 자동으로 선택하다.
https://developer.android.com/guide/background
WorkerManger에는 다음과 같은 개념이 있다.
- Worker : Abstract클래스로서 이 클래스를 확장받은 일할놈(?)이 어떤일을 할지를 구체적으로 기술한다. 자신의 일이 잘 끝났다 못끝냈다는 Success, Failure, Retry 3개의 값을 반환함으로서 workmanager가 전반적인 work를 관리할수 있도록 한다.
- WorkRequest : 작업할놈이 결정되었다면 이 클래스를 통해서 작업요청을 해야하는데, OneTimeWorkrequest, PeriodicWorkRequest가 있다.
- WorkerManager : WorkRequest를 받은 Worker가 를 큐에 넣거나 빼는 등의 전체 work 에 대한 작업상태를 모니터링 한다.
1. 앱이 백그라운드에서 log 를 출력하기.
https://github.com/sugoigroup/android_workmanager_example/commit/d9662cc3e3fa38c01079a7bac25003b5b42fd46f
1 2 3 4 5 6 7 | dependencies { ... def work_version = "2.5.0" implementation "androidx.work:work-runtime:$work_version" } |
2. 샘플워커 클래스를 만들고, workRequest->workerManager에 등록
https://github.com/sugoigroup/android_workmanager_example/commit/d9662cc3e3fa38c01079a7bac25003b5b42fd46f
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import android.util.Log; import androidx.annotation.NonNull; import androidx.work.Worker; import androidx.work.WorkerParameters; public class UpladWorker extends Worker { private int count = 0; public UpladWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } @NonNull @Override public Result doWork() { while (count < 10) { count++; try { Log.i("worker", "now background" + count); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } return Result.success(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 충전중일때만 worker 가 실행되도록 제한을 건다. Constraints constraints = new Constraints.Builder() .setRequiresCharging(true) .build(); // 이번 한번만 일하도록 한다. OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(UpladWorker.class) .setConstraints(constraints) .build(); // 매니져에게 일할거라고 등록한다. WorkManager.getInstance(this).enqueue(oneTimeWorkRequest); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | public class UpladWorker extends Worker { private int count = 0; public UpladWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } @NonNull @Override public Result doWork() { while (count < 10) { count++; try { // Log.i("worker", "now background" + count); showNotification("worker", "now background" + count); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } return Result.success(); } private void showNotification(String task, String desc) { NotificationManager manager = (NotificationManager) getApplicationContext().getSystemService( Context.NOTIFICATION_SERVICE); String channelId = "my_channel"; String channelName = "my_name"; // Oreo 부터는 노티에 알림하려면 채널이 있어야 한다. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT); manager.createNotificationChannel(channel); } NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), channelId) .setContentTitle(task) .setContentText(desc) .setSmallIcon(R.mipmap.ic_launcher); manager.notify(1, builder.build()); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | ------------- UploadWorker.java public Result doWork() { while (count < 10) { if (isStopped()) { continue; } count++; try { // Log.i("worker", "now background" + count); showNotification("worker", "now background" + count); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } return Result.success(); } ------------------- MainActivity.java // 충전중일때만 worker 가 실행되도록 제한을 건다. Constraints constraints = new Constraints.Builder() .setRequiresCharging(true) .build(); // 이번 한번만 일하도록 한다. OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(UpladWorker.class) .setConstraints(constraints) .addTag(CANCEL_ME) .build(); // 매니져에게 일할거라고 등록한다. WorkManager.getInstance(this).enqueue(oneTimeWorkRequest); |
때문에 이를 막기 위해서 beginUniqueWork라는 함수로 workManager를 가동하면 KEEP, REPLACE, APPEND 라는 3개의 옵션을 통해 같은 이름을 가진 작업 queue가 있을때, 나중에 들어온 같은 이름의 작업을 무시할건지, 중간에 교체할건지, 순서대로 진행할건지를 결정할수 있도록 해준다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv = (TextView) findViewById(R.id.hello); tv.setOnClickListener(e -> { startQueue(); }); startQueue(); } private void startQueue() { // 충전중일때만 worker 가 실행되도록 제한을 건다. Constraints constraints = new Constraints.Builder() .setRequiresCharging(true) .build(); // 이번 한번만 일하도록 한다. final OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(UpladWorker.class) .setConstraints(constraints) .addTag(CANCEL_ME) .build(); // 매니져에게 일할거라고 등록한다. // WorkManager.getInstance(this) .beginUniqueWork("iamUnique", ExistingWorkPolicy.APPEND_OR_REPLACE, oneTimeWorkRequest) .enqueue(); } |
REPLACE : B로 새롭게 시작한다.(A가 어디까지 진행된지 상관없다)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | ------ LogWorker.java public class LogWorker extends Worker { public LogWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } @NonNull @Override public Result doWork() { Log.i("worker", "Wow All Done!"); return Result.success(); } } ------- MainActivity.java // 이번 한번만 일하도록 한다. final OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(UpladWorker.class) .setConstraints(constraints) .addTag(CANCEL_ME) .build(); // 이번 한번만 일하도록 한다. final OneTimeWorkRequest logWorkerRequest = new OneTimeWorkRequest.Builder(LogWorker.class) .setConstraints(constraints) .addTag(CANCEL_ME) .build(); // 매니져에게 일할거라고 등록한다. // then 으로 순차적인 workRequest를 실행할수 있다. WorkManager.getInstance(this) .beginUniqueWork("iamUnique", ExistingWorkPolicy.APPEND_OR_REPLACE, oneTimeWorkRequest) .then(logWorkerRequest) .enqueue(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | ------ LogWorker.java public class LogWorker extends Worker { public LogWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } @NonNull @Override public Result doWork() { String detector = getInputData().getString(KEY_DETECTOR); Log.i("worker", detector + " detector is founding a key!"); //내보낼 값을 정하자 Data outputData = new Data.Builder() .putString(FOUND_OUT_KEY, "elzzup") .build(); return Result.success(outputData); } ------- MainActivity.java private void startQueue() { // 충전중일때만 worker 가 실행되도록 제한을 건다. Constraints constraints = new Constraints.Builder() .setRequiresCharging(true) .build(); // 이번 한번만 일하도록 한다. final OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(UpladWorker.class) .setConstraints(constraints) .addTag(CANCEL_ME) .build(); //보낼값을 정하자. Data whoIsTheDetector = new Data.Builder().putString(KEY_DETECTOR, "kim").build(); // 이번 한번만 일하도록 한다. final OneTimeWorkRequest logWorkerRequest = new OneTimeWorkRequest.Builder(LogWorker.class) .setConstraints(constraints) .setInputData(whoIsTheDetector) .addTag(CANCEL_ME) .build(); // 매니져에게 일할거라고 등록한다. // then 으로 순차적인 workRequest를 실행할수 있다. WorkManager.getInstance(this) .beginUniqueWork("iamUnique", ExistingWorkPolicy.APPEND_OR_REPLACE, oneTimeWorkRequest) .then(logWorkerRequest) .enqueue(); // 결과를 foundKeyByWorkerGuy(logWorkerRequest.getId()); } private void foundKeyByWorkerGuy(UUID workerUUID) { LiveData<WorkInfo> lf = WorkManager.getInstance(this).getWorkInfoByIdLiveData(workerUUID); lf.observe(this, workInfo -> { if (workInfo.getOutputData().getString(FOUND_OUT_KEY) != null) { tv.setText("The Key is " + workInfo.getOutputData().getString(FOUND_OUT_KEY)); } else { tv.setText("Finding the key"); } }); } |
0 comments:
댓글 쓰기