Android--線程詳解

什麼是線程?

線程或者線程執行本質上就是一串命令(也是程序代碼),而後咱們把它發送給操做系統執行。html

Multithreaded_process

通常來講,咱們的CPU在任什麼時候候一個核只能處理一個線程。多核處理器(目前大多數Android設備已經都是多核)顧名思義,就是能夠同時處理多線程(通俗地講就是能夠同時處理多件事)。java

多核處理與單核多任務處理的實質

上面我說的是通常狀況,並非全部的描述都是必定正確的。由於單核也能夠用多任務模擬出多線程。android

每一個運行在線程中的任務均可以分解成多條指令,並且這些指令不用同時執行。因此,單核設備能夠首先切換到線程1去執行指令1A,而後切換到線程2去執行指令2A,接着返回到線程1再去執行1B、1C、1D,而後繼續切換到線程2,執行2B、2C等等,以此類推。git

這個線程之間的切換十分迅速,以致於在單核的設備中也會發生。幾乎全部的線程都在相同的時間內進行任務處理。其實,這都是由於速度太快形成的假象,就像電影《黑客帝國》裏的特工Brown同樣,能夠變幻出不少的頭和手。github

agent_brown_dodging_bullets

接下來咱們來看一些代碼。數據庫

Java核內心的線程

在Java中,若是要想作平行任務處理的話,會在Runnable裏面執行你的代碼。能夠繼承Thread類,或者實現Runnable接口:設計模式

// Version 1
public class IAmAThread extends Thread {
    public IAmAThread() {
        super("IAmAThread");
    }
 
    @Override
    public void run() {
        
// your code (sequence of instructions)
    }
}
// to execute this sequence of instructions in a separate thread.
new IAmAThread().start();
 
// Version 2
public class IAmARunnable implements Runnable {
    @Override
    public void run() {
        
// your code (sequence of instructions)
    }
}
// to execute this sequence of instructions in a separate thread.
IAmARunnable myRunnable = new IAmARunnable();
new Thread(myRunnable).start();

這兩個方法基本上是同樣的。第一個版本是建立一個Thread類,第二個版本是須要建立一個Runnable對象,而後也須要一個Thread類來調用它。緩存

第二個版是一般建議使用的方法。這也是一個很大的主題了,超過了本文的範圍,之後會再作討論。安全

Android上的線程

不管什麼時候啓動APP,全部的組件都會運行在一個單獨的線程中(默認的)——叫作主線程。這個線程主要用於處理UI的操做併爲視圖組件和小部件分發事件等,所以主線程也被稱做UI線程。服務器

若是你在UI線程中運行一個耗時操做,那麼UI就會被鎖住,直到這個耗時操做結束。對於用戶體驗來講,這是很是糟糕的!這也就是爲何咱們要理解Android上的線程機制了。理解這些機制就能夠把一些複雜的工做移動到其它的線程中去執行。若是你在UI線程中運行一個耗時的任務,那麼頗有可能會發生ANR(應用無響應),這樣用戶就會很快地結束掉你的APP。

Android和Java同樣,它支持使用Java裏面的Thread類來進行一步任務處理。因此能夠輕鬆地像上面Java的例子同樣來使用Android上的線程,不過那好像仍是有點困難。

爲何在Android上使用標準Java的線程會困難呢?

其實平行任務處理沒有想象中的那麼簡單,你必須在多線程中保證併發,就像偉大的Tim Bray說的那樣:ordinary humans can’t do concurrency at scale (or really at all) …

特別對於Android來講,如下這些功能就略顯臃腫:

  1. 異步對於UI線程來講是一個主要的PITA(若是你須要在後臺線程中向主線程更新界面,那麼你就會用到)。
  2. 若是屏幕方向或者屏幕配置改變的話,就會出現一些更加奇怪的現象。由於改變屏幕方向,會引發Activity重建(因此後臺線程就須要去改變被銷燬的Activity的狀態了,而若是後臺線程不是在UI線程之上的話,那狀況會更加複雜,緣由如條件1)。
  3. 對於線程池來講,沒有默認的處理方式。
  4. 取消線程操做須要自定義代碼實現。

那麼在Android上怎麼進行任務併發處理呢?

你可能聽過一些Android上一些常見的名詞:

一、Handler
這就是咱們今天要討論的詳細主題。

