마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

한 액티비티에서 AsyncTask3개를 쓰고 있는데 java.util.concurrent.RejectedExecutionException이 발생합니다.

0 추천

안녕하세요 이번에 안드로이드 앱 개발을 시작하게 된 안드로이드 공부한 지 약 2달 다되가는 사람입니다. 아무래도 인터넷으로 여기저기 필요한 소스를 긁어오게 되어 뭔가 좀 억지스럽게 코딩이 된 거 같아서 더 열심히 공부하고 있습니다. 아무튼, 요 며칠 계속 인터넷으로 검색을 하고 관련된 정보를 읽어보고는 있는데 해결이 안되서 글 작성해 봅니다. 현재 제가 개발 중인 앱은 단순하게 보면 1.메인 액티비에서 각 테이블(3개)에 있는 최신 데이터 값을 읽어와서 화면에 뿌리고, 2.각 데이터 값을 터치하면 해당 액티비티가 나와서 관련 세부 정보(시간차트라던지 아니면 리스트뷰로 내역 조회)를 보여주는 기능을 하게끔 작업하고 있습니다. 아무래도 DB에 있는 자료를 가져와야 하고 DB는 약 1초마다 데이터가 업데이트가 되기 때문에 AsyncTask를 사용해야 할 것 같은데, RejectedException이 발생하네요.

질문을 요약하면 1. 메인액티비티에서 각 테이블(3개)를 AsyncTask를 사용하여 뿌려줄 때 RejectedException 뜨는 문제를 해결 할 수 있나요? 그리고 2. 액티비티 간 이동 발생 시 이전 액티비티에서 사용 중이던 AsyncTask를 중지 시킬 수 있는지 알고 싶습니다. 

소스가 너무 길어서 요약해서 보여드리자면

 

불러와야 할 테이블 마다(3개) AsyncTask를 사용했습니다.

doInBackground에서 작업을 진행할 때 

if (isCancelled() != true) {

조건을 걸어서 cancel이 true면 작업을 중지하게 했구요. 각 액티비티로 이동 시 에도 항상 cancel()함수를 호출해서 task.cancel(true);를 줬습니다.  onDestroy()에도 cancel()함수를 호출 하게 했구요

onPostExecute에서 리스트뷰를 사용하는 AsyncTask(2개)는 

protected void onPostExecute(ArrayList<String> list) {

    Adapter.clear();
    Adapter.addAll(list);
    Adapter.notifyDataSetChanged();
    handler.sendEmptyMessageDelayed(0, 60000);
}

이런식으로 했구요

 

나머지 하나는 각 TextView.setText(변수명); 형식으로 했습니다.

 

AsyncTask를 시작하는 건 아래와 같이 핸들러를 이용했는데

    public Handler handler = new Handler() {

        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            Task1 = new AsyncTask1();
            Task2 = new AsyncTask2();
            Task3 = new AsyncTask3();
        
               Task1.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
               Task2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
               Task3.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
        }
    };

 

따로 설정을 해야 하는 부분이 있을까요?


소스에 대한 간단한 소개는 팝업 액티비티를 통해 DB에 등록되어 있는 id,pw 자료를 받아와서 DB에 직접 접근합니다.(DB접근은 간접적으로 해야 한다고 알고 있는데, 부득이 하게 앱에서 직접 접근합니다.)

DB접속이 되면 AsyncTask의 doInBackground안에서 각 테이블 별로 지정된 데이터 row행을 가져와서 onPost에서 데이터를 뿌리는 작업을 합니다.  

 

전체소스를 보여드리고 싶은데 업로드가 안되서 부득이 중요하다고 생각대는 부분만 작성합니다.

 

에러 Log는 다음과 같습니다.

    java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@86a22a7 rejected from java.util.concurrent.ThreadPoolExecutor@fe9d354[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 210]
        at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2049)
        at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:814)
        at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1360)
        at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:616)
        at com.example.jongsunkim.gloen_patrol.MainViewActivity$10.handleMessage(MainViewActivity.java:459)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6780)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)

