Android中AsyncTask分析--你所不註意的坑

来源:http://www.cnblogs.com/jycboy/archive/2016/11/14/asynctask_1.html
-Advertisement-
Play Games

AsyncTask,是android提供的輕量級的非同步類,可以直接繼承AsyncTask,在類中實現非同步操作,並提供介面反饋當前非同步執行的程度(可以通過介面實現UI進度更新),最後反饋執行的結果給UI主線程. 本文不分析AsyncTask的使用,它的使用教程網上一搜一大堆,本文主要分析它的內部邏輯和 ...


AsyncTask,是android提供的輕量級的非同步類,可以直接繼承AsyncTask,在類中實現非同步操作,提供介面反饋當前非同步執行的程度(可以通過介面實現UI進度更新),最後反饋執行的結果給UI主線程.

本文不分析AsyncTask的使用,它的使用教程網上一搜一大堆,本文主要分析它的內部邏輯和實現,它是怎麼實現非同步的,它是怎麼處理多個任務的,是併發麽??

一、線程任務的調度

在AsyncTask內部會創建一個類相關的線程池來管理要運行的任務,也就就是說當你調用了AsyncTask的execute()後,AsyncTask會把任務交給線程池,由線程池來管理創建Thread和運行Therad。

在Android4.0版本中它內部是有兩個線程池:SerialExecutor和ThreadPoolExecutor,SerialExecutor是串列的,ThreadPoolExecutor是併發的,而預設的就是SerialExecutor的,所以你一個程式中如果用了好幾個AsyncTask你就得註意了:不要忘了換成併發的線程池執行。下麵演示一下,穿行的調度

1.一個簡單的例子:可以看出他是一個個執行的   代碼如下:
public class AsyncTaskDemoActivity extends Activity {  
    private static int ID = 0;  
    private static final int TASK_COUNT = 9;  
    private static ExecutorService SINGLE_TASK_EXECUTOR;  
    private static ExecutorService LIMITED_TASK_EXECUTOR;  
    private static ExecutorService FULL_TASK_EXECUTOR;  
      
    static {  
        SINGLE_TASK_EXECUTOR = (ExecutorService) Executors.newSingleThreadExecutor();  
        LIMITED_TASK_EXECUTOR = (ExecutorService) Executors.newFixedThreadPool(7);  
        FULL_TASK_EXECUTOR = (ExecutorService) Executors.newCachedThreadPool();  
    };  
      
    @Override  
    public void onCreate(Bundle icicle) {  
        super.onCreate(icicle);  
        setContentView(R.layout.asynctask_demo_activity);  
        String title = "AsyncTask of API " + VERSION.SDK_INT;  
        setTitle(title);  
        final ListView taskList = (ListView) findViewById(R.id.task_list);  
        taskList.setAdapter(new AsyncTaskAdapter(getApplication(), TASK_COUNT));  
    }  
      
    private class AsyncTaskAdapter extends BaseAdapter {  
        private Context mContext;  
        private LayoutInflater mFactory;  
        private int mTaskCount;  
        List<SimpleAsyncTask> mTaskList;  
          
        public AsyncTaskAdapter(Context context, int taskCount) {  
            mContext = context;  
            mFactory = LayoutInflater.from(mContext);  
            mTaskCount = taskCount;  
            mTaskList = new ArrayList<SimpleAsyncTask>(taskCount);  
        }  
          
        @Override  
        public int getCount() {  
            return mTaskCount;  
        }  
  
        @Override  
        public Object getItem(int position) {  
            return mTaskList.get(position);  
        }  
  
        @Override  
        public long getItemId(int position) {  
            return position;  
        }  
  
        @Override  
        public View getView(int position, View convertView, ViewGroup parent) {  
            if (convertView == null) {  
                convertView = mFactory.inflate(R.layout.asynctask_demo_item, null);  
                SimpleAsyncTask task = new SimpleAsyncTask((TaskItem) convertView);  
                /* 
                 * It only supports five tasks at most. More tasks will be scheduled only after 
                 * first five finish. In all, the pool size of AsyncTask is 5, at any time it only 
                 * has 5 threads running. 
                 */  
                task.execute();
                // use AsyncTask#SERIAL_EXECUTOR is the same to #execute();  
//                task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);  
                // use AsyncTask#THREAD_POOL_EXECUTOR is the same to older version #execute() (less than API 11)  
                // but different from newer version of #execute()  
//                task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);  
                // one by one, same to newer version of #execute()  
//                task.executeOnExecutor(SINGLE_TASK_EXECUTOR);  
                // execute tasks at some limit which can be customized  
//                task.executeOnExecutor(LIMITED_TASK_EXECUTOR);  
                // no limit to thread pool size, all tasks run simultaneously  
                //task.executeOnExecutor(FULL_TASK_EXECUTOR);  
                  
                mTaskList.add(task);  
            }  
            return convertView;  
        }  
    }  
      
