線程或者線程執行本質上就是一串命令(也是程序代碼),而後咱們把它發送給操做系統執行。html
通常來講,咱們的CPU在任什麼時候候一個核只能處理一個線程。多核處理器(目前大多數Android設備已經都是多核)顧名思義,就是能夠同時處理多線程(通俗地講就是能夠同時處理多件事)。java
上面我說的是通常狀況,並非全部的描述都是必定正確的。由於單核也能夠用多任務模擬出多線程。android
每一個運行在線程中的任務均可以分解成多條指令,並且這些指令不用同時執行。因此,單核設備能夠首先切換到線程1去執行指令1A,而後切換到線程2去執行指令2A,接着返回到線程1再去執行1B、1C、1D,而後繼續切換到線程2,執行2B、2C等等,以此類推。git
這個線程之間的切換十分迅速,以致於在單核的設備中也會發生。幾乎全部的線程都在相同的時間內進行任務處理。其實,這都是由於速度太快形成的假象,就像電影《黑客帝國》裏的特工Brown同樣,能夠變幻出不少的頭和手。github
接下來咱們來看一些代碼。數據庫
在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類來調用它。緩存
第二個版是一般建議使用的方法。這也是一個很大的主題了,超過了本文的範圍,之後會再作討論。安全
不管什麼時候啓動APP,全部的組件都會運行在一個單獨的線程中(默認的)——叫作主線程。這個線程主要用於處理UI的操做併爲視圖組件和小部件分發事件等,所以主線程也被稱做UI線程。服務器
若是你在UI線程中運行一個耗時操做,那麼UI就會被鎖住,直到這個耗時操做結束。對於用戶體驗來講,這是很是糟糕的!這也就是爲何咱們要理解Android上的線程機制了。理解這些機制就能夠把一些複雜的工做移動到其它的線程中去執行。若是你在UI線程中運行一個耗時的任務,那麼頗有可能會發生ANR(應用無響應),這樣用戶就會很快地結束掉你的APP。
Android和Java同樣,它支持使用Java裏面的Thread類來進行一步任務處理。因此能夠輕鬆地像上面Java的例子同樣來使用Android上的線程,不過那好像仍是有點困難。
其實平行任務處理沒有想象中的那麼簡單,你必須在多線程中保證併發,就像偉大的Tim Bray說的那樣:ordinary humans can’t do concurrency at scale (or really at all) …
特別對於Android來講,如下這些功能就略顯臃腫:
那麼在Android上怎麼進行任務併發處理呢?
你可能聽過一些Android上一些常見的名詞:
一、Handler
這就是咱們今天要討論的詳細主題。
二、AsyncTask
使用AsyncTask是在Android上操做線程最簡單的方式,也是最容易出錯的方式。
三、IntentService
這種方式須要寫更多的代碼,可是這是把耗時任務移動到後臺的很好的方式,也是我最喜歡的方式。配上使用一個EventBus機制的框架如Otto,這樣的話實現IntentService就很是簡單了。
四、Loader
關於處理異步任務,還有不少事情須要作,好比從數據庫或者內容提供者那裏處理一些數據。
五、Service
若是你曾經使用過Service的話,你應該知道這裏會有一點誤區,其中一個常見的誤解就是服務是運行在後臺線程的。其實不是!看似運行在後臺是由於它們不與UI組件關聯,可是它們(默認)是運行在UI線程上的……因此默認運行在UI線程上,甚至在上面沒有UI部件。
若是想要把服務運行在後臺線程中,那麼必須自定義一個線程,而後把操做代碼都運行在那個線程中(與上面提到的方法很相似)。事實上你應該使用IntentService實現,可是這不是本文討論的主題。
如下是從 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的API,能夠清楚看到這幾個方法:
這裏的代碼都是很基礎的,不過你能夠好好看看註釋。
示例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了嗎?若是是的話,那麼能夠盡情使用線程了。
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 } }
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; }
((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: } }