二、AsyncTask
使用AsyncTask是在Android上操做線程最簡單的方式,也是最容易出錯的方式。

三、IntentService
這種方式須要寫更多的代碼,可是這是把耗時任務移動到後臺的很好的方式,也是我最喜歡的方式。配上使用一個EventBus機制的框架如Otto,這樣的話實現IntentService就很是簡單了。

四、Loader
關於處理異步任務,還有不少事情須要作,好比從數據庫或者內容提供者那裏處理一些數據。

五、Service
若是你曾經使用過Service的話,你應該知道這裏會有一點誤區,其中一個常見的誤解就是服務是運行在後臺線程的。其實不是!看似運行在後臺是由於它們不與UI組件關聯,可是它們(默認)是運行在UI線程上的……因此默認運行在UI線程上,甚至在上面沒有UI部件。

若是想要把服務運行在後臺線程中,那麼必須自定義一個線程,而後把操做代碼都運行在那個線程中(與上面提到的方法很相似)。事實上你應該使用IntentService實現,可是這不是本文討論的主題。

Android上的Handler

如下是從 Android developer documentation for Handlers:中摘選的一段話:

> A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread/message queue of the thread that is creating it — from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

爲了更好地瞭解這個概念,也許你須要去看看什麼是Message Queues。

消息隊列

在線程裏基本都有一個叫作「消息隊列」的東西,它負責線程間通訊。這是一種設計模式,全部控制指令或者內容在線程間傳遞。

消息隊列如同它的名字那樣,對於線程來講,它就是一個指令隊列。這裏咱們還能夠作一些更酷的事:

  • 定時消息和線程在某個時間點才執行。
  • 須要在另外一個線程中去添加入隊動做,而不是在本線程中。

注意:這裏說的「消息」和Runnable對象、指令隊列的概念是同樣的。

回到Android上的Handler……若是你仔細閱讀的話,能夠看到文檔是這樣說的:

> A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue.

因此Handler可讓你給線程隊列發消息:

> Each Handler instance is associated with a single thread and that thread’s message queue.

一個Handler對象只能和一個線程關聯:

> When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it

因此一個Handler到底和哪一個線程關聯呢?就是創造它的線程。

> — from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.、

在咱們瞭解這些知識後,請繼續看……

小貼士: 這裏有幾點可能你還不知道。每一個線程都和一個Handler類實例綁定,並且能夠和別的線程一塊兒運行,相互通訊。

還有一個小建議(若是用過AsyncTask的話),AsyncTask內部也是使用Handler進行處理的,只是否是運行在UI線程而已,它會提供一個channel來和UI線程通訊,使用postExecute方法便可實現。

這還挺酷的,那怎麼建立Handler呢?

有兩種方式:

  1. 使用默認的構造方法:new Handler()。
  2. 使用帶參的構造方法,參數是一個Runnable對象或者回調對象。

Handler裏面有什麼實用的API嗎?

請記住:

  • Handler只是簡單往消息隊列中發送消息而已(或者使用post方式)
  • 它們有更方便的方法能夠幫助與UI線程通訊。

若是你如今看看Handler的API,能夠清楚看到這幾個方法:

  1. post
  2. postDelayed
  3. postAtTime

代碼示例

這裏的代碼都是很基礎的,不過你能夠好好看看註釋。

示例1:使用Handler的「post」方法

public class TestActivity extends Activity {
 
// ...
// all standard stuff
 
@Override
public void onCreate(Bundle savedInstanceState) {
 
    
// ...
    
// all standard stuff
 
    
// we're creating a new handler here
    
// and we're in the UI Thread (default)
    
// so this Handler is associated with the UI thread
    Handler mHandler = new Handler();
 
    
// I want to start doing something really long
    
// which means I should run the fella in another thread.
    
// I do that by sending a message - in the form of another runnable object
 
    
// But first, I'm going to create a Runnable object or a message for this
    Runnable mRunnableOnSeparateThread = new Runnable() {
        @Override
        public void run () {
 
            
// do some long operation
            longOperation();
 
            
// After mRunnableOnSeparateThread is done with it's job,
            
// I need to tell the user that i'm done
            
// which means I need to send a message back to the UI thread
 
            
// who do we know that's associated with the UI thread?
            mHandler.post(new Runnable(){
                @Override
                public void run(){
                    
// do some UI related thing
                    
// like update a progress bar or TextView
                    
// ....
                }
            });
 
 
        }
    };
 
    
// Cool but I've not executed the mRunnableOnSeparateThread yet
    
// I've only defined the message to be sent
    
// When I execute it though, I want it to be in a different thread
    
// that was the whole point.
 
    new Thread(mRunnableOnSeparateThread).start();
}
 
}