    private class SimpleAsyncTask extends AsyncTask<Void, Integer, Void> {  
        private TaskItem mTaskItem;  
        private String mName;  
          
        public SimpleAsyncTask(TaskItem item) {  
            mTaskItem = item;  
            mName = "Task #" + String.valueOf(++ID);  
        }  
          
        @Override  
        protected Void doInBackground(Void... params) {  
            int prog = 1;  
            while (prog < 101) {  
                SystemClock.sleep(100);  
                publishProgress(prog);  
                prog++;  
            }  
            return null;  
        }  
          
        @Override  
        protected void onPostExecute(Void result) {  
        }  
          
        @Override  
        protected void onPreExecute() {  
            mTaskItem.setTitle(mName);  
        }  
          
        @Override  
        protected void onProgressUpdate(Integer... values) {  
            mTaskItem.setProgress(values[0]);  
        }  
    }  
}  
  
class TaskItem extends LinearLayout {  
    private TextView mTitle;  
    private ProgressBar mProgress;  
      
    public TaskItem(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
  
    public TaskItem(Context context) {  
        super(context);  
    }  
      
    public void setTitle(String title) {  
        if (mTitle == null) {  
            mTitle = (TextView) findViewById(R.id.task_name);  
        }  
        mTitle.setText(title);  
    }  
      
    public void setProgress(int prog) {  
        if (mProgress == null) {  
            mProgress = (ProgressBar) findViewById(R.id.task_progress);  
        }  
        mProgress.setProgress(prog);  
    }  
}  

  2.你想要的併發執行

上面的情況肯定不是你想要的,你想要的應該是這種情況: 只需要修改一行代碼:
 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);  

  當然也可以換成你自己的線程池。

二、源碼分析

  1.成員變數:

定義了需要用到的成員,可以根據名字就能知道乾什麼的

//生產線程的工廠
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);

    /**
     * 可以平行的執行任務!就是併發的
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

    /**
     * 線性執行的執行器
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    //內部交互的handler
    private static final InternalHandler sHandler = new InternalHandler();
    //預設的Executor
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

定義了需要用到的成員,可以根據名字就能知道乾什麼的,另外註意都是static修飾的:

  第二行的sThreadFactory是創建線程的;

  第十行的sPoolWorkQueue阻塞隊列,存放任務的;

 第十七行是 THREAD_POOL_EXECUTOR是線程池,這個是併發執行的線程池;

 第26行是線性調度的線程池,SERIAL_EXECUTOR,執行完一個才會執行下一個;

 第28行是一個內部封裝的Handler:InternalHandler

 第30行可以看出他預設的是線性調度的線程池, Executor sDefaultExecutor = SERIAL_EXECUTOR,看到這裡你應該註意一個問題,如果程式里有好多個AsyncTask,它們就是線性調度的,這肯定可你預想的不一樣,所以你別忘了換成併發的執行器。

2.內部Handler的使用:

2.1 自定義的InternalHandler(內部handler)

private static class InternalHandler extends Handler {
        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        //接受message的處理,可以看到根據狀態選擇是完成了,還是更新著
        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;
            }
        }
    }

  在上邊handleMessage中,根據msg進行判斷,是完成了還是在更新;

     任務完成調用finish方法,在其中執行你定義的onPostExecute方法,

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

  3.構造方法

public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                return postResult(doInBackground(mParams));
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    final Result result = get();

                    postResultIfNotInvoked(result);
                } 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);
                } catch (Throwable t) {
                    throw new RuntimeException("An error occured while executing "
                            + "doInBackground()", t);
                }
            }
        };
    }

構造方法中其實隱藏的信息很多,WorkerRunnable和FutureTask;

其中WorkerRunnable繼承了Callable介面,應該是用於在未來某個線程的回調介面,在其中執行了ostResult(doInBackground(mParams));調用doInBackground並用postResult方法,把result發送到主線程。

FutureTask你看類的介紹是說控制任務的,控制任務的開始、取消等等,在這不細究,跟本文關係不大,而且我也沒看明白。

第17行有一個方法:postResultIfNotInvoked(result);根據名字可以看出來是如果沒有調用把把結果post出去,所以他應該是處理取消的任務的。

構造方法就分析到這,下一步就是execute():

看下postResult方法:代碼很少也很簡單,就是把msg發送給handler:

//用shandler把設置message,併發送。
private Result postResult(Result result) {
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

3.1 按照執行過程流程,實例化完,就可以調用execute():

//Params... 就相當於一個數組,是傳給doInBackground的參數
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;
    } 

代碼邏輯很清晰,沒有幾行:

20行:修改了狀態;

21行:準備工作;

24行:設置參數;

25行:線程池調用執行,註意參數是mFuture。 

3.2 execute的執行邏輯

就以它定義SerialExecutor為例:

/*An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.*/
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);
            }
        }
    }

 可以看到它的方法都是用synchronized關鍵字修飾的,而其讓他的介紹里也說了在同一時間只有一個任務在執行。

   在裡邊調用r.run()方法,執行完了在調用下一個。按先後順序每次只運行一個

 

