Handler線程間通訊

  1 package com.hixin.appexplorer;
  2 
  3 import java.util.List;
  4 
  5 import android.app.Activity;
  6 import android.app.ProgressDialog;
  7 import android.content.Context;
  8 import android.content.pm.PackageInfo;
  9 import android.content.pm.PackageManager;
 10 import android.os.Bundle;
 11 import android.os.Handler;
 12 import android.os.Message;
 13 import android.view.LayoutInflater;
 14 import android.view.View;
 15 import android.view.ViewGroup;
 16 import android.view.Window;
 17 import android.view.WindowManager;
 18 import android.widget.BaseAdapter;
 19 import android.widget.GridView;
 20 import android.widget.ImageView;
 21 import android.widget.TextView;
 22 
 23 public class MainActivity extends Activity implements Runnable {
 24 
 25     GridView gv;
 26     private List<PackageInfo> packageInfos;
 27     private ProgressDialog pd;
 28     private Handler mHandler = new Handler() {
 29         @Override
 30         public void handleMessage(Message msg) {
 31             // TODO Auto-generated method stub
 32             super.handleMessage(msg);
 33             gv.setAdapter(new GridViewAdapter(MainActivity.this));
 34             pd.dismiss();
 35             setProgressBarIndeterminateVisibility(false);
 36         }
 37         
 38     };
 39     @Override
 40     protected void onCreate(Bundle savedInstanceState) {
 41         super.onCreate(savedInstanceState);
 42     //    requestWindowFeature(Window.FEATURE_NO_TITLE);
 43         requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
 44         getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
 45         setContentView(R.layout.show_app_grid);
 46         
 47         gv = (GridView)this.findViewById(R.id.gv_apps);
 48         pd = ProgressDialog.show(this,"請稍候...", "正在搜索你所安裝的應用程序",true,false);
 49         setProgressBarIndeterminateVisibility(true);
 50         Thread t = new Thread(this);
 51         t.start();
 52         }
 53     
 54     @Override
 55     public void run() {
 56         // TODO Auto-generated method stub
 57         packageInfos = getPackageManager().getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);
 58         try {
 59             Thread.sleep(2000);
 60         } catch (InterruptedException e) {
 61             // TODO Auto-generated catch block
 62             e.printStackTrace();
 63         }
 64         mHandler.sendEmptyMessage(0);
 65     }
 66     
 67     
 68     class GridViewAdapter extends BaseAdapter{
 69 
 70         LayoutInflater inflater;
 71         public GridViewAdapter(Context context) {
 72             inflater = LayoutInflater.from(context);
 73         }
 74         @Override
 75         public int getCount() {
 76             // TODO Auto-generated method stub
 77             return packageInfos.size();
 78         }
 79 
 80         @Override
 81         public Object getItem(int position) {
 82             // TODO Auto-generated method stub
 83             return packageInfos.get(position);
 84         }
 85 
 86         @Override
 87         public long getItemId(int position) {
 88             // TODO Auto-generated method stub
 89             return position;
 90         }
 91 
 92         @Override
 93         public View getView(int position, View convertView, ViewGroup parent) {
 94             // TODO Auto-generated method stub
 95         
 96                 View view = inflater.inflate(R.layout.gv_item, null);
 97                 TextView tv = (TextView)view.findViewById(R.id.gv_item_appname);
 98                 ImageView iv = (ImageView)view.findViewById(R.id.gv_item_icon);
 99                 tv.setText(packageInfos.get(position).applicationInfo.loadLabel(getPackageManager()));
100                 iv.setImageDrawable(packageInfos.get(position).applicationInfo.loadIcon(getPackageManager()));
101             
102             return view;
103         }
104         
105     }
106     
107 }
108 
109     

 

當須要顯示的項目不少,會形成很大的延遲。這時候屏幕會一直黑屏。給用戶很很差的體驗。應該在獲取信息的時候,顯示一些進度信息給用戶看,增長用戶體驗。html

程序在期間獲取安裝程序信息,主界面上顯示進度條,當信息獲取完畢以後(獲取信息在新線程裏執行),發送一條消息通知主線程,主線程關掉進度條,加載列表。java

 

基本概念android

Looper:每個線程均可以產生一個Looper,用來管理線程的Message,Looper對象會創建一個MessgaeQueue數據結構來存放message。安全

Handler:與Looper溝通的對象,能夠push消息或者runnable對象到MessgaeQueue,也能夠從MessageQueue獲得消息。數據結構

