[Android] AsyncTask 가 떠나간 이유

H43RO·2021년 9월 23일
6

Android 와 친해지기

목록 보기
13/26
post-thumbnail
post-custom-banner

AsyncTask

안드로이드를 공부해보았다면 누구나 최소한 한 번쯤은 사용해봤다는 이 전설의 클래스는, 이름 그대로 비동기적인 작업을 손쉽게 할 수 있도록 도와주는 녀석이다. 핸들러 & 루프 패턴보다 훨씬 코드가 간결한데 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);
    }
}

왜 Deprecated 되었을까

AsyncTask 는 사용하기 편리하다는 장점이 있었지만, 사실 치명적인 문제점들이 많다.

우선 사용상으로의 단점으로는 다들 알다시피 오직 한 번만 실행되어 재사용이 불가능하다는 점, 종료를 직접 해주지 않으면 종료가 되지 않아 메모리 누수가 발생한다는 점, 항상 UI 쓰레드 상에서 호출해야 한다는 점 등이 있었다.

이 뿐만이 아니라, 시스템상의 결함들도 존재했다.

  1. 메모리 릭
    화면이 회전할 때 액티비티는 종료되었다가 새롭게 생성된다. 이 경우 AsyncTask 가 계속 중첩되어 실행될 수가 있다. 새로 생성된 액티비티는 완전히 다른 인스턴스인데, AsyncTask 가 실행되고 있던 와중에 화면 회전이 일어난 거라면 기존 액티비티조차 메모리에서 해제되지 않는다. 이는 OutOfMemory 에러를 뿜을 가능성이 농후하다.
  1. 순차적으로 실행되기 때문에 속도가 저하될 수 있음
  1. Fragment 에서 AsyncTask 를 실행할 경우
    Fragment 에서 AsyncTask 를 실행하고 뒤로가기 키를 통해 액티비티를 종료하면, Fragment 가 Activity 와 분리되면서 getContext(), getActivity() 등이 null 을 반환하게 되어 onPostExecute() 에서 NullPointerException 이 발생하게 된다.
  1. 취소 메소드만 있고, onError() 와 같은 예외처리 메소드 없음
  1. 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>

그러니까 RxJava, Coroutine 으로 넘어오라!

소올직히 필자는 얘를 꼭 Deprecated 했어야 했는지 이해가 잘 안된다. 일반적으로 멀티 쓰레딩 프로그래밍 환경 자체가 갖고 있는 위험성과 별반 다를게 없어 그냥 조심조심해서 상황에 맞게 적절히 사용하면 될 것 같은데 말이다. 예전부터 느낀 거지만 안드로이드는 정말 줬다 뺏는 거 너무 잘한다.

뭐 그래도 비동기 처리 방식은 여러 가지가 있기 때문에, 굳이 AsyncTask 에 목 매달 필요는 없다. 어차피 완전히 버려진 거, 아직 사용하고 있는 사람이 있다면 꼭 RxJava 혹은 Coroutine 을 익혀 사용해보길 권장한다. 물론 AsyncTask 보다 러닝커브가 심각한 편이라 처음이라면 버거울 수 있겠지만, 흐름을 이해하는 순간 그리 어렵지 않게 느껴질 것이다.

도움이 될 만한 자료

RxJava 관련 시리즈 : https://velog.io/@haero_kim/series/Reactive-Programming

코루틴 관련 시리즈 : https://velog.io/@haero_kim/series/Kotlin-과-친해지기

profile
어려울수록 기본에 미치고 열광하라
post-custom-banner

1개의 댓글

comment-user-thumbnail
2022년 7월 15일

적절한 짤 사용으로 재밌게 잘 봤습니다 ^^

답글 달기