안드로이드를 공부해보았다면 누구나 최소한 한 번쯤은 사용해봤다는 이 전설의 클래스는, 이름 그대로 비동기적인 작업을 손쉽게 할 수 있도록 도와주는 녀석이다. 핸들러 & 루프 패턴보다 훨씬 코드가 간결한데 UI 쓰레드에 직접 접근할 수 있어 매우 사랑받았던 클래스이다. 코드 자체의 가독성도 뛰어나서 그런지 러닝 커브가 낮아 초심자들에게도 어렵지 않은 API 였다.
Android 11 에서 AsyncTask 는 2019년 11월 8일 6시 54분에 사망 선고를 받았다. 정말 사용하기 쉬워서 많은 이들에게 사랑받았음에도 불구하고 Deprecated 된 이유는 꽤나 합리적일 것이다.
이제 아래와 같은 코드는 추억으로 남게 되었다. 진짜 웃긴게 필자는 이 코드를 보고 왠지 모르게 아련해진다. (?) 안드로이드 앱 개발에 처음 입문했을 적엔 사용하기 편한 AsyncTask 를 워낙 애용했어서 그런지 그 시절이 떠오르는 것만 같아 가슴이 몽글몽글해진다. (갑자기 분위기 감성팔이)
public class MyAsyncTask extends AsyncTask<Void, Integer, Boolean> {
TextView textView;
public MyAsyncTask(TextView textView) {
this.textView = textView;
}
@Override
protected Boolean doInBackground(Void... strings) {
for(int i=0; i< 10000; i++) {
publishProgress(i);
}
return true;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Boolean s) {
super.onPostExecute(s);
}
@Override
protected void onProgressUpdate(Integer... values) {
textView.setText(values[0].toString());
super.onProgressUpdate(values);
}
@Override
protected void onCancelled(Boolean s) {
super.onCancelled(s);
}
}
AsyncTask 는 사용하기 편리하다는 장점이 있었지만, 사실 치명적인 문제점들이 많다.
우선 사용상으로의 단점으로는 다들 알다시피 오직 한 번만 실행되어 재사용이 불가능하다는 점, 종료를 직접 해주지 않으면 종료가 되지 않아 메모리 누수가 발생한다는 점, 항상 UI 쓰레드 상에서 호출해야 한다는 점 등이 있었다.
이 뿐만이 아니라, 시스템상의 결함들도 존재했다.
- 메모리 릭
화면이 회전할 때 액티비티는 종료되었다가 새롭게 생성된다. 이 경우 AsyncTask 가 계속 중첩되어 실행될 수가 있다. 새로 생성된 액티비티는 완전히 다른 인스턴스인데, AsyncTask 가 실행되고 있던 와중에 화면 회전이 일어난 거라면 기존 액티비티조차 메모리에서 해제되지 않는다. 이는OutOfMemory
에러를 뿜을 가능성이 농후하다.
- 순차적으로 실행되기 때문에 속도가 저하될 수 있음
- Fragment 에서 AsyncTask 를 실행할 경우
Fragment 에서 AsyncTask 를 실행하고 뒤로가기 키를 통해 액티비티를 종료하면, Fragment 가 Activity 와 분리되면서getContext()
,getActivity()
등이null
을 반환하게 되어onPostExecute()
에서NullPointerException
이 발생하게 된다.
- 취소 메소드만 있고,
onError()
와 같은 예외처리 메소드 없음
- AsyncTask 병렬 실행 시
doInBackground()
의 실행순서가 보장되지 않음
안드로이드 버전이 올라가면서, 기본 동작이 순차 실행으로 바뀌었기 때문에 플랫폼의 버전에 따라 일관되지 않은 동작을 야기할 수 있는 점이 문제다.
이렇듯 Context Leak 이나 OOM, NPE 이슈는 물론 안드로이드 버전 별로 다른 동작을 할 수도 있는 잠재적 위험군인 AsyncTask
는 Deprecated 될 수 밖에 없었다.
아래는 참고용으로, 안드로이드 오픈소스 헤더 부분에 달려있는 주석이다. 요약하자면 '너무 단점 투성이라 더 이상 유용성을 제공하지 못 한다고 판단하여 Deprecated 하게 되었다' 고 적혀있다.
* <p>AsyncTask was intended to enable proper and easy use of the UI thread. However, the most
* common use case was for integrating into UI, and that would cause Context leaks, missed
* callbacks, or crashes on configuration changes. It also has inconsistent behavior on different
* versions of the platform, swallows exceptions from {@code doInBackground}, and does not provide
* much utility over using {@link Executor}s directly.</p>
소올직히 필자는 얘를 꼭 Deprecated 했어야 했는지 이해가 잘 안된다. 일반적으로 멀티 쓰레딩 프로그래밍 환경 자체가 갖고 있는 위험성과 별반 다를게 없어 그냥 조심조심해서 상황에 맞게 적절히 사용하면 될 것 같은데 말이다. 예전부터 느낀 거지만 안드로이드는 정말 줬다 뺏는 거 너무 잘한다.
뭐 그래도 비동기 처리 방식은 여러 가지가 있기 때문에, 굳이 AsyncTask
에 목 매달 필요는 없다. 어차피 완전히 버려진 거, 아직 사용하고 있는 사람이 있다면 꼭 RxJava 혹은 Coroutine 을 익혀 사용해보길 권장한다. 물론 AsyncTask 보다 러닝커브가 심각한 편이라 처음이라면 버거울 수 있겠지만, 흐름을 이해하는 순간 그리 어렵지 않게 느껴질 것이다.
RxJava 관련 시리즈 : https://velog.io/@haero_kim/series/Reactive-Programming
코루틴 관련 시리즈 : https://velog.io/@haero_kim/series/Kotlin-과-친해지기
적절한 짤 사용으로 재밌게 잘 봤습니다 ^^