아직 에러코드 해석도 제대로 안된거 같아서 부끄럽지만 질문 남겨봅니다. 

 

 

쎄쌀 (250 포인트) 님이 2018년 4월 13일 질문

2개의 답변

+1 추천
 
채택된 답변

안녕하세요..

이 문제는 AsyncTask가 사용하는 ThreadPoolExecutor의 Policy가 없어서.. TaskQueue가 꽉 차면 Exception을 내고 크래시되게 되어 있습니다. cancel은 외부에서 asynctask를 종료시키는 건데.. 현실적으로 로직에서 적용하기 어려운 경우가 많습니다. 그래서 이런 경우에 Timeout을 가지면 쉽게 해결할 수 있습니다.

그리고,   if (this.isCancelled()) {
                return null;
이 로직은 외부에서 cancel() 메서드를 호출하지 않는 이상 return null을 타지 않습니다. 그리고, conn.close();는 꼭 finally { conn.close(); }에서 처리해 주는 것이 좋습니다. 

그래서 제가 AsyncTask의 여러 단점을 개선한 라이브러리를 개발했는데요.. 아래 링크에서 확인해 보시고 써 보세요.. 적당히 이용하면 문제를 쉽게 해결하실 수 있을 것 같네요.

https://github.com/mcsong/AdvancedAsyncTask

mcsong (44,040 포인트) 님이 2018년 4월 14일 답변
쎄쌀님이 2018년 4월 17일 채택됨
AdvancedAsyncTask를 사용해 보았습니다. ^^ 관련 질문 드려요
+1 추천
Exception의 원인은 error log 남은 것과 같이 처리되지 않고 큐잉된 task가 너무 많기 때문입니다. (128개)

아마도 activity 전환가 task 정리를 완전히 안하는 걸로 보이는데요.

AsyncTask.cancel() 를 호출한다고 task가 끝나는 것이 아니고, AsyncTask의 background thread에서

cancel 상태를 수시로 체크하다가 cancel 상태가 되면 스스로 자기 thread를 종료해야 합니다.
디자이너정 (42,810 포인트) 님이 2018년 4월 13일 답변
먼저 답변 달아주셔서 감사드립니다. ^^ 그리고 background thread에서 이렇게 했는데 안되더라구요. 어떻게 해야 하나요 ㅠ

   @Override
        protected ArrayList<String> doInBackground(String... params) {
            ArrayList<String> list = new ArrayList<String>();

            if (this.isCancelled()) {
                return null;
            } else {
                String query = "SELECT TOP(4) * FROM 000000 ORDER BY0000 DESC";
                ResultSet reset = null;
                Connection conn = null;
                String server = "jdbc:jtds:sqlserver:/00000000;databaseName=000000";
                try {

                    Class.forName("net.sourceforge.jtds.jdbc.Driver");
                    conn = DriverManager.getConnection(server, idData.toString().trim(), pwData.toString().trim());

                    Statement stmt = conn.createStatement();

                    reset = stmt.executeQuery(query);


                    while (reset.next()) {
                        if (isCancelled()) break;
                        if (reset.getString(12).equals("1")) {
                            sign = "(ON)";
                        } else {
                            sign = "(OFF)";
                        }
                        str = reset.getTimestamp(1).toString().trim() + reset.getInt(2) + " " + reset.getInt(3) + "\t\t\t\t\t\t\t\t\t\t" + reset.getInt(4) + " " + reset.getString(5).trim() + " "
                                + reset.getInt(6) + " " + reset.getInt(7) + " " + reset.getInt(8) + " " + reset.getString(9) + "\n" + reset.getString(13).trim() + " " + sign.trim();
                        list.add(str);
                    }
                    conn.close();

                } catch (Exception e) {
                    Log.w("111Error connection", "" + e.getMessage());
                }
            }
            return list;
        }
...