線程A的Handler對象引用能夠傳遞給別的線程,讓別的線程B或C等能送消息來給線程A。app

線程A的Message Queue裏的消息,只有線程A所屬的對象能夠處理。ide

注意:Android裏沒有global的MessageQueue,不一樣進程(或APK之間)不能經過MessageQueue交換消息。函數

本例中線程A就是UI線程,線程B就是新建的處理線程oop

 

ps:handler.obtainMessage()理解post

在handler.obtainMessage()的參數是這樣寫的:
Message android.os.Handler.obtainMessage(int what, int arg1, int arg2, Object obj)

public final Message obtainMessage (int what, int arg1, int arg2, Object obj) 
Since: API Level 1 
Same as obtainMessage(), except that it also sets the what, obj, arg1,and arg2 values on the returned Message.

Parameters
what  Value to assign to the returned Message.what field. 
arg1  Value to assign to the returned Message.arg1 field. 
arg2  Value to assign to the returned Message.arg2 field. 
obj  Value to assign to the returned Message.obj field. 

而Handler中obtainMessage與new Message的區別:

obtainmessage()是從消息池中拿來一個msg 不須要另開闢空間new

new須要從新申請,效率低,obtianmessage能夠循環利用;

 

 1 //use Handler.obtainMessage(),instead of msg = new Message();   
 2 //because if there is already an Message object,that not be used by    
 3 //any one ,the system will hand use that object,so you don't have to    
 4 //create and object and allocate memory.   
 5 //it  is also another example of object recycling and reusing in android.   
 6     Message msg = mHandler.obtainMessage();  
 7     msg.what = UPDATE_LISTVIEW;  
 8     msg.obj = current + "/" + total + "songs";  
 9     //this method is called from worker Thread,so we cannot update UI from here.   
10     msg.sendToTarget();  

 

 
在看下面代碼:
1 Message msg = handler.obtainMessage();  
2                         msg.arg1 = i;  
3                         msg.sendToTarget();   
4   
5   
6 Message msg=new Message();  
7     msg.arg1=i;  
8     handler.sendMessage(msg);  
第一種寫法是message 從handler 類獲取,從而能夠直接向該handler 對象發送消息,第二種寫法是直接調用 handler 的發送消息方法發送消息。
 
 

Handler相關說明:

解釋:安卓的UI線程(即OnCreate函數建立的線程)是線程非安全的。也就是說,在UI線程中,使用sleep這樣的函數會致使整個線程延遲,可是咱們在安卓開發中,每每會常常遇到一些延遲比較厲害的操做,(例如經過HTTP獲取數據信息)若是放在主線程中,則會影響UI界面的渲染。可是若是另外新開一個線程,則因爲UI線程只能在主線程中修改,而致使沒法修改主線程的UI界面。這個時候Handler就出來解決這個問題。

handler能夠分發Message對象和Runnable對象到主線程中, 每一個Handler實例,都會綁定到建立他的線程中(通常是位於主線程), 也就是說Handler對象初始化後,就默認與對它初始化的進程的消息隊列綁定,所以能夠利用Handler所包含的消息隊列,制定一些操做的順序。

Handler主要兩大做用:

1. 提供post操做。post操做主要將Runnable對象放進主線程(UI)線程中的隊列中操做。post還支持延遲操做。使用post後,Runnable是按照隊列的形式逐個執行的。

2. handlerMessage操做。主要用於新開一個線程與主線程中的通訊。新開的線程執行完畢後,能夠經過sendMessage給主線程發送消息,而且傳遞一些參數,而後主線程就能夠修改UI界面了。

 

Handler提供的函數:

post類方法容許你排列一個Runnable對象到主線程隊列中:

post(Runnable)

postAtTime(Runnable,long)

postDelayed(Runnable long)

sendMessage類方法, 容許你安排一個帶數據的Message對象到隊列中:

sendEmptyMessage(int)

sendMessage(Message)

 sendMessageAtTime(Message,long)

sendMessageDelayed(Message,long)

 

應用實例:

   1,傳遞Message。用於接受子線程發送的數據, 並用此數據配合主線程更新UI

          在Android中,對於UI的操做一般須要放在主線程中進行操做。若是在子線程中有關於UI的操做,那麼就須要把數據消息做爲一個Message對象發送到消息隊列中,而後,用Handler中的handlerMessge方法處理傳過來的數據信息,並操做UI。類sendMessage(Message msg)方法實現發送消息的操做。 在初始化Handler對象時重寫的handleMessage方法來接收Messgae並進行相關操做。

 2,傳遞Runnable對象。用於經過Handler綁定的消息隊列,安排不一樣操做的執行順序。

