AsyncTask學習,asynctask

來源:互聯網
上載者:User

AsyncTask學習,asynctask

       記錄下AsyncTask的學習心得:

      AsyncTask類是用來處理非同步請求的,當我們在UI線程(主線程)中執行耗時操作時(從網路擷取資料,從資料庫中擷取資料,或者讀取檔案)會阻塞主線程的運行,導致介面不流暢,卡。這種情況下,我們需要將這些耗時的操作另起一個線程來執行,盡量在主線程中執行UI操作。

     一般做法,我們用Thread+handler去處理耗時操作,和處理善後工作。AsyncTask對Thread和Handler進行了一定的封裝,簡化代碼的編寫,方便我們使用。

      AsyncTask的架構是建立一個Executor來執行ArrayDeque裡的Runnable對象,這個Executor可以是自訂的,也可以是AsyncTask預設實現的SerialExecutor

   

 private static class SerialExecutor implements Executor {        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();        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);            }        }    }

  上面代碼 SerialExecutor實現了Executor中的execute方法,execute中將一個Runnable對象(在本類中是一個FutureTask)排入佇列中,然後判斷當前是否有活動的Runnable,如果沒有的話,就從Queue中take一個,然後將它設為當前活動Runnable, 並且執行這個Runnable。

      現在來看一下則個Runnable做了什麼,首先他執行參數Runnable的run方法,finally去執行sheduleNext()函數,進入這個函數,他拉去隊列的首元素出來當成當前active  Runnable,如果不為空白,就執行他,這樣Executor就順序執行了Queue裡的Runnable對象。當然還可以自訂自己的Executor,比如給隊列賦予權重,權重大的runnable先得到執行