若是根本就沒有Handler對象,回調post方法會比較難辦。

示例2:使用postDelayed方法

近期本站新介紹的特性中,我每次都要模擬EditText的自動完成功能,每次文字改變後都會觸發一個API的調用,從服務器中檢索數據。

我想減小APP調用API的次數,因此決定使用Handler的postDelayed方法來實現這個功能。

本例不針對平行處理,只是關於Handler給消息隊列發送消息還有安排消息在將來的某一點執行等。

// the below code is inside a TextWatcher
// which implements the onTextChanged method
// I've simplified it to only highlight the parts we're
// interested in
 
private long lastChange = 0;
 
@Override
public void onTextChanged(final CharSequence chars,
                          int start, int before, int count) {
 
        
// The handler is spawned from the UI thread
        new Handler().postDelayed(
 
            
// argument 1 for postDelated = message to be sent
            new Runnable() {
                @Override
                public void run() {
 
                    if (noChangeInText_InTheLastFewSeconds()) {
                        searchAndPopulateListView(chars.toString());  
// logic
                    }
                }
            },
 
            
// argument 2 for postDelated = delay before execution
            300);
 
        lastChange = System.currentTimeMillis();
}
 
 
private boolean noChangeInText_InTheLastFewSeconds() {
    return System.currentTimeMillis() - lastChange >= 300
}
最後我就把「postAtTime」這個方法做爲聯繫留給讀者們了,掌握Handler了嗎?若是是的話,那麼能夠盡情使用線程了。

1. Android進程

    在瞭解Android線程以前得先了解一下Android的進程。當一個程序第一次啓動的時候,Android會啓動一個LINUX進程和一個主線程。默認的狀況下,全部該程序的組件都將在該進程和線程中運行。同時,Android會爲每一個應用程序分配一個單獨的LINUX用戶。Android會盡可能保留一個正在運行進程,只在內存資源出現不足時,Android會嘗試中止一些進程從而釋放足夠的資源給其餘新的進程使用, 也能保證用戶正在訪問的當前進程有足夠的資源去及時地響應用戶的事件。Android會根據進程中運行的組件類別以及組件的狀態來判斷該進程的重要性,Android會首先中止那些不重要的進程。按照重要性從高到低一共有五個級別:

  • 前臺進程
    前臺進程是用戶當前正在使用的進程。只有一些前臺進程能夠在任什麼時候候都存在。他們是最後一個被結束的,當內存低到根本連他們都不能運行的時候。通常來講, 在這種狀況下,設備會進行內存調度,停止一些前臺進程來保持對用戶交互的響應。
  • 可見進程
    可見進程不包含前臺的組件可是會在屏幕上顯示一個可見的進程是的重要程度很高,除非前臺進程須要獲取它的資源,否則不會被停止。
  • 服務進程
    運行着一個經過startService() 方法啓動的service,這個service不屬於上面提到的2種更高重要性的。service所在的進程雖然對用戶不是直接可見的,可是他們執行了用戶很是關注的任務(好比播放mp3,從網絡下載數據)。只要前臺進程和可見進程有足夠的內存,系統不會回收他們。
  • 後臺進程
    運行着一個對用戶不可見的activity(調用過 onStop() 方法).這些進程對用戶體驗沒有直接的影響,能夠在服務進程、可見進程、前臺進 程須要內存的時候回收。一般,系統中會有不少不可見進程在運行,他們被保存在LRU (least recently used) 列表中,以便內存不足的時候被第一時間回收。若是一個activity正 確的執行了它的生命週期,關閉這個進程對於用戶體驗沒有太大的影響。
  • 空進程
    未運行任何程序組件。運行這些進程的惟一緣由是做爲一個緩存,縮短下次程序須要從新使用的啓動時間。系統常常停止這些進程,這樣能夠調節程序緩存和系統緩存的平衡。

    Android 對進程的重要性評級的時候,選取它最高的級別。另外,當被另外的一個進程依賴的時候,某個進程的級別可能會增高。一個爲其餘進程服務的進程永遠不會比被服務的進程重要級低。由於服務進程比後臺activity進程重要級高,所以一個要進行耗時工做的activity最好啓動一個service來作這個工做,而不是開啓一個子進程――特別是這個操做須要的時間比activity存在的時間還要長的時候。例如,在後臺播放音樂,向網上上傳攝像頭拍到的圖片,使用service可使進程最少獲取到「服務進程」級別的重要級,而不用考慮activity目前是什麼狀態。broadcast receivers作費時的工做的時候,也應該啓用一個服務而不是開一個線程。

 

