티스토리 뷰
내가 구현하려던 건 안드로이드에서 사용자에 대한 인공지능 모델을 생성하고, 그 결과값을 받아오는 것이었다
(안드로이드->web->파이썬)
당연히 어플을 실행하지 않아도, 백그라운드에서 인공지능 모델을 생성하고 학습시켜야 했기 때문에
백그라운드로 로직을 실행할 수 있는 Service 클래스로 구현했다
그런데 파이썬에서 모델을 학습시키기 위해서는 아주 많은 시간이 필요했고.. 안드로이드에서 파이썬의 응답을 기다리는 동안 ui가 멈춰 아무것도 하지 못했고, 응답시간이 넘으면 아예 ANR(Application Not Responding) 오류가 발생했다..
구글링하다가 서비스는 응답시간이 10초가 넘으면 ANR오류가 발생한다는 걸 봤고,
백그라운드에서 ui의 제한없이 실행할 수 있는 클래스들을 찾아봤다
그리고 WorkManager라는 걸 봤다
WorkManager는 오랫동안 죽지 않는 서비스 작업이 가능하기 때문에
보통 오래걸리는 작업들을 WorkManager로 구현한다고 한다.
WorkManager는 한번 수행하는 것과 정기적으로 수행하는 것 두가지 방식이 존재한다.
적용하려고 보니 자바 코드는 많이 안나오고 죄다 코틀린이길래.. 한번 정리해본다!
public class AiWorker extends Worker {
public AiWorker (@NonNull Context appContext, @NonNull WorkerParameters workerParams) {
super(appContext, workerParams);
}
@NonNull
@Override
public Result doWork() {
try {
// 서버를 통해 인공지능 모델을 생성하고, 결괏값을 받아오는 코드
return Result.success();
} catch (Throwable throwable) {
return Result.failure();
}
}
}
Worker를 상속받는 클래스 코드이다.
doWork를 오버라이딩해서 이 부분에 필요한 로직을 구현하면 된다.
구현할 로직에 context가 필요하다면 생성자를 실행할 때 매개변수로 받아온 appContext를 사용하면 된다.
doWork에서 반환할 수 있는 Result는 아래와 같은 세가지 형식으로 존재한다.
- Result.success() : 작업 성공
- Result.failure() : 작업 실패, 다시 시작 x
- Result.retry() : 작업 실패, 다시 시작 o
public class SetActivity extends AppCompatActivity {
private WorkManager workManager;
private WorkRequest aiWorkRequest;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_set);
workManager = WorkManager.getInstance(getApplicationContext());
//스위치 클릭 이벤트
aiSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked == true) {
// Worker 등록
aiWorkRequest =
new PeriodicWorkRequest.Builder(AiWorker.class, 1, TimeUnit.HOURS)
.addTag("WORK_TAG")
.build();
// 실행
workManager.enqueue(aiWorkRequest);
}
else
workManager.cancelAllWorkByTag("WORK_TAG");
}
});
}
}
Worker를 실행하고 취소할 액티비티 클래스의 코드이다.
나는 Switch 버튼을 따로 생성하여 버튼이 활성화되었으면 Worker를 등록하고 실행하며, 비활성화되었으면 Worker를 취소하는 방식으로 구현했다.
PeriodicWorkRequest를 통해 WorkRequest를 생성하고, 생성된 WorkRequest를 WorkManager.enqueue를 통해 실행시키는 방식이다.
WorkManager를 사용하기 전에
WorkManager.getInstance(this)
또는
WorkManager.getInstance(getApplicationContext())
를 통해 WorkManager에 값을 꼭 할당해야 한다.
그리고 1시간 간격으로 Worker를 실행할 거라 PeriodicWorkRequest.Builder()를 사용했지만
일회성으로 실행시킬 거라면 아래의 코드로 Worker를 등록시키면 된다.
aiWorkRequest = new OneTimeWorkRequest.Builder(AiWorker.class).addTag("WORK_TAG").build();
1시간 간격이 아닌 10분 간격으로 실행할거라면 전달인자를 10, TimeUnit.MINUTES 이런식으로 수정해서 응용하자.
나는 SharedPreferences에 switch의 값을 저장했는데, 저장되어 있는 값과 Worker의 값이 맞지않아서
실행되어 있지도 않은 Worker를 종료시키려고 해 오류가 났었다.
그럴 때는 태그를 통해 실행중인 Work가 있는지 체크하는 함수를 추가하여
실행중인 Work가 있을 때만 취소하면 된다.
// 작업 중인 Work가 있다면 취소
if(isWorkScheduled("WORK_TAG"))
workManager.cancelAllWorkByTag("WORK_TAG");
private boolean isWorkScheduled(String tag) {
WorkManager instance = WorkManager.getInstance(getApplicationContext());
ListenableFuture<List<WorkInfo>> statuses = instance.getWorkInfosByTag(tag);
try {
List<WorkInfo> workInfoList = statuses.get();
for (WorkInfo workInfo : workInfoList) {
WorkInfo.State state = workInfo.getState();
Log.d("work state", String.valueOf(state));
if(state == WorkInfo.State.RUNNING | state == WorkInfo.State.ENQUEUED)
return true;
}
return false;
} catch (ExecutionException e) {
e.printStackTrace();
return false;
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
}
참고
https://rosypark.tistory.com/322
https://codechacha.com/ko/android-jetpack-workmanager/
https://stackoverflow.com/questions/51612274/check-if-workmanager-is-scheduled-already
'app > android' 카테고리의 다른 글
[안드로이드/코틀린] 뷰 바인딩 (ViewBinding) (0) | 2023.09.16 |
---|---|
[안드로이드/코틀린] 툴바(Toolbar) 커스텀 방법 (1) | 2023.09.16 |
[안드로이드/android] os 12 이상 스플래시 화면 (0) | 2023.08.02 |
[안드로이드/android] 클래스 안에 브로드캐스트 클래스를 만들었을 때 Manifest 지정하는 법 (0) | 2022.09.19 |
[안드로이드/android] 디바이스 해상도 뷰 고정시키는 방법 (0) | 2022.09.03 |