Handler對象在進行初始化的時候,會默認的自動綁定消息隊列。利用類post方法,能夠將Runnable對象發送到消息隊列中,按照隊列的機制按順序執行不一樣的Runnable對象中的run方法。

另外,Android的CPU分配的最小單元是線程,Handler通常是在某個線程裏建立的,於是Handler和Thread就是相互綁定的,一一對應。而Runnable是一個接口,Thread是Runnable的子類。因此說,他倆都算一個進程。xml文件:

<RelativeLayout 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" >

    <Button 
        android:id="@+id/startButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="start"
        android:layout_centerInParent="true"
        />
    <Button 
        android:id="@+id/endButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="end"
        android:layout_below="@id/startButton"
        />

</RelativeLayout>
View Code

 

package com.example.androidexpriment;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;


public class MainActivity extends Activity {  
       
    private String TAG = "MainActivity";
      //聲明兩個按鈕控件  
     private Button startButton = null;  
      private Button endButton = null;  
      @Override 
      public void onCreate(Bundle savedInstanceState) {  
         super.onCreate(savedInstanceState);  
          setContentView(R.layout.activity_main);  
        //根據控件的ID獲得表明控件的對象,併爲這兩個按鈕設置相應的監聽器  
        startButton = (Button)findViewById(R.id.startButton);  
        startButton.setOnClickListener(new StartButtonListener());  
        endButton = (Button)findViewById(R.id.endButton);  
        endButton.setOnClickListener(new EndButtonListener());  
        Log.i(TAG,"Activity-->" + Thread.currentThread().getId()); 
           
     }  
     class StartButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
             //調用Handler的post方法,將要執行的線程對象添加到隊列當中  
            handler.post(updateThread);  
         }  
           
     }  
       
     class EndButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
            handler.removeCallbacks(updateThread);  
         }  
           
   }  
    //建立一個Handler對象  
    Handler handler  = new Handler();
    //將要執行的操做寫在線程對象的run方法當中  
    Runnable updateThread =  new Runnable(){  
  
         @Override 
        public void run() {  
            Log.i(TAG,"UpdateThread");  
            Log.i(TAG,"Activity-->" + Thread.currentThread().getId()); 
             //在run方法內部,執行postDelayed或者是post方法  
            handler.postDelayed(updateThread, 3000);  
        }  
           
    };  
 } 

 

點擊start後,程序的運行結果就是每隔3秒鐘,就會在控制檯打印一行UpdateTread。這是由於實現了Runnable接口的updateThread對象進入了空的消息隊列即被當即執行run方法,而在run方法的內部,又在3000ms以後將其再次發送進入消息隊列中。

注意這種方法建立Handler對象並不須要重寫handlerMessage方法。

從輸出結果能看出來:

   post方法雖然發送的是一個實現了Runnable接口的類對象,可是它並不是建立了一個新線程,而是執行了該對象中的run方法。也就是說,整個run中的操做和主線程處於同一個線程。這樣對於那些簡單的操做,彷佛並不會影響。可是對於耗時較長的操做,就會出現「假死」。爲了解決這個問題,就須要使得handler綁定到一個新開啓線程的消息隊列上,在這個處於另外線程的上的消息隊列中處理傳過來的Runnable對象和消息。Runnable對象只是做爲一個封裝了操做的對象被傳遞,並未產生新線程。

下面這種寫法也是能夠的:

package com.example.androidexpriment;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {
    
     TestThread t = null;
     private String TAG = "MainActivity";
      //聲明兩個按鈕控件  
     private Button startButton = null;  
      private Button endButton = null;  
      @Override 
      public void onCreate(Bundle savedInstanceState) {  
         super.onCreate(savedInstanceState);  
         setContentView(R.layout.activity_main);  
        //根據控件的ID獲得表明控件的對象,併爲這兩個按鈕設置相應的監聽器  
        startButton = (Button)findViewById(R.id.startButton);  
        startButton.setOnClickListener(new StartButtonListener());  
        endButton = (Button)findViewById(R.id.endButton);  
        endButton.setOnClickListener(new EndButtonListener());  
        Log.i(TAG,"onCreate-->" + Thread.currentThread().getId()); 
        t= new TestThread(1);
           
     }  
     class StartButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
             //調用Handler的post方法,將要執行的線程對象添加到隊列當中  
            handler.post(t);  
         }  
           
     }  
           
     class EndButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
            handler.removeCallbacks(t);  
         }  
           
   }  
    //建立一個Handler對象  
    Handler handler  = new Handler();
    
    class TestThread extends Thread{
            int prime;
            TestThread(int prime) {
                this.prime = prime;
            }
        @Override
        public void run() {
                 //在run方法內部,執行postDelayed或者是post方法  
                   handler.postDelayed(t, 3000);  
                   Log.i(TAG,"TestThread-->" + Thread.currentThread().getId()); 
            } 
        }            
 } 

 

