一、handler的使用場景為么會有handler?(部分內容圖片摘自http://www.runoob.com/w3cnote/android-tutorial-handler-message.html)
二、handler的消息處理機制
在Android中提供了一種異步回調機制Handler,使用它,我們可以在完成一個很長時間的任務后做出相應的通知。
- UI線程:就是我們的主線程,系統在創建UI線程的時候會初始化一個Looper對象,同時也會創建一個與其關聯的MessageQueue;
- Handler:作用就是發送與處理信息,如果希望Handler正常工作,在當前線程中要有一個Looper對象
- Message:Handler接收與處理的消息對象
- MessageQueue:消息隊列,先進先出管理Message,在初始化Looper對象時會創建一個與之關聯的MessageQueue;
- Looper:每個線程只能夠有一個Looper,管理MessageQueue,不斷地從中取出Message分發給對應的Handler處理!
通俗一點講:當我們的子線程想修改Activity中的UI組件時,我們可以新建一個Handler對象,通過這個對象向主線程發送信息;而我們發送的信息會先到主線程的MessageQueue進行等待,由Looper按先入先出順序取出,再根據message對象的what屬性分發給對應的Handler進行處理!
三、Handler的相關方法
在使用android的消息的處理機制的時候:一般是有兩種手段,
1、該message自己綁定到目標handler后,自行進入messageQueue,等待handler接受處理。Message方法:public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) ,通過該方法可以獲得一個消息:Message message = Message.obtain(handler, 33, 2, 3, "hello");
發送消息的方式,有一點將自己綁定好了被發射的感覺,message.sendToTarget(); ---被動(意會)
2、handler主動設置要發送的消息的各個屬性值:arg1,arg2,obj,what。方法:public final Message obtainMessage(int what, int arg1, int arg2, Object obj) 通過該方法也可以獲得一個消息:比如Message message = handler.obtainMessage(3, 1, 2, "java");
然后將設置好的消息,由handler發送出去:handler.sendMessage(message);----主動(自己意會的)。下面十幾個常用方法:
- void handleMessage(Message msg):處理消息的方法,使用handleMessage去處理消息,里面的參數Message msg既是在messageQueue里面取出的消息message~
- sendEmptyMessage(int what):發送空消息
- sendEmptyMessageDelayed(int what,long delayMillis):指定延時多少毫秒后發送空信息
- sendMessage(Message msg):立即發送信息
- sendMessageDelayed(Message msg):指定延時多少毫秒后發送信息
- final boolean hasMessage(int what):檢查消息隊列中是否包含what屬性為指定值的消息 如果是參數為(int what,Object object):除了判斷what屬性,還需要判斷Object屬性是否為指定對象的消息
//========================
//廢話不多說直接上demo:由上圖中的圖2可以看出消息的處理是在一條全新的線程中進行的,因此關鍵的是要新建一個thread,在它的run方法中執行消息的發送,
// 首先是被動方式:
1 import android.os.Bundle; 2 import android.os.Handler; 3 import android.os.Message; 4 import android.app.Activity; 5 import android.view.View; 6 import android.widget.Button; 7 8 9 /** 10 * 此處重點熟悉message的屬性~,將message綁定到目標handler,然后直接使用message的方法將消息發送~,被動發送。 11 * @author 12 * 13 */ 14 15 // 在安卓開發中是絕對不能使用UI主線程去訪問網絡 的,一定是要開一條新的線程去訪問然后把結果返回 16 public class MainActivity extends Activity { 17 private Button button; 18 19 // handler對象,用來接收消息~ 20 private Handler handler = new Handler() { 21 @Override 22 public void handleMessage(android.os.Message msg) { //這個是發送過來的消息 23 // 處理從子線程發送過來的消息 24 int arg1 = msg.arg1; //獲取消息攜帶的屬性值 25 int arg2 = msg.arg2; 26 int what = msg.what; 27 Object result = msg.obj; 28 System.out.println("-arg1--->>" + arg1); 29 System.out.println("-arg2--->>" + arg2); 30 System.out.println("-what--->>" + what); 31 System.out.println("-result--->>" + result); 32 Bundle bundle = msg.getData(); // 用來獲取消息里面的bundle數據 33 System.out.println("-getData--->>" 34 + bundle.getStringArray("strs").length); 35 }; 36 }; 37 38 @Override 39 protected void onCreate(Bundle savedInstanceState) { 40 super.onCreate(savedInstanceState); 41 setContentView(R.layout.activity_main); 42 button = (Button) findViewById(R.id.button1); 43 44 button.setOnClickListener(new View.OnClickListener() { 45 @Override 46 public void onClick(View v) { 47 // TODO Auto-generated method stub 48 // 啟動一個子線程 49 new Thread(new MyThread()).start(); //一定要在這里面啟動! 50 } 51 }); 52 } 53 54 public class MyThread implements Runnable { 55 @Override 56 public void run() { //run方法里面寫要發送的消息對象,並對消息攜帶的信息進行定義!! 57 // TODO Auto-generated method stub 58 59 // 第一種方式:獲取消息 60 // Message message = Message.obtain(); 61 // message.what = 1; 62 // message.arg1 = 2; 63 // message.arg2 = 3; 64 // message.obj = "jack"; 65 // handler.sendMessage(message); 66 67 // 第二種方式 68 // Message message = Message.obtain(handler); 69 // message.what = 1; 70 // message.arg1 = 2; 71 // message.arg2 = 3; 72 // message.obj = "jack"; 73 // //handler.sendMessage(message); 74 // //此時在構造方法里面已經將message的target綁定了handler不需要再次發送了。 75 // message.sendToTarget(); 76 77 // 第三種方式,和上面是沒有區別的。。 78 // Message message = Message.obtain(handler, 33); 79 // message.arg1 = 2; 80 // message.arg2 = 3; 81 // message.obj = "jack"; 82 // message.sendToTarget(); 83 84 // 第4種方式這幾種方式都是大同小異,只不過是內部封裝了而已,使用的時候根據實際需要就可以了。 85 Message message = Message.obtain(handler, 33, 2, 3, "hello"); 86 Bundle data = new Bundle(); //message也可以攜帶復雜一點的數據比如:bundle對象。 87 data.putStringArray("strs", new String[] { "c", "c++", "java" }); 88 message.setData(data); 89 message.sendToTarget(); // 不可忘! 90 } 91 } 92 }
// ================================布局文件就不給出了,就是一個按鈕然后為該按鈕綁定了事件。
// =============================handler的主動發送消息
1 import android.os.Bundle; 2 import android.os.Handler; 3 import android.os.Message; 4 import android.os.SystemClock; 5 import android.app.Activity; 6 import android.view.Menu; 7 import android.view.View; 8 import android.view.View.OnClickListener; 9 import android.widget.Button; 10 11 /** 12 * 此處主要是使用handler的主動抓取、發送message功能!在messaQequue里面 13 * 14 * @author15 * 16 */ 17 public class MainActivity extends Activity implements OnClickListener { 18 private Button button, button2; 19 20 // Handler可以接受發送消息,從消息隊列中提取消息用於更新UI,這里都沒有對UI進行操作,主要是介紹如何定義自己的message如何發送這個message,最后將會給一個demo 21 private Handler handler = new Handler() { 22 @Override 23 public void handleMessage(android.os.Message msg) { 24 25 System.out.println("-arg1--->" + msg.arg1); 26 System.out.println("-arg2--->" + msg.arg2); 27 System.out.println("-what--->" + msg.what); 28 System.out.println("-obj--->" + msg.obj); 29 }; 30 }; 31 32 @Override 33 protected void onCreate(Bundle savedInstanceState) { 34 super.onCreate(savedInstanceState); 35 setContentView(R.layout.activity_main); 36 button = (Button) findViewById(R.id.button1); 37 button2 = (Button) findViewById(R.id.button2); 38 39 button.setOnClickListener(this); 40 button2.setOnClickListener(this); 41 } 42 43 @Override 44 public void onClick(View v) { 45 // TODO Auto-generated method stub 46 switch (v.getId()) { 47 case R.id.button1: 48 new Thread(new Runnable() { //使用匿名內部類的方式,這個無難點吧。。。 49 @Override 50 public void run() { 51 // TODO Auto-generated method stub 52 // handler發送消息的第一種方式 53 // handler.sendEmptyMessage(3); 54 55 // handler發送消息的第二種方式,第二個參數是指定在指定的時間上發送消息,這個是確定的某個時間! 56 // 可以通過獲取當前的系統時間后SystemClock.uptimeMillis()再加上某個時間,如果給出的時間小於當前時間則立即發送,親測
//而且感覺這個功能很bug的,在特定的時間,需要我們人為的去計算:SystemClock.uptimeMillis()+myTime(這個是你想在多少毫秒后啟動的毫秒值) 57 // handler.sendEmptyMessageAtTime(3, X+3000); 59 // handler發送消息的第三種方式,這個效果是在3000毫秒后延遲。 60 // handler.sendEmptyMessageDelayed(3, 3000); 61 62 // handler發送消息的第四種方式 63 // Message message = Message.obtain(); //這個是使用message被動得到 64 // Message message = handler.obtainMessage(); // 65 // handler的主動獲取消息,在源碼方面一樣!無區別。 66 // message.arg1 = 1; 67 // message.arg2 = 2; 68 // message.obj = "java"; 69 // message.what = 4; 70 // 使用handler發送消息的第五種方式,原理都是一樣的~
//我最習慣還是message自己搞自己的事情別去干發送的活,message需要攜帶的屬性由他自己搞,剩下的發送接收處理的體力勞動由handler搞。
71 Message message = handler.obtainMessage(3, 1, 2, "java"); 72 handler.sendMessage(message); 73 } 74 }).start(); //記得啟動 75 break; 76 case R.id.button2: 77 // 第二個按鈕使用post方式發送消息,該方法需要一個runnable的實例,使用匿名內部類的方式實現。 78 // 直接使用一個匿名內部類Runnable來執行1.獲取消息對象;2.發送消息對象。換湯不換葯,通過看源碼也可以知道背后的實現都是一個道理。 79 handler.post(new Runnable() { 80 @Override 81 public void run() { 82 // TODO Auto-generated method stub 83 Message message = handler.obtainMessage(23, 21, 22, "postMessage"); 84 handler.sendMessage(message); 85 } 86 }); 87 break; 88 } 89 } 90 91 }
//-----------------------------------具體應用handler來更新UI
//========================================
2 3 import java.io.ByteArrayOutputStream; 4 import java.io.IOException; 5 import java.io.InputStream; 6 7 import org.apache.http.HttpEntity; 8 import org.apache.http.HttpResponse; 9 import org.apache.http.client.HttpClient; 10 import org.apache.http.client.methods.HttpGet; 11 import org.apache.http.impl.client.DefaultHttpClient; 12 import org.apache.http.util.EntityUtils; 13 14 import android.os.Bundle; 15 import android.os.Handler; 16 import android.os.Message; 17 import android.app.Activity; 18 import android.app.ProgressDialog; 19 import android.graphics.BitmapFactory; 20 import android.view.View; 21 import android.widget.Button; 22 import android.widget.ImageView; 23 24 public class MainActivity extends Activity { 25 private Button button; 26 private ImageView imageView; 27 private String image_path = "http://www.deskcar.com/desktop/fengjing/200895150214/21.jpg"; 28 private final int IS_FINSIH = 1; 29 private ProgressDialog dialog = null; 30 31 // 標准的寫法 32 private Handler handler = new Handler() { 33 // 使用handleMessage去處理消息!!,里面的參數Message msg既是發送過來的參數~ 34 @Override 35 public void handleMessage(android.os.Message msg) { 36 // 37 // 在此接受發送過來的消息<---msg 38 byte[] data = (byte[]) msg.obj; //轉型 39 // 將接受過來的數據賦值給imageview 40 imageView.setImageBitmap(BitmapFactory.decodeByteArray(data, 0, 41 data.length)); 42 // 標記~數據發送已經結束了?,此處有由代碼的邏輯順序來決定的, 43 if (msg.what == IS_FINSIH) { 44 dialog.dismiss(); 45 } 46 }; 47 }; 48 49 @Override 50 protected void onCreate(Bundle savedInstanceState) { 51 super.onCreate(savedInstanceState); 52 setContentView(R.layout.activity_main); 53 imageView = (ImageView) findViewById(R.id.imageView1); 54 dialog = new ProgressDialog(this); 55 dialog.setTitle("提示"); 56 dialog.setMessage("正在下載,請稍等..."); 57 dialog.setCancelable(false); 58 59 button = (Button) findViewById(R.id.button1); 60 button.setOnClickListener(new View.OnClickListener() { 61 @Override 62 public void onClick(View v) { 63 // TODO Auto-generated method stub 64 // 開啟線程 65 new Thread(new MyThread()).start(); 66 dialog.show(); // 顯示對話框他會直接先顯示出來這個是位於主線程里面的。與thread互不影響。 67 68 } 69 }); 70 } 71 72 // 避免在UI主線程里面更新數據 73 public class MyThread implements Runnable { 74 @Override 75 public void run() { 76 // TODO Auto-generated method stub 77 // 使用http完成網絡下載的操作 78 HttpClient httpClient = new DefaultHttpClient(); 79 HttpGet httpGet = new HttpGet(image_path); 80 HttpResponse httpResponse = null; 81 //下面是我在http的使用過程中總結的一些心得,對於http沒有經驗,有錯請指出。。。 82 /* 83 * 這種方式獲取響應內容的實體~entity中的流對象!不建議使用了。比較麻煩, InputStream inputStream 84 * =null; inputStream = httpResponse.getEntity().getContent(); 85 * 然后將該inputStream寫入到ByteArrayOutputStream,然后該內存緩沖流可以轉為字節數組byte[]= 86 * outputStream.toByteArray(); 87 */ 88 // 使用http新的就是通過httpclient執行要執行的get/或者post方法然后獲取服務端響應過來的實體對象entity; 89 // 然后從該entity中獲取需要的數據比如inputstream/文件的長度等。。。一般我們的最終目的都是獲取一個字節數組!byte[]只有二進制的數據才是數據的終極形態! 90 // 在使用的時候為了方便操作有一個工具類可以幫助我們簡單的獲取二進制數組 91 try { 92 httpResponse = httpClient.execute(httpGet);// client執行請求~典型的面對對象:客戶端對象取執行請求的方法,獲得返回的內容對象。通過工具來解析內容 93 // 判斷連接是否正常 94 if (httpResponse.getStatusLine().getStatusCode() == 200) { 95 96 HttpEntity entity = httpResponse.getEntity(); //獲取響應頭的實體內容 97 // 使用EntityUtils 98 byte[] data = EntityUtils.toByteArray(entity); 99 // 到了這里就是需要把我們的數據發送給UI主線程,二進制數組是最好的發送對象!!! 100 // Message message = new Message() 該方法是創建而不是在的當前的線程池取出。 101 Message message = Message.obtain(); 102 message.obj = data; 103 message.what = IS_FINSIH; // 結束標志位 104 handler.sendMessage(message); // 將數據發送過去~ 105 } 106 107 } catch (IOException e) { 108 // TODO Auto-generated catch block 109 e.printStackTrace(); 110 } 111 } 112 } 113 }
---------------如有侵犯,請告知。
