亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

android異步任務設計思詳解(AsyncTask)

2020-04-11 11:53:26
字體:
來源:轉載
供稿:網友

這里說有設計思想是我根據查看Android源代碼提煉出來的代碼邏輯,所以不會跟Google工程師的原始設計思想100%符合(也有可能是0%),但是本文一定可以幫助你理解AsyncTask,也可能有一些你以前沒有發現的內容。

大家都知道,Android的主線程(又叫UI線程,線程ID為1)有一些限制策略,使得主線程有些事做不了,比如訪問網絡就不允許,否則就是報,但在2.3之后的版本,你可以通過添加以下代碼更改其限制策略,從而強制使得主線程可以訪問網絡:

復制代碼 代碼如下:

if (android.os.Build.VERSION.SDK_INT > 9) {
    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
}

不過StrictMode是一個開發工具主要用于偵測主線程中的磁盤和網絡訪問,而不是讓你做這“壞”事,其實Android這樣限制是有好處的,強制讓開發者重視用戶體驗,一個反面例子是Windows,主線程里什么都可以做,一些很懶的開發者就把所有任務放到主線程里,使得線程經常好卡,比如編輯器UE或Notepad++打開了一個網絡上(如samba服務器)的文件,如果突然網絡中斷了,那你的整個編輯器都卡住,要等好久才會有反應,不過我不確定那是不是因為主線程里訪問了網絡,不過Windows經常因為這個原因卡。還有一個正面例子是iOS,極其注意響應速度,所以當有用戶輸入事件時,其內核都有相應的調度,從而優先響應用戶操作。

還是回到正題,就是因為主線程的這些限制使開發者不得不寫多個線程,當然,你也可以不用AsyncTask,不過你不用也避免不了多線程,如果你不用,就是可能要用Handler和Thread了,我想很多人初學的時候就是那么干的,包括我,因為那時很有可能還沒有發現有這個類,于是就經常寫Handler和Thread的了,寫著寫著就發現有一些代碼是相同的,你寫的Handler和Thread匿名類肯定是重復代碼,如下:

復制代碼 代碼如下:

[final Handler handler = new Handler() {
    public void handleMessage(Message msg) {
     System.out.println("The hard have done!");
     // ... front end code
    }
};
new Thread() {
    public void run() {
        doHardWork();
        handler.sendEmptyMessage(0);
    }

    private void doHardWork() {
        // ... back end code
    }
}.start();

你可能想到要復用這些代碼,當然,你可以通過Copy的方式來復用這段代碼,用的時候只要在省略號處寫入你的代碼就可以了,但更好的復用是將其用一個類封裝起來,好吧,那我們就簡單的封裝一下吧,于是,就變成了這樣:

復制代碼 代碼如下:

public class HandlerAndThread {
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            System.out.println("The hard have done!");
            //...
        }
    };

    public void doInBackground() {
        new Thread() {
            public void run() {
                doHardWork();
                handler.sendEmptyMessage(0);
            }


            private void doHardWork() {
                // ...
            }
        };
    }      
}

這樣好像還不行,因為無法告訴后臺線程做什么事,做完了也不知道通知,要復用還是得Copy代碼,我們可以加兩個方法,一個在前臺調用一個在后臺調用,只要定義一個新類就可以實現復用,于是代碼變成這樣:

復制代碼 代碼如下:

public class HandlerAndThread {
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            System.out.println("The hard have done!");
            runInFrontend();    // added
        }
    };

    public void doInBackground() {
        new Thread() {
            public void run() {
                doHardWork();
                handler.sendEmptyMessage(0);
            }

            private void doHardWork() {
                runInBackend();    //added
            }
        };
    }

    //added
    protected void runInBackend() {
    }

    //added
    protected void runInFrontend() {
    }
}

一個可復用的類就出爐了,我們寫一個子類,并用一個Activity來調用一下吧:

復制代碼 代碼如下:

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        new SubHandlerAndThread().doInBackground();
    }

    class SubHandlerAndThread extends HandlerAndThread {
        protected void runInBackend() {
            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        protected void runInFrontend() {
            System.out.println("Task has been done");
        }
    }
}

這樣是不是比直接寫Thread和Handler簡潔了許多呢,這里我是用sleep來模似長時間事務的,如果在真實的環境中,我們可能是要下載,如果是下載,我們可能希望傳入個下載地址的參數到后臺線程,來讓他按我們的需要下載,我們給加doInBackground方法加一個參數,于是HandlerAndThread類的代碼就變成這樣:

復制代碼 代碼如下:

public class HandlerAndThread {
    ...

    public void doInBackground(final String url) { // added url
        new Thread() {
            public void run() {
                doHardWork();
                handler.sendEmptyMessage(0);
            }

            private void doHardWork() {
                runInBackend(url);  // added url
            }
        };
    }

    protected void runInBackend(String url) { // added url
    }

    ...
}

而調用類的代碼變成這樣:

復制代碼 代碼如下:

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        String url = "http://path/to/file";
        new SubHandlerAndThread().doInBackground(url);   //added url
    }

    class SubHandlerAndThread extends HandlerAndThread {
        @Override
        protected void runInBackend(String url) {    // added url
            System.out.println("Start download from url:" + url);
            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        protected void runInFrontend() {
            System.out.println("finish download");
        }
    }
}

假如是下一個文件呢,我們是不是加一個進度更新的方法呢,于是又變成這樣:

復制代碼 代碼如下:

public class HandlerAndThread {
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {        // added
            case 0:
                runInFrontend();
                break;

            case 1:
                runInFrontendProgress(msg.arg1);
                break;
            }
        }
    };

    ...

    final protected void publishProgress(int progress) {    // added
        handler.obtainMessage(1, progress, 0);
    }

    protected void runInFrontendProgress(int progress) {    // added
    }
}

public class MainActivity extends Activity {
    ...

    class SubHandlerAndThread extends HandlerAndThread {
        @Override
        protected void runInBackend(String url) {
            System.out.println("Start download from url:" + url);
            for (int i = 0; i < 10; ++i) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrack();
                }
                publishProgress(i*10);    // added
            }
        }

        ...

        @Override
        protected void runInFrontendProgress(int progress) { // added
            System.out.println("Progress: " + progress);
        }
    }
}

你可能已經沒有耐心一版一版的進化了,那我就跳躍一下吧,一次多加幾條需要:一、我們下載完了可能要得到文件的路徑,所以我們給runInFrontend方法加一個輸入參數filePath表示路徑;二、把子類必須實現的方法改成抽象方法,類也改成抽象方法;我把代碼中的一些方法名改一下,使其更好理解,把doInBackground改為execute,把runInFrontend改為onPostExecute,把runInFrontendProgress改為onProgressUpdate。最終版如下:

復制代碼 代碼如下:

public abstract class HandlerAndThread {
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 0:
                onPostExecute((String) msg.obj);
                break;


            case 1:
                onProgressUpdate(msg.arg1);
                break;
            }
        }
    };

    public void doInBackground(final String url) {
        new Thread() {
            public void run() {
                String result = runInBackend(url);
                handler.obtainMessage(0, result);
            }


        };
    }

    final protected void publishProgress(int progress) {
        handler.obtainMessage(1, progress, 0);
    }


    abstract protected String runInBackend(String url);
    protected void onPostExecute(String filePath) { }
    protected void onProgressUpdate(int progress) {    }
}
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        String url = "http://path/to/file";
        new SubHandlerAndThread().doInBackground(url);
    }

    class SubHandlerAndThread extends HandlerAndThread {
        @Override
        protected String runInBackend(String url) {
            System.out.println("Start download from url:" + url);
            for (int i = 0; i < 10; ++i) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                publishProgress(i*10);
            }

            return "/path/to/file";
        }

        @Override
        protected void onPostExecute(String filePath) {
            System.out.println("Download finished");
        }

        @Override
        protected void onProgressUpdate(int progress) {
            System.out.println("Progress: " + progress);
        }
    }
}

這是不是跟Android的AsyncTask很像呢,我想Google就是由于這種需求做出這個類的,Android官網是這樣描述AsyncTask的:

This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

意思是這個類使得:不使用Thread和Handler,就可以在后臺執行操作然后在發布結果到UI線程。其實他內部的實現就是封裝了Thread和Handler,所以你就不必要直接用這兩個低層類了,但他的目的也是代碼復用,他的實現跟我們上面寫的類也差不多。主要有這幾點不同:一、AsyncTask使用了線程池而不是單個線程去執行后臺任務,該線程池是整個進程共用的,是因為他的線程池對象是一個靜態成員變量,這一點很多人搞錯,誤以為AsyncTask越來創建的線程越多,這不是絕對正確的,因為線程池會根據負載動態調整的,而且有最大值和空閑超時,AsyncTask的配置是最小5,最大128,空閑超時1秒,當然你也可以配置成線程數根據任務數線程遞增,關于線程池,可參考這里,后續我會在博客寫文章討論Java線程池;二、AsyncTask的輸入和輸出參數使用了泛型;三、AsyncTask支持中斷當前任務。