2. 單線程模型

    當一個程序第一次啓動時,Android會同時啓動一個對應的主線程(Main Thread),主線程主要負責處理與UI相關的事件,如用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事件,並把相關的事件分發到對應的組件進行處理。因此主線程一般又被叫作UI線程在開發Android應用時必須遵照單線程模型的原則: Android UI操做並非線程安全的而且這些操做必須在UI線程中執行。

 

2.1 子線程更新UI

    Android的UI是單線程(Single-threaded)的。爲了不拖住GUI,一些較費時的對象應該交給獨立的線程去執行。若是幕後的線程來執行UI對象,Android就會發出錯誤訊息 CalledFromWrongThreadException。之後遇到這樣的異常拋出時就要知道怎麼回事了!

 

2.2 Message Queue

     在單線程模型下,爲了解決相似的問題,Android設計了一個Message Queue(消息隊列), 線程間能夠經過該Message Queue並結合Handler和Looper組件進行信息交換。下面將對它們進行分別介紹:

     1. Message
    Message消息,理解爲線程間交流的信息,處理數據後臺線程須要更新UI,則發送Message內含一些數據給UI線程。
 
     2. Handler
    Handler處理者,是Message的主要處理者,負責Message的發送,Message內容的執行處理。後臺線程就是經過傳進來的Handler對象引用來 sendMessage(Message)。而使用Handler,須要implement 該類的  handleMessage(Message)方法,它是處理這些Message的操做內容,例如Update UI。一般須要子類化Handler來實現handleMessage方法。
 
     3. Message Queue
    Message Queue消息隊列,用來存放經過Handler發佈的消息,按照先進先出執行。
    每一個message queue都會有一個對應的Handler。Handler會向message queue經過兩種方法發送消息:sendMessage或post。這兩種消息都會插在message queue隊尾並按先進先出執行。但經過這兩種方法發送的消息執行的方式略有不一樣:經過sendMessage發送的是一個message對象,會被Handler的handleMessage()函數處理;而經過post方法發送的是一個runnable對象,則會本身執行。
 
     4. Looper
    Looper是每條線程裏的Message Queue的管家。Android沒有Global的Message Queue,而Android會自動替主線程(UI線程)創建Message Queue,但在子線程裏並無創建Message Queue。因此調用Looper.getMainLooper()獲得的主線程的Looper不爲NULL,但調用Looper.myLooper()獲得當前線程的Looper就有可能爲NULL。

    對於子線程使用Looper,API Doc提供了正確的使用方法:

class LooperThread extends Thread { 
    public Handler mHandler; 
 
    public void run() { 
        Looper.prepare(); //建立本線程的Looper並建立一個MessageQueue
 
        mHandler = new Handler() { 
            public void handleMessage(Message msg) { 
                // process incoming messages here 
            } 
        }; 
   
        Looper.loop(); //開始運行Looper,監聽Message Queue 
    } 
} 

這個Message機制的大概流程:

    1. 在Looper.loop()方法運行開始後,循環地按照接收順序取出Message Queue裏面的非NULL的Message。

    2. 一開始Message Queue裏面的Message都是NULL的。當Handler.sendMessage(Message)到Message Queue,該函數裏面設置了那個Message對象的target屬性是當前的Handler對象。隨後Looper取出了那個Message,則調用該Message的target指向的Hander的dispatchMessage函數對Message進行處理。

    在dispatchMessage方法裏,如何處理Message則由用戶指定,三個判斷,優先級從高到低:

    1) Message裏面的Callback,一個實現了Runnable接口的對象,其中run函數作處理工做;

    2) Handler裏面的mCallback指向的一個實現了Callback接口的對象,由其handleMessage進行處理;

    3) 處理消息Handler對象對應的類繼承並實現了其中handleMessage函數,經過這個實現的handleMessage函數處理消息。

    因而可知,咱們實現的handleMessage方法是優先級最低的!

    3. Handler處理完該Message (update UI) 後,Looper則設置該Message爲NULL,以便回收!

 
    在網上有不少文章講述主線程和其餘子線程如何交互,傳送信息,最終誰來執行處理信息之類的,我的理解是最簡單的方法——判斷Handler對象裏面的Looper對象是屬於哪條線程的,則由該線程來執行!
    1. 當Handler對象的構造函數的參數爲空,則爲當前所在線程的Looper;
    2. Looper.getMainLooper()獲得的是主線程的Looper對象,Looper.myLooper()獲得的是當前線程的Looper對象。