PriorityBlockingQueue<PriorityRunnable> mTasks=new PriorityBlockingQueue<PriorityRunnable>();  private static class PriorityRunnable implements Comparable<PriorityRunnable>{        private Runnable mRunnable;            private  Priority mPriority;            private Integer mSequence;             private AtomicInteger mSequenceGenerator = new AtomicInteger();public  PriorityRunnable(final Runnable r){            mRunnable=new Runnable() {                public void run() {                    try {                        r.run();                    } finally {                        scheduleNext();                    }                }            };                this.setmSequence(getSequenceNumber());               // this.setmPriority();            } public int getSequenceNumber() {        return mSequenceGenerator.incrementAndGet(); }public Runnable getRunnable(){return mRunnable;}public void setmRunnable(Runnable mRunnable) {this.mRunnable = mRunnable;}public Priority getmPriority() {return mPriority;}public void setmPriority(Priority mPriority) {this.mPriority = mPriority;}public Integer getmSequence() {return mSequence;}public void setmSequence(Integer mSequence) {this.mSequence = mSequence;}@Overridepublic int compareTo(PriorityRunnable another) {// TODO Auto-generated method stub Priority left = this.getmPriority();      Priority right = another.getmPriority();      return left == right ?                this.mSequence - another.mSequence :                right.ordinal() - left.ordinal();}        }

  上面的Runnable是自訂的,實現了compareable介面,因為我們用PriorityBlockingQueue來容納Runnable。

      上面說到execute參數裡的Runnable是個FutureTask,FutrueTask是用來執行非同步計算的,它可以封裝一個Callable或者Runnable,並且獲得Callable的計算結果和運行狀態。在AsyncTask中,每一次耗時請求都new一個全新的FutureTask。FutureTask的初始化工作在 new AsyncTask()中。

   public AsyncTask() {        mWorker = new WorkerRunnable<Params, Result>() {            public Result call() throws Exception {                mTaskInvoked.set(true);                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);                //noinspection unchecked                return postResult(doInBackground(mParams));            }        };        mFuture = new FutureTask<Result>(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 occured while executing doInBackground()",                            e.getCause());                } catch (CancellationException e) {                    postResultIfNotInvoked(null);                }            }        };    }

 在AsyncTask初始化中,首先先new 一個WorkRunnable,WorkRunnable實現了Callable<Result>的抽象類別,在call方法體內,首先mTaskInvoked.set(true),這個mTaskInvoked是個AutomicBoolean類型的變數,用來追蹤Task的狀態,噹噹前Task被執行是,將狀態置為true,用來判斷Task是否被取消執行。設定完mTaskInvoked後,又為該Task設定了線程優先順序為Process.THREAD_PRIORITY_BACKGROUND層級,最後返回postResult函數值,進入postResult():

    

  private Result postResult(Result result) {        @SuppressWarnings("unchecked")        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,                new AsyncTaskResult<Result>(this, result));        message.sendToTarget();        return result;    }

  postResult函數參數為doInBackground()函數的執行結果,這裡的doInBackground()函數為AsyncTask的抽象方法,我們需要在自訂的AsyncTask類中去實現這個函數,我們的非同步請求的主要邏輯就在這裡執行。doInBackground()函數的傳回值類型是Result,Result是泛型型別,postResult()函數用來建立一個Message,然後將Result封裝為AsyncTaskResult類型,然後由Message攜帶傳給相應線程的Handler去處理。看一下AsyncTaskResult的資料結構:

 private static class AsyncTaskResult<Data> {        final AsyncTask mTask;        final Data[] mData;        AsyncTaskResult(AsyncTask task, Data... data) {            mTask = task;            mData = data;        }    }

  它有兩個欄位,mTask和mDate, mTask用來儲存自己的引用,mData用來儲存mFuture.get()的值,這個類的用法下面會進一步分析。

new WorkRunnable後接著new FutureTask,並且mWorker作為參數傳入FutureTask作為其擷取資料的來源。FutureTask可以控制線程的執行和得到線程返回的資料,當FutureTask中的Callable執行完後,FutureTask的done函數得以觸發。如果線程在執行前就被Cancle掉,那麼直接執行FutureTask的done函數。FutureTask的狀態是isDone,postResultIfNotInvoked()函數主要是用來處理task被cancel的情況,當Task被cancel時,WorkRunnable的call函數不會執行,此時mTaskInvoked的值為false,然後執行postResult函數,這樣保證了Task不管是被執行還是被cancel都能得到處理。

  private void postResultIfNotInvoked(Result result) {        final boolean wasTaskInvoked = mTaskInvoked.get();        if (!wasTaskInvoked) {            postResult(result);        }    }

 現在來看InternalHandler,這是個Handler的子類,他接受Message傳來的資料,根據msg.what進行判斷以進行不同的邏輯處理。在這裡msg.what有兩種類型,一種是MESSAGE_POST_RESULT,表示處理mWorkRunnable執行完畢發送的message,執行AsyncTask的finish函數。另一種是MESSAGE_POST_PROGRESS,它表示處理mWorkRunnable在更新時發送的message,執行AsyncTask的onProgressUpdate函數.

  finish函數:

private void finish(Result result) {        if (isCancelled()) {            onCancelled(result);        } else {            onPostExecute(result);        }        mStatus = Status.FINISHED;    }

  函數體先判斷當前task是否被cancelled,如果isCancelled,執行onCacelled(result)函數。 

onCancelled函數:

 protected void onCancelled(Result result) {        onCancelled();    }  protected void onCancelled() {    }

  我們可以override這個函數,執行自己的操作。

      如果當前task沒有被cancelled掉,執行onPostExecute(result)函數,同樣我們可以在AsyncTask的子類中override這個函數,這個函數是在UI線程中執行的,可以對UI元素進行操作。

     在這裡出現了一個變數mStatus,看它的定義:

   public enum Status {        /**         * Indicates that the task has not been executed yet.         */        PENDING,        /**         * Indicates that the task is running.         */        RUNNING,        /**         * Indicates that {@link AsyncTask#onPostExecute} has finished.         */        FINISHED,    }

  它有三種狀態,一種是Pendding,注釋明明白白的說道,這是為了標識還未執行的tasks的狀態,Running用來標識正在啟動並執行task的狀態,Finished用來標識已經執行完任務的task的狀態。

     現在再看看到底AsyncTask是怎麼執行的,我們平常用的時候是這樣子的  new MyAsyncTask().executef();

     看看execute函數的廬山真面目吧:

public final AsyncTask<Params, Progress, Result> execute(Params... params) {        return executeOnExecutor(sDefaultExecutor, params);    }

 

 public final AsyncTask<Params, Progress, Result> 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;    }

  首先,函數體先判斷當前task的狀態,如果狀態不是未運行,則拋出異常,如果執行execute()時,當前task處於Running狀態,則拋出“cannot execute task:the task is already running"異常,

     至於為什麼要這麼設計,我的猜想是:

Future 僅在計算完成時才能檢索結果;如果計算尚未完成,則阻塞 get 方法。一旦計算完成,就不能再重新開始或取消計算。

  只要是重新new AsyncTask(),就會獲得一個全新的task。

      回到代碼,如果當前task的狀態是pending,那麼更新它的狀態為Running。然後執行onPreExecute(),函數,它和onPostExecute還有onBackground()函數一樣,都可以在用戶端override,定義自己的商務邏輯。執行完畢後,將從用戶端傳來的可變參數params賦值給mWorkRunnable的mParams成員變數,用以儲存。最後開始調用線程池的execute()函數,之前說過execute()函數將當前task隊列加入queue隊列中,如果沒有正在執行的task,則執行剛建立的task,如果有正在執行任務的task,則先緩衝在隊列中,等待他執行的時機。

     最後看一下線程池:

 private static final ThreadFactory sThreadFactory = new ThreadFactory() {        private final AtomicInteger mCount = new AtomicInteger(1);        public Thread newThread(Runnable r) {            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());        }    }; private static final BlockingQueue<Runnable> sPoolWorkQueue =            new LinkedBlockingQueue<Runnable>(10); public static final Executor THREAD_POOL_EXECUTOR            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

  ThreadFactory類是用來建立線程的,簡單的重寫newThread方法即可定義自己的ThreadFactory。

      ThreadPoolExecutor類有幾個重要的參數,core_pool_size:池中所儲存的線程數,包括空閑線程。,maximum_pool_size:池中允許的最大線程數,keep_alive:當線程數大於核心時,此為終止前多餘的空閑線程等待新任務的最長時間。unit - keepAliveTime 參數的時間單位。workQueue - 執行前用於保持任務的隊列。此隊列僅保持由 execute 方法提交的 Runnable 任務。threadFactory - 執行程式建立新線程時使用的工廠。

    對於AsycnTask的大體分析就到此為止了。談一下自己對AsycnTask的幾點感想:

      1.AsycnTask在SerialExecutor設計時用到了設計模式中的責任鏈模式。

      2.頻繁的擷取網路資料或者進行耗時操作時,new AsycnTask()可能造成大量的臨時對象,影響效能,導致介面”打嗝“性卡頓。

      3.AsycnTask不適合上傳大檔案,一般上傳大檔案用socket上傳,或者斷點上傳,利用RandomAceccsFile來實現。 

      4AsycnTask利用線程池來管理Thread,在效能上比每次單獨建立一個Thread更省記憶體。

      以上有不對的地方,敬請堪正。

   關於ThreadPoolExecutor,點這http://blog.sina.com.cn/s/blog_70bcd7c10101a1w7.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.