現在知道了AsyncTask的設計思想了吧,是不是很簡單呢,所以建議童鞋們去看一下它的源碼,反正我寫代碼時有查看源碼的習慣,因為我會好奇它是如何實現的,看源碼有很多好處,如可以學到好的API設計思想,軟件架構。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产日韩欧美一二三区| 中文字幕日本欧美| 自拍偷拍亚洲区| 久久久久国产精品一区| 国产丝袜一区视频在线观看| 国产精品一区二区3区| 国产精品jizz在线观看麻豆| 久久久欧美精品| 久久久午夜视频| 精品中文字幕在线| 精品久久香蕉国产线看观看亚洲| 日韩欧美中文第一页| 日韩中文在线视频| 亚洲欧美日韩图片| 欧美激情在线观看视频| 国产视频观看一区| 亚洲第一网中文字幕| 久久久影视精品| 久久精品这里热有精品| 欧美日韩一区二区三区在线免费观看| 成人h视频在线| 亚洲自拍偷拍色片视频| 亚洲精品美女久久久久| www.99久久热国产日韩欧美.com| 国产精品日韩专区| 欧美韩日一区二区| 中文在线资源观看视频网站免费不卡| 国产成人在线亚洲欧美| 亚洲国产精久久久久久久| 日韩高清a**址| 国产精品视频区1| 国产日本欧美一区二区三区| 久久国内精品一国内精品| 欧美有码在线观看| 欧美日韩国产二区| 国产精品九九久久久久久久| 久久久国产视频| 欧美日韩国产一区中文午夜| www.久久草.com| 亚洲精品456在线播放狼人| 国产成人aa精品一区在线播放| 韩日欧美一区二区| 中文字幕av一区二区三区谷原希美| 精品国产成人在线| 欧洲成人免费aa| 日韩在线视频线视频免费网站| 91亚洲国产成人久久精品网站| 欧美成人免费小视频| 欧美电影免费观看大全| 91精品啪aⅴ在线观看国产| 中文字幕欧美在线| 91成人国产在线观看| 亚洲男人天堂视频| 国产精品久久久久77777| 色诱女教师一区二区三区| 亚洲人成五月天| 亚洲系列中文字幕| 自拍视频国产精品| 成人av番号网| 亚洲国产成人精品电影| 亚洲精品一区中文字幕乱码| 欧美另类高清videos| 久久精品国产欧美激情| 国产日韩精品入口| 日韩av电影免费观看高清| 日韩免费观看网站| 亚洲精品成人久久| 久久男人的天堂| 日韩av电影手机在线观看| 国产亚洲成精品久久| 丝袜情趣国产精品| 中文字幕亚洲欧美| 97在线免费观看视频| 国产精品久久久久av| 91po在线观看91精品国产性色| 久久久亚洲成人| 日韩中文娱乐网| 欧美自拍视频在线| 国产aⅴ夜夜欢一区二区三区| 日韩在线观看精品| 91国产视频在线播放| 欧美丰满少妇xxxxx做受| 国产一区香蕉久久| 亚洲国产精品va在看黑人| 国产视频精品免费播放| 亚洲国产成人精品一区二区| 亚洲免费成人av电影| 91亚洲国产精品| 欧美日本亚洲视频| 91tv亚洲精品香蕉国产一区7ujn| 久久久久久久999精品视频| 欧美激情视频一区二区三区不卡| 日韩中文字幕在线看| 亚洲欧美福利视频| 欧美高清不卡在线| 九九精品视频在线观看| 日韩av男人的天堂| 奇米成人av国产一区二区三区| 在线播放国产精品| 亚洲日本成人女熟在线观看| 欧美激情三级免费| 国产免费一区二区三区在线观看| 精品国产一区二区三区久久久| 欧美成人免费小视频| 色综合久久88| 欧美成人午夜激情视频| 久久久久久久999精品视频| 伊人激情综合网| 久久久久久久999精品视频| 日韩av在线看| 日韩免费观看高清| 欧美性videos高清精品| 久久免费精品视频| 日韩欧美成人区| 久久久国产精品视频| 亚洲在线www| 欧美专区日韩视频| 国产视频久久久久久久| 国产精选久久久久久| 亚洲一区二区三区四区在线播放| 亚洲精品视频中文字幕| 亚洲精品国产精品国自产观看浪潮| 国产一区二中文字幕在线看| 欧美激情精品久久久久久黑人| 最新的欧美黄色| 国产a∨精品一区二区三区不卡| 欧美国产日韩一区二区三区| 国产精品视频区| 国产精品福利小视频| 亚洲a在线播放| 精品日韩视频在线观看| 姬川优奈aav一区二区| 亚洲精品自拍第一页| 欧美午夜电影在线| 欧美大片免费看| 欧美大全免费观看电视剧大泉洋| 亚洲电影天堂av| 成人av电影天堂| 国产精品色视频| 国产精品日日摸夜夜添夜夜av| 日韩性xxxx爱| 色系列之999| 高清在线视频日韩欧美| 国内伊人久久久久久网站视频| 色综合91久久精品中文字幕| 777国产偷窥盗摄精品视频| 亚洲欧美精品一区二区| 成人444kkkk在线观看| 色婷婷av一区二区三区在线观看| 久久久噜噜噜久久中文字免| 伊人久久免费视频| 亚洲国产精品一区二区久| 亚洲国产成人av在线| 色偷偷91综合久久噜噜| 久久999免费视频| 精品国产拍在线观看| 狠狠躁18三区二区一区| 国产成+人+综合+亚洲欧美丁香花| 亚洲精品狠狠操| 国产精品视频一| 97高清免费视频| 欧美在线一区二区三区四| 日本成人在线视频网址| 国产精品久久久久不卡|