android學習筆記之多線程學習

來源:互聯網
上載者:User

文章轉自:http://android.blog.51cto.com/268543/343823

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 mHander;public viod run() {Looper.prepare();  //建立本線程的Looper並建立一個MessageQueuemHandler = new Handler() {public void handleMessage(Message msg) {//process incoming message 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 {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;@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(R.layout.activity_main);((Button)findViewById(R.id.load_Handler)).setOnClickListener(new View.OnClickListener() {public void onClick(View v) {// TODO Auto-generated method stubdata = null;data = new ArrayList<String>();adapter = null;showDialog(PROGRESS_DIALOG);new ProgressThread(handler, data).start();}});}@Overrideprotected Dialog onCreateDialog(int id) {// TODO Auto-generated method stubswitch(id) {case PROGRESS_DIALOG:return ProgressThread.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;}@Overridepublic void run() {// TODO Auto-generated method stubfor(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預設就使用當前線程的Looperprivate final Handler handler = new Handler(Looper.getMainLooper()) {public void handleMessage(Message msg) {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;default:break;}}};}

 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)的參數類型。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.