Android Handler 四個使用執行個體 及HandlerThread的使用,androidhandler執行個體

來源:互聯網
上載者:User

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學會使用怎樣在新線程中處理訊息的方法。


著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

聯繫我們

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