Android Handler 四個使用執行個體 及HandlerThread的使用,androidhandler執行個體
當應用程式啟動時,Android首先會開啟一個主線程 (也就是UI線程),主線程為管理介面中的UI控制項,進行事件分發,比如說:點擊一個Button,Android會分發事件到Button上,來響應你的操作。如果此時需要一個耗時的操作。例如: 連網讀取資料,或者讀取本地較大的一個檔案的時候,你不能把這些操作放在主線程中,如果你放在主線程中的話,介面會出現假死現象,如果5秒鐘還沒有完成的話,會收到Android系統的一個錯誤提示"強制關閉"。這個時候我們需要把這些耗時的操作,放在一個子線程中,因為子線程涉及到UI更新,Android主線程是線程不安全的,也就是說,更新UI只能在主線程中更新,子線程中操作是危險的。這個時候,Handler就出現了,來解決這個複雜的問題,由於Handler運行在主線程中(UI線程中),它與子線程可以通過Message對象來傳遞資料,這個時候,Handler就承擔著接收子線程傳過來的(子線程用sendMessage()方法傳遞)Message對象(裡麵包含資料),把這些訊息放入主線程隊列中,配合主線程進行UI更新。
Handler使用執行個體一、
這個例子介紹最簡單的Handler使用,是將Handler綁定到它所建立的線程中。本次執行個體完成的功能是:單擊[開始] 按鈕,程式會開始啟動線程,並且線程程式完成後延時1s後繼續啟動該線程,每次線程的run函數中完成對介面輸出UpdateThread...文字,不停的運行下去,當單擊“結束”按鈕時,該線程就會停止,如果繼續單擊“開始”,則文字又開始輸出了。
原始碼:
package com.example.handlertest;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity { private TextView text_view = null; private Button start = null; private Button end = null; //使用handler時首先要建立一個handler Handler handler = new Handler(); //要用handler來處理多線程可以使用runnable介面,這裡先定義該介面 //線程中運行該介面的run函數 Runnable update_thread = new Runnable() { public void run() { //線程每次執行時輸出"UpdateThread..."文字,\n為自動換行 text_view.append("\nUpdateThread..."); //延時1s後又將線程加入到線程隊列中 handler.postDelayed(update_thread, 1000); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text_view = (TextView)findViewById(R.id.text_view); start = (Button)findViewById(R.id.start); start.setOnClickListener(new StartClickListener()); end = (Button)findViewById(R.id.end); end.setOnClickListener(new EndClickListener()); } private class StartClickListener implements OnClickListener { public void onClick(View v) { // TODO Auto-generated method stub //將線程介面立刻送到線程隊列中 handler.post(update_thread); } } private class EndClickListener implements OnClickListener { public void onClick(View v) { // TODO Auto-generated method stub //將介面從線程隊列中移除 handler.removeCallbacks(update_thread); } }}布局檔案:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/text_view" android:layout_width="fill_parent" android:layout_height="200dip" android:text="@string/hello_world" tools:context=".MainActivity" /> <Button android:id="@+id/start" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="開始" /> <Button android:id="@+id/end" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="結束" /></LinearLayout>
Handler使用執行個體二、
這個例子比剛才那個例子稍微複雜些。因為這個例子中用到了Handler的訊息佇列機制,即通過Handler中一個線程向訊息佇列中用sendMessage方法發送訊息,發送的訊息當然可以用來傳遞參數。在Handler中用handleMessage來處理訊息,處理方法是獲得訊息佇列中的訊息參數,用這些參數來完成另外一些功能。
本執行個體實現的是當“Start”按鈕按下時,會啟動一個線程,並綁定到Handler中,該線程發送帶有參數的message到handler的訊息佇列中,訊息佇列的另一端擷取該訊息,並且用該訊息的參數來更新進度條。
原始碼:
package com.example.handlertest;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.ProgressBar;public class MainActivity extends Activity {private ProgressBar progress_bar = null;private Button start = null;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);progress_bar = (ProgressBar) findViewById(R.id.progress_bar);start = (Button) findViewById(R.id.start);start.setOnClickListener(new StartOnClickListener());}private class StartOnClickListener implements OnClickListener {public void onClick(View v) {// 讓進度條顯示出來progress_bar.setVisibility(View.VISIBLE);// 將線程加入到handler的線程隊列中update_progress_bar.post(update_thread);}}// 建立一個handler,內部完成處理訊息方法Handler update_progress_bar = new Handler() {@Overridepublic void handleMessage(Message msg) {// super.handleMessage(msg);// 顯示進度條progress_bar.setProgress(msg.arg1);// 重新把進程加入到進程隊列中update_progress_bar.post(update_thread);}};// 不加這個分號則不能自動添加代碼Runnable update_thread = new Runnable() {int i = 0;public void run() {// TODO Auto-generated method stubi += 10;// 首先獲得一個訊息結構Message msg = update_progress_bar.obtainMessage();// 給訊息結構的arg1參數賦值msg.arg1 = i;// 延時1s,java中的try+catch用來排錯處理try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO: handle exceptione.printStackTrace();}// 把訊息發送到訊息佇列中update_progress_bar.sendMessage(msg);if (i == 100) {// 把線程從線程隊列中移除update_progress_bar.removeCallbacks(update_thread);}}};}布局檔案:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/start" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:text="@string/start" /> <ProgressBar android:id="@+id/progress_bar" android:layout_width="fill_parent" android:layout_height="100dip" android:layout_alignParentTop="true" style="?android:attr/progressBarStyleHorizontal" android:visibility="gone" /></LinearLayout>
Handler使用執行個體三、
上面2個例子表面上看Handler使用了post方法啟動了runnbale,其實啟動的線程和activity主線程是同一個線程,因為它只是運行了線程的run方法,而不是start方法。執行個體3的目的是為了驗證僅使用Handler的post方法是否處於同一個線程。
該執行個體在主activtiy的onCreate函數中列印了2條關於本線程的資訊,然後建立一個Handler並為它綁定一個線程,線上程的run方法中也列印了線程的資訊,觀察2者的資訊是否一樣。
結果如下:
說明這2個線程確實是同一線程,並且可以看出主介面中的文字大概過了5s才顯示出來,因為語句setContentView(R.layout.activity_main);放在了handler的post啟動語句後面,而handler綁定的線程中又延時了5s,所以同時也證明了只有是同一個線程才會出現這種情況。而且Logcat中列印出如下日誌:
09-06 17:10:19.353: I/Choreographer(19937): Skipped 62 frames! The application may be doing too much work on its main thread.
原始碼:
package com.example.handlertest;import android.app.Activity;import android.os.Bundle;import android.os.Handler;public class MainActivity extends Activity { //建立一個handler private Handler handler = new Handler(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //將runnable載入到handler的線程隊列中去 handler.post(r); //Thread t = new Thread(r); //t.start(); setContentView(R.layout.activity_main); //列印activtiy線程資訊 System.out.println("activity_id---->"+Thread.currentThread().getId()); System.out.println("activity_name---->"+Thread.currentThread().getName()); } Runnable r = new Runnable() { public void run() { //列印建立線程資訊 System.out.println("handler_id---->"+Thread.currentThread().getId()); System.out.println("handler_name---->"+Thread.currentThread().getName()); //延時10s,為了觀察主介面中內容出現的時間 try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } };}如果把語句:
handler.post(r);
換成:
Thread t = new Thread(r);
t.start();
其它的不變,則程式運行時主介面內容立刻就顯示出來了,且系統輸出如下:
這2者都說明這樣綁定的線程與它所在的activity線程就不是同一個線程了。
Handler使用執行個體四、
這個例子將學會怎樣不使用runnable來啟動一個線程,而是用HandlerThread的looper來構造一個handler,然後該handler自己獲得訊息,並傳遞資料,然後又自己處理訊息,當然這是在另一個線程中完成的。
訊息結構中傳遞簡單的整型可以採用它的參數arg1和arg2,或者傳遞一些小的其它資料,可以用它的Object,該Object可以是任意的對象。當需要傳送比較大的資料是,可以使用訊息的setData方法,該方法需要傳遞一個Bundle的參數。Bundle中存放的是鍵值對的map,只是它的鍵值類型和資料類型比較固定而已。
原始碼:
package com.example.handlertest;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.HandlerThread;import android.os.Looper;import android.os.Message;public class MainActivity extends Activity {@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); System.out.println("activity_ID---->"+Thread.currentThread().getId()); //建立一個HanderThread對象,該對象實現了用Looper來處理訊息佇列的功能 HandlerThread handler_thread = new HandlerThread("handler_thread"); handler_thread.start(); //MyHandler類是自己繼承的一個類,這裡採用hand_thread的Looper來初始化它 MyHandler my_handler = new MyHandler(handler_thread.getLooper()); //獲得一個訊息msg Message msg = my_handler.obtainMessage(); //採用Bundle儲存資料,Bundle中存放的是索引值對的map,只是它的索引值類型和資料類型比較固定而已 Bundle b = new Bundle(); b.putString("whether", "晴天"); b.putInt("temperature", 34); msg.setData(b); //將msg發送到自己的handler中,這裡指的是my_handler,調用該handler的HandleMessage方法來處理該mug msg.sendToTarget(); } class MyHandler extends Handler { //空的建構函式 public MyHandler() {} //以Looper型別參數傳遞的函數,Looper為訊息泵,不斷迴圈的從訊息佇列中得到訊息並處理,因此 //每個訊息佇列都有一個Looper,因為Looper是已經封裝好了的訊息佇列和訊息迴圈的類 public MyHandler(Looper looper) { //調用父類的建構函式 super(looper); } @Override public void handleMessage(Message msg) { //此處能看到,訊息處理不是在主線程中執行的 System.out.println("Handler_ID---->"+Thread.currentThread().getId()); System.out.println("Handler_Name---->"+Thread.currentThread().getId()); //將訊息中的bundle資料取出來 Bundle b = msg.getData(); String whether = b.getString("whether"); int temperature = b.getInt("temperature"); System.out.println("whether= "+whether+" ,temperature= "+temperature); } }}
總結:
Android中的handler可以用來完成非同步訊息出來,即發送訊息和接收訊息相互獨立,可以同時運行。在例1和例2中,實際上handler中使用的線程是與它所在的activity處於同一個主線程,因為handler中調用的runnable介面是直接運行該介面的run函數的,而不是start函數。例3專門比較了這2中情況。例4學會使用怎樣在新線程中處理訊息的方法。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。