AsyncTask 원리 및 부족

7033 단어

AsyncTask 이해:

  • AsyncTask는Handler와 스레드 풀의 봉인입니다.
  • 네트워크 요청 등 시간이 걸리는 온라인 스레드에서 완성하고handler를 통해 메인 스레드에 UI 업데이트를 완료합니다.
  • 스레드 탱크를 사용하는 주요 원인은 불필요한 스레드 창설과 폐기 비용을 피하기 때문이다.

  • AsyncTask 의 부족함:

  • 메모리 유출 문제
  • AsyncTask 객체는 마스터 스레드에서 만들어야 합니다. 이것은 내부 sHandler와 관련이 있습니다. 아래에 설명되어 있습니다.
  • AsyncTask 객체의 excute 메서드는 주 스레드에서 호출되어야 합니다.
  • AsyncTask 개체 하나에 excute 메서드를 한 번만 호출할 수 있음
  • Params:doInBackground 메소드의 매개변수 유형,Progress: AsyncTask에서 수행하는 백그라운드 작업의 진행 유형;Result: 백그라운드 작업의 반환 결과 유형입니다.
    public abstract class AsyncTask {
      ......
    }
    

    AsyncTask를 초기화하면 mWorker 및 mFuture가 초기화됩니다.mWorker는 WorkRunnable 클래스의 객체에서 파생됩니다.WorkRunnable는 Callable 인터페이스를 구현하는 추상적인 클래스입니다.call 방법에서 먼저 mTaskInvoked를true로 설정하면 현재 작업이 호출되었음을 표시하고 루트의 우선 순위를 설정합니다.AsyncTask 대상의doInBackground 방법을 호출하여 우리가 정의한 백엔드 작업을 시작하고 되돌아오는 결과를 Result에 저장합니다.마지막으로 작업 결과를 postResult 방법에 전달합니다.이를 통해 알 수 있듯이 실제 AsyncTask의 구성원인 mWorker는 AyncTask가 최종적으로 수행할 작업(즉 mWorker의call 방법)을 포함하고 있다.
    public AsyncTask() {
            mWorker = new WorkerRunnable() {
                public Result call() throws Exception {
                    mTaskInvoked.set(true);
                    Result result = null;
                    try {
                        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                        //noinspection unchecked
                        result = doInBackground(mParams);
                        Binder.flushPendingCommands();
                    } catch (Throwable tr) {
                        mCancelled.set(true);
                        throw tr;
                    } finally {
                        postResult(result);
                    }
                    return result;
                }
            };
    
            mFuture = new FutureTask(mWorker) {
                @Override
                protected void done() {
                    try {
                        postResultIfNotInvoked(get());
                    } catch (InterruptedException e) {
                        android.util.Log.w(LOG_TAG, e);
                    } catch (ExecutionException e) {
                        throw new RuntimeException("An error occurred while executing doInBackground()",
                                e.getCause());
                    } catch (CancellationException e) {
                        postResultIfNotInvoked(null);
                    }
                }
            };
        }
    

    위의 분석을 통해 알 수 있듯이 mWorker에서 정의한call 방법이 실행될 때doInBackground가 실행되고 우리가 정의한 백엔드 작업도 진정으로 시작된다.그럼 이 콜 방법은 언제 호출될까요?우리는 층층이 봉인된 것을 볼 수 있다. 실제로는 mFuture 대상이 콜 방법을 봉인했다. mFuture 대상이 AsyncTask에 포함된 스레드 탱크에 제출되면 콜 방법이 호출되고 우리가 정의한 백엔드 작업도 실행되기 시작한다.다음은 mFuture가execute () -> execute OnExecutor () 를 호출할 때 스레드 탱크에 제출되어 실행된 것을 보여 줍니다.exec.execute(mFuture);
        @MainThread
        public final AsyncTask execute(Params... params) {
            return executeOnExecutor(sDefaultExecutor, params);
        }
        // AsyncTask execute 。 execute() , RUNNING FINISHED 
        @MainThread
        public final AsyncTask executeOnExecutor(Executor exec,
                Params... params) {
            if (mStatus != Status.PENDING) {
                switch (mStatus) {
                    case RUNNING:
                        throw new IllegalStateException("Cannot execute task:"
                                + " the task is already running.");
                    case FINISHED:
                        throw new IllegalStateException("Cannot execute task:"
                                + " the task has already been executed "
                                + "(a task can be executed only once)");
                }
            }
    
            mStatus = Status.RUNNING;
    
            onPreExecute();
    
            mWorker.mParams = params;
            exec.execute(mFuture);
    
            return this;
        }
    

    상기 코드의 sDefaultExecutor는 SerialExecutor 클래스입니다.완료될 Runnable 작업을 mTasks의 대기열 끝에 눌러서 mTask의 맨 위에서 Runnable을 꺼내THREADPOOL_EXECUTOR 스레드 풀 실행
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    

    sDefaultExecutor는 SERIALEXECUTOR 및 static final 코스메틱으로 세션에 SERIAL 하나만 초기화됨을 나타냅니다.EXECUTOR, 다음 코드를 통해 SerialExecutor 스레드 탱크의 역할이 작업의 줄을 서는 데 사용되고 작업을THREAD 에 맡긴다는 것을 알 수 있습니다.POOL_EXECUTOR 실행
        private static class SerialExecutor implements Executor {
            final ArrayDeque mTasks = new ArrayDeque();
            Runnable mActive;
    
            public synchronized void execute(final Runnable r) {
                mTasks.offer(new Runnable() {
                    public void run() {
                        try {
                            r.run();
                        } finally {
                            scheduleNext();
                        }
                    }
                });
                if (mActive == null) {
                    scheduleNext();
                }
            }
    
            protected synchronized void scheduleNext() {
                if ((mActive = mTasks.poll()) != null) {
                    THREAD_POOL_EXECUTOR.execute(mActive);
                }
            }
        }
    

    mWorker의 콜 방법은 온라인 스레드에서 실행되며, DoInBackground가 실행된 후에 결과를 sHandler에 전달합니다.
       private Result postResult(Result result) {
            @SuppressWarnings("unchecked")
            Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                    new AsyncTaskResult(this, result));
            message.sendToTarget();
            return result;
        }
    

    Handler 대상을 만들 때 현재 라인의 Looper가 필요하다는 것을 알고 있습니다. 나중에 sHandler를 통해 실행 환경을 백엔드 라인에서 메인 라인으로 전환할 수 있도록 (즉 메인 라인에서handleMessage 방법을 실행할 수 있도록) 메인 라인의 Looper를 사용해야 합니다.또한 sHandler는 정적 변수로 정적 변수는 클래스가 불러올 때 초기화되며, 변형은 주 라인에서 AsyncTask 클래스를 불러와야 합니다.
       private static class InternalHandler extends Handler {
            public InternalHandler() {
                super(Looper.getMainLooper());
            }
    
            @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
            @Override
            public void handleMessage(Message msg) {
                AsyncTaskResult> result = (AsyncTaskResult>) msg.obj;
                switch (msg.what) {
                    case MESSAGE_POST_RESULT:
                        // There is only one result
                        result.mTask.finish(result.mData[0]);
                        break;
                    case MESSAGE_POST_PROGRESS:
                        result.mTask.onProgressUpdate(result.mData);
                        break;
                }
            }
        }
    

    MESSAGEPOST_RESULT에서finish 방법을 사용합니다. 만약 작업이 onPostExecute 방법을 사용하지 않았다면, 작업이 onCancelled 방법을 사용하지 않았다면.
       private void finish(Result result) {
            if (isCancelled()) {
                onCancelled(result);
            } else {
                onPostExecute(result);
            }
            mStatus = Status.FINISHED;
        }
    

    좋은 웹페이지 즐겨찾기