如今來看一個例子,模擬從網絡獲取數據,加載到ListView的過程:

public class ListProgressDemo extends ListActivity { 
 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.listprogress); 
 
        ((Button) findViewById(R.id.load_Handler)).setOnClickListener(new View.OnClickListener(){ 
 
            @Override 
            public void onClick(View view) { 
                data = null; 
                data = new ArrayList<String>(); 
 
                adapter = null; 
 
                showDialog(PROGRESS_DIALOG); 
                new ProgressThread(handler, data).start(); 
            } 
        }); 
    } 
 
    @Override 
    protected Dialog onCreateDialog(int id) { 
        switch(id) { 
        case PROGRESS_DIALOG: 
                 return ProgressDialog.show(this, "",  
                   "Loading. Please wait...", true); 
 
        default: return null; 
        } 
    } 
 
    private class ProgressThread extends Thread { 
 
        private Handler handler; 
        private ArrayList<String> data; 
 
        public ProgressThread(Handler handler, ArrayList<String> data) { 
            this.handler = handler; 
            this.data = data; 
        } 
 
        @Override 
        public void run() { 
            for (int i=0; i<8; i++) { 
                data.add("ListItem"); //後臺數據處理
                try { 
                    Thread.sleep(100); 
                }catch(InterruptedException e) { 
                     
                    Message msg = handler.obtainMessage(); 
                    Bundle b = new Bundle(); 
                    b.putInt("state", STATE_ERROR); 
                    msg.setData(b); 
                    handler.sendMessage(msg);  
                     
                } 
            } 
            Message msg = handler.obtainMessage(); 
            Bundle b = new Bundle(); 
            b.putInt("state", STATE_FINISH); 
            msg.setData(b); 
            handler.sendMessage(msg); 
        } 
         
    } 
 
    // 此處甚至能夠不須要設置Looper,由於Handler默認就使用當前線程的Looper
    private final Handler handler = new Handler(Looper.getMainLooper()) {
 
        public void handleMessage(Message msg) { // 處理Message,更新ListView
            int state = msg.getData().getInt("state"); 
            switch(state){ 
                case STATE_FINISH: 
                    dismissDialog(PROGRESS_DIALOG); 
                    Toast.makeText(getApplicationContext(), 
                            "加載完成!", 
                            Toast.LENGTH_LONG) 
                         .show(); 
 
                    adapter = new ArrayAdapter<String>(getApplicationContext(), 
                            android.R.layout.simple_list_item_1, 
                            data ); 
                             
                    setListAdapter(adapter); 
 
                    break; 
 
                case STATE_ERROR: 
                   dismissDialog(PROGRESS_DIALOG); 
                   Toast.makeText(getApplicationContext(), 
                           "處理過程發生錯誤!", 
                           Toast.LENGTH_LONG) 
                        .show(); 
 
                   adapter = new ArrayAdapter<String>(getApplicationContext(), 
                           android.R.layout.simple_list_item_1, 
                           data ); 
                            
                      setListAdapter(adapter); 
 
                      break; 
 
               default: 
 
            } 
        } 
    }; 
 
 
    private ArrayAdapter<String> adapter; 
    private ArrayList<String> data; 
 
    private static final int PROGRESS_DIALOG = 1; 
    private static final int STATE_FINISH = 1; 
    private static final int STATE_ERROR = -1; 
} 

這個例子,我本身寫完後以爲仍是有點亂,要稍微整理才能看明白線程間交互的過程以及數據的先後變化。隨後瞭解到AsyncTask類,相應修改後就很容易明白了!

 