三、AsyncTask中非同步的處理邏輯

沒有忘了前邊構造方法中的postResult(doInBackground(mParams))和postResultIfNotInvoked(result);方法吧,如果忘了翻前邊去看。這兩個方法把執行成功的和失敗的任務都包含了。

所以我們可以設想一下它是怎麼執行的:

1.在executeOnExecutor方法中給變數賦值

2.用執行器Executor另起線程執行任務

3.在Executor中一些複雜的邏輯,用FutureTask進行判斷任務是否被取消,如果沒有就調用回調介面call()方法,

4.在call()方法中調用了postResult(doInBackground(mParams));

5.postResult發送給主線程的Handler進行後續處理。

 

看的時候可以畫下圖,會很清晰,基本邏輯就這樣,好AsyncTask就分析到這,歡迎糾錯。。。

 轉發請註明出處,原文地址:http://www.cnblogs.com/jycboy/p/asynctask_1.html

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、SVG繪圖總結: ①方法一:已有svg文件,<img src="x.svg"> 方法二:<body><svg></svg></body> ②繪製矩形:<rect x="" y="" width="" height=""></rect> ③繪製圓形:<circle cx="" cy="" r="" ...
  • jquery美化選擇器實例有:邊框、下劃線、 伸縮 、滑動、 覆蓋、 旋轉、 彈出層選擇 、環形效果。 線上預覽 實例代碼 <body class="demo-1"> <div class="container"> <header class="codrops-header"> <h1> 自定義選擇 ...
  • //創建新的tcp伺服器var net = require('net');var chatServer = net.createServer()chatServer.on('connection',function(client){ client.write('Hi\n'); client.writ ...
  • 先上效果圖: 獲取手機已安裝的App列表利用Android系統API就可以辦到,這裡為什麼要引入RxJava?現在我們假設一下有下麵幾個需求: 1、我們不需要所有的App,只需要用戶安裝的第三方App,即過濾到系統App; 2、我們自定義一個AppInfo類,該類中保存了App_Icon、App_N ...
  • 今天學到了這個Loader,淺談一下自己的看法: 1.定義 Loader是一個載入器,可以用來它訪問數據,可以看做訪問數據的機器(好比挖掘機)。裝再器從android3.0開始引進,它使得在activity或fragment中非同步載入數據變得簡單。 具有如下特別: 1)它們對每個Activity和F ...
  • 轉載請標明出處:http://www.cnblogs.com/zhaoyanjun/p/6062880.html 本文出自 "【趙彥軍的博客】" 前言 以前我在 "【Android Handler、Loop 的簡單使用】" 介紹了子線程和子線程之間的通信。 很明顯的一點就是,我們要在子線程中調用Lo ...
  • 通常我們在處理耗時任務時候都會通過新建線程來處理,當任務處理完後通過Handler將結果發送回主線程。比如下麵示例: 那麼,我們能不能通過Handler從主線程發送消息給子線程呢?答案是肯定的,需要用到Looper.prepare()和Looper.loop()。如下麵的代碼: ...
  • 一、概要: 本文主要以Android的渲染機制、UI優化、多線程的處理、緩存處理、電量優化以及代碼規範等幾方面來簡述Android的性能優化 二、渲染機制的優化: 大多數用戶感知到的卡頓等性能問題的最主要根源都是因為渲染性能。 Android系統每隔16ms發出VSYNC信號,觸發對UI進行渲染, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...