ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 안드로이드 AsyncTask의 doInBackground 호출 안됨
    Android 2020. 7. 19. 22:40

     

    AsyncTask를 사용하던 중 doInBackground()가 호출이 되지 않는 상황을 겪게 되었다. 복잡한 앱이 아닌 단순 클릭킹 프로토타입을 만들던 중에 일어난 일이라 황당할 따름이었다. 대략 화면을 터치하면 화면이 전환되며 4개의 작은 동영상이 동시에 재생되는 너무나 간단한 작업이라 아무 생각 없이 AsyncTask 4개를 돌렸는데 1개만 재생이 되는 현상이 나타났다.

     

    찾아보니 문제의 원인은 AsyncTask가 이름의 뉘앙스와는 어울리지 않게 기본적으로 싱글 스레드에서 실행이 되었기 때문이었다. 영상 재생 후 다른 작업을 같이 하려고 무한루프에서 2초 정도씩 슬립하며 추가 작업을 수행했는데 이게 문제였다. 이 무한 루프가 끝날 때까지 다른 AsyncTask들이 끝날 기약없는 대기를 타고 있었던 것이다.

     

    찾아보니 Honeycomb 이전과 이후로 동작이 다르다고 하는데, AsyncTask의 동작에 대한 자세한 정보는 이 문서에서 찾을 수 있다. 기본 동작이 그럴거면 그냥 TaskPool이라고 부르는게 나을텐데, 구글의 작명 센스에 다시 한 번 감탄하고 말았다. Deprecated되긴 했지만 여전히 나와 비슷한 문제를 겪는 사람들이 있을 것 같아서 문제의 원인과 해결 방법을 남겨본다.

     

    문제 발생을 위해 다음과 같은 간단한 상황을 만들어 보았다. AsyncTask를 상속한 MyTask는 doInBackground()에서 무한 루프가 돌아가는데, 2초씩 기다리며 주기적인 작업을 수행한다. 이 경우 다수의 MyTask를 만들면 하나만 수행이 되는 현상을 관찰할 수 있다. 다음은 문제를 발생시키는 코드이다.

     

        protected TextView tvResult1;
        protected TextView tvResult2;
    
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // TextView to show the progress of MyTask
            tvResult1 = (TextView) findViewById(R.id.TV_RESULT1);
            tvResult2 = (TextView) findViewById(R.id.TV_RESULT2);
    
            // Create 2 different MyTasks
            MyTask task1 = new MyTask("task1", tvResult1);
            MyTask task2 = new MyTask("task2", tvResult2);
    
            // Run the MyTasks
            task1.execute();
            task2.execute();
        }
    
        protected class MyTask extends AsyncTask<Void, Void, Void> {
            protected String name;
            protected TextView tv;
    
            public MyTask(String name, TextView tv) {
                // Store the information
                this.name = name;
                this.tv = tv;
            }
    
            @Override
            protected Void doInBackground(Void... voids) {
                boolean stop = false;
    
                while (!stop) {
                    try {
                        // Sleep for 2 secs
                        Thread.sleep(2000);
                        
                        // Show MyTask is running
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                tv.setText(name + " is running.");
                            }
                        });
    
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                    }
                }
    
                return null;
            }
        }

     

    위 코드에서 task1과 task2를 각각 만들어 실행시키면 2초 후에 각각의 작업이 수행되고 있다는 메시지가 TV_RESULT1과 TV_RESULT2에 나타날 것이라고 기대하겠지만, 실제로는 다음과 같이 task1만 실행이 된다.

     

     

    문제의 해결은 간단한데, AsyncTask.execute()대신 AsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)를 호출하여 각각의 AsyncTask를 서로 다른 스레드에서 실행시키는 것이다. 위 예제의 경우 다음과 같이 실행 코드를 변경해 준다.

     

        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // TextView to show the progress of MyTask
            tvResult1 = (TextView) findViewById(R.id.TV_RESULT1);
            tvResult2 = (TextView) findViewById(R.id.TV_RESULT2);
    
            // Create 2 different MyTasks
            MyTask task1 = new MyTask("task1", tvResult1);
            MyTask task2 = new MyTask("task2", tvResult2);
    
            // Run the MyTasks
            task1.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
            task2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        }

     

    변경된 코드를 실행하면 다음과 같이 2초 후에 두 Task가 정상적으로 실행되는 것을 확인할 수 있다.

     

     

     

    Fin.

    반응형

    댓글

Calvin's Memo