2.3 AsyncTask

AsyncTask版:
((Button) findViewById(R.id.load_AsyncTask)).setOnClickListener(new View.OnClickListener(){ 
 
    @Override 
    public void onClick(View view) { 
        data = null; 
        data = new ArrayList<String>(); 
 
        adapter = null; 
 
        //顯示ProgressDialog放到AsyncTask.onPreExecute()裏 
        //showDialog(PROGRESS_DIALOG); 
        new ProgressTask().execute(data); 
    } 
}); 
 
private class ProgressTask extends AsyncTask<ArrayList<String>, Void, Integer> { 
 
/* 該方法將在執行實際的後臺操做前被UI thread調用。能夠在該方法中作一些準備工做,如在界面上顯示一個進度條。*/ 
@Override 
protected void onPreExecute() { 
    // 先顯示ProgressDialog
    showDialog(PROGRESS_DIALOG); 
} 
 
/* 執行那些很耗時的後臺計算工做。能夠調用publishProgress方法來更新實時的任務進度。 */ 
@Override 
protected Integer doInBackground(ArrayList<String>... datas) { 
    ArrayList<String> data = datas[0]; 
    for (int i=0; i<8; i++) { 
        data.add("ListItem"); 
    } 
    return STATE_FINISH; 
} 
 
/* 在doInBackground 執行完成後,onPostExecute 方法將被UI thread調用, 
 * 後臺的計算結果將經過該方法傳遞到UI thread. 
 */ 
@Override 
protected void onPostExecute(Integer result) { 
    int state = result.intValue(); 
    switch(state){ 
    case STATE_FINISH: 
        dismissDialog(PROGRESS_DIALOG); 
        Toast.makeText(getApplicationContext(), 
                "加載完成!", 
                Toast.LENGTH_LONG) 
             .show(); 
 
        adapter = new ArrayAdapter<String>(getApplicationContext(), 
                android.R.layout.simple_list_item_1, 
                data ); 
                 
        setListAdapter(adapter); 
 
        break; 
         
    case STATE_ERROR: 
       dismissDialog(PROGRESS_DIALOG); 
       Toast.makeText(getApplicationContext(), 
               "處理過程發生錯誤!", 
               Toast.LENGTH_LONG) 
            .show();
 
       adapter = new ArrayAdapter<String>(getApplicationContext(), 
               android.R.layout.simple_list_item_1, 
               data );
 
          setListAdapter(adapter);
 
          break;
 
   default:
 
   }
}

Android另外提供了一個工具類:AsyncTask。它使得UI thread的使用變得異常簡單。它使建立須要與用戶界面交互的長時間運行的任務變得更簡單,不須要藉助線程和Handler便可實現。

    1)  子類化AsyncTask
    2)  實現AsyncTask中定義的下面一個或幾個方法
         onPreExecute() 開始執行前的準備工做;
         doInBackground(Params...) 開始執行後臺處理,能夠調用publishProgress方法來更新實時的任務進度;
         onProgressUpdate(Progress...)  在publishProgress方法被調用後,UI thread將調用這個方法從而在界面上展現任務的進展狀況,例如經過一個進度條進行展現。
         onPostExecute(Result) 執行完成後的操做,傳送結果給UI 線程。
 
         這4個方法都不能手動調用。並且除了doInBackground(Params...)方法,其他3個方法都是被UI線程所調用的,因此要求:
        1) AsyncTask的實例必須在UI thread中建立;
        2) AsyncTask.execute方法必須在UI thread中調用;
        
    同時要注意:該task只能被執行一次,不然屢次調用時將會出現異常。並且是不能手動中止的,這一點要注意,看是否符合你的需求!
 
    在使用過程當中,發現AsyncTask的構造函數的參數設置須要看明白: AsyncTask<Params, Progress, Result>
    Params對應doInBackground(Params...)的參數類型。而new AsyncTask().execute(Params... params),就是傳進來的Params數據,你能夠execute(data)來傳送一個數據,或者execute(data1, data2, data3)這樣多個數據。
    Progress對應onProgressUpdate(Progress...)的參數類型;
    Result對應onPostExecute(Result)的參數類型。
    當以上的參數類型都不須要指明某個時,則使用Void,注意不是void。不明白的能夠參考上面的例子,或者API Doc裏面的例子。
相關文章
相關標籤/搜索