雖然建立了一個Thread,可是並無執行Thread的start()方法。考慮到Thread和Runnable之間的關係,上面的兩種代碼並沒有實質差異,因此logcat中甚至都沒出現啓動新線程的日誌。

然而,若是稍加修改:加上啓動方法

  class StartButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
             //調用Handler的post方法,將要執行的線程對象添加到隊列當中  
            handler.post(t); 
            t.start();
         }           
     }  
         

能夠明顯看到,雖然啓動了新線程,但post仍然能夠把這個線程推到主線程裏面去,線程由虛擬機自動結束。

因此,在UI線程(主線程)中:

    mHandler=new Handler();

    mHandler.post(new Runnable(){

    void run(){

       //執行代碼..

     }

    });

 這個線程實際上是在UI線程以內運行的,並無新建線程。

 

    常見的新建線程的方法是:參考J2SE文檔的

   一、

 class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
  }


  • The following code would then create a thread and start it running:

     

         PrimeThread p = new PrimeThread(143);
         p.start();
    

   二、

   class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
  • The following code would then create a thread and start it running:

     

         PrimeRun p = new PrimeRun(143);
         new Thread(p).start();
    

儘可能按照上面給出的兩種方式作,不要受網上影響簡單的從Threa建立,那樣不能作到傳遞參數。

static void sleep(long millis)
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers.

代碼驗證:

 

package com.example.androidexpriment;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;


public class MainActivity extends Activity {  
       
    private String TAG = "MainActivity";
      //聲明兩個按鈕控件  
    private Button startButton = null;  
    private Button endButton = null; 
    TestThread t = null;
    int flag = 0;
      @Override 
      public void onCreate(Bundle savedInstanceState) {  
         super.onCreate(savedInstanceState);  
          setContentView(R.layout.activity_main);  
        //根據控件的ID獲得表明控件的對象,併爲這兩個按鈕設置相應的監聽器  
        startButton = (Button)findViewById(R.id.startButton);  
        startButton.setOnClickListener(new StartButtonListener());  
        endButton = (Button)findViewById(R.id.endButton);  
        endButton.setOnClickListener(new EndButtonListener());  
        Log.i(TAG,"onCreate-->" + Thread.currentThread().getId()); 
        
        t= new TestThread(1);
           
     }  
     class StartButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
             //調用Handler的post方法,將要執行的線程對象添加到隊列當中  
             t.start();
         }  
           
     }  
       
     class EndButtonListener implements OnClickListener{  
  
         @Override 
         public void onClick(View v) {  
             handler.sendEmptyMessage(33);
             flag = 5;
         }  
           
   }  
     
     
     class TestThread extends Thread{
         int prime;
         TestThread(int prime) {
             this.prime = prime;
         }
        @Override
        public void run() {
             //在run方法內部,執行postDelayed或者是post方法             
            try {
                while(true) {
                    Log.i(TAG,"TestThread-->" + Thread.currentThread().getId()); 
                    //handler.sendEmptyMessageDelayed(22,3000);
                    Thread.sleep(3000);
                    handler.sendEmptyMessage(22);
                    if(flag  == 5)  //線程最佳的退出方法,就是自殺,也就是在線程的函數裏面天然的return 出來
                        return;
                }             
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
         
     }
    //建立一個Handler對象  
    Handler handler  = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            super.handleMessage(msg);
            switch(msg.what) {
                case 22:
                    Log.i(TAG,"StartButton");  
                    Log.i(TAG,"Handler-->" + Thread.currentThread().getId()); 
                    break;
                case 33:
                    Log.i(TAG,"EndButton");  
                    Log.i(TAG,"Handler-->" + Thread.currentThread().getId()); 
                    break;
            }
                    
        }
        
    };

 } 

相關文章
相關標籤/搜索