一、handler的使用場景為麼會有handler?
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TP3JGakd0YzZkMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLzgzN2MjN1kDMxEjMxgTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
二、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屬性是否為指定對象的消息
// 首先是被動方式:
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.view.View;
import android.widget.Button;
// 在安卓開發中是絕對不能使用UI主線程去通路網絡 的,一定是要開一條新的線程去通路然後把結果傳回
public class MainActivity extends Activity {
private Button button;
// handler對象,用來接收消息~
private Handler handler = new Handler() {
@Override
public void handleMessage(android.os.Message msg) { //這個是發送過來的消息
// 處理從子線程發送過來的消息
int arg1 = msg.arg1; //擷取消息攜帶的屬性值
int arg2 = msg.arg2;
int what = msg.what;
Object result = msg.obj;
System.out.println("-arg1—>>" + arg1);
System.out.println("-arg2—>>" + arg2);
System.out.println("-what—>>" + what);
System.out.println("-result—>>" + result);
Bundle bundle = msg.getData(); // 用來擷取消息裡面的bundle資料
System.out.println("-getData—>>"
+ bundle.getStringArray(“strs”).length);
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// 啟動一個子線程
new Thread(new MyThread()).start(); //一定要在這裡面啟動!
}
});
}
public class MyThread implements Runnable {
@Override
public void run() { //run方法裡面寫要發送的消息對象,并對消息攜帶的資訊進行定義!!
// TODO Auto-generated method stub
// 第一種方式:擷取消息
// Message message = Message.obtain();
// message.what = 1;
// message.arg1 = 2;
// message.arg2 = 3;
// message.obj = “jack”;
// handler.sendMessage(message);
// 第二種方式
// Message message = Message.obtain(handler);
// message.what = 1;
// message.arg1 = 2;
// message.arg2 = 3;
// message.obj = “jack”;
// //handler.sendMessage(message);
// //此時在構造方法裡面已經将message的target綁定了handler不需要再次發送了。
// message.sendToTarget();
// 第三種方式,和上面是沒有差別的。。
// Message message = Message.obtain(handler, 33);
// message.arg1 = 2;
// message.arg2 = 3;
// message.obj = “jack”;
// message.sendToTarget();
// 第4種方式這幾種方式都是大同小異,隻不過是内部封裝了而已,使用的時候根據實際需要就可以了。
Message message = Message.obtain(handler, 33, 2, 3, “hello”);
Bundle data = new Bundle(); //message也可以攜帶複雜一點的資料比如:bundle對象。
data.putStringArray(“strs”, new String[] { “c”, “c++”, “java” });
message.setData(data);
message.sendToTarget(); // 不可忘!
}
}
}
//handler的主動發送消息
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity implements OnClickListener {
private Button button, button2;
// Handler可以接受發送消息,從消息隊列中提取消息用于更新UI,這裡都沒有對UI進行操作,主要是介紹如何定義自己的message如何發送這個message,最後将會給一個demo
private Handler handler = new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
System.out.println("-arg1—>" + msg.arg1);
System.out.println("-arg2—>" + msg.arg2);
System.out.println("-what—>" + msg.what);
System.out.println("-obj—>" + msg.obj);
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button1);
button2 = (Button) findViewById(R.id.button2);
button.setOnClickListener(this);
button2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.button1:
new Thread(new Runnable() { //使用匿名内部類的方式,這個無難點吧。。。
@Override
public void run() {
// TODO Auto-generated method stub
// handler發送消息的第一種方式
// handler.sendEmptyMessage(3);
// handler發送消息的第二種方式,第二個參數是指定在指定的時間上發送消息,這個是确定的某個時間!
// 可以通過擷取目前的系統時間後SystemClock.uptimeMillis()再加上某個時間,如果給出的時間小于目前時間則立即發送,親測
//而且感覺這個功能很bug的,在特定的時間,需要我們人為的去計算:SystemClock.uptimeMillis()+myTime(這個是你想在多少毫秒後啟動的毫秒值)
// handler.sendEmptyMessageAtTime(3, X+3000);
// handler發送消息的第三種方式,這個效果是在3000毫秒後延遲。
// handler.sendEmptyMessageDelayed(3, 3000);
// handler發送消息的第四種方式
// Message message = Message.obtain(); //這個是使用message被動得到
// Message message = handler.obtainMessage(); //
// handler的主動擷取消息,在源碼方面一樣!無差別。
// message.arg1 = 1;
// message.arg2 = 2;
// message.obj = “java”;
// message.what = 4;
// 使用handler發送消息的第五種方式,原理都是一樣的~
//我最習慣還是message自己搞自己的事情别去幹發送的活,message需要攜帶的屬性由他自己搞,剩下的發送接收處理的體力勞動由handler搞。
Message message = handler.obtainMessage(3, 1, 2, “java”);
handler.sendMessage(message);
}
}).start(); //記得啟動
break;
case R.id.button2:
// 第二個按鈕使用post方式發送消息,該方法需要一個runnable的執行個體,使用匿名内部類的方式實作。
// 直接使用一個匿名内部類Runnable來執行1.擷取消息對象;2.發送消息對象。換湯不換藥,通過看源碼也可以知道背後的實作都是一個道理。
handler.post(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Message message = handler.obtainMessage(23, 21, 22, “postMessage”);
handler.sendMessage(message);
}
});
break;
}
}
}
//具體應用handler來更新UI
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.BitmapFactory;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends Activity {
private Button button;
private ImageView imageView;
private String image_path = “http://www.deskcar.com/desktop/fengjing/200895150214/21.jpg”;
private final int IS_FINSIH = 1;
private ProgressDialog dialog = null;
// 标準的寫法
private Handler handler = new Handler() {
// 使用handleMessage去處理消息!!,裡面的參數Message msg既是發送過來的參數~
@Override
public void handleMessage(android.os.Message msg) {
//
// 在此接受發送過來的消息<—msg
byte[] data = (byte[]) msg.obj; //轉型
// 将接受過來的資料指派給imageview
imageView.setImageBitmap(BitmapFactory.decodeByteArray(data, 0,
data.length));
// 标記~資料發送已經結束了?,此處有由代碼的邏輯順序來決定的,
if (msg.what == IS_FINSIH) {
dialog.dismiss();
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.imageView1);
dialog = new ProgressDialog(this);
dialog.setTitle(“提示”);
dialog.setMessage(“正在下載下傳,請稍等…”);
dialog.setCancelable(false);
button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// 開啟線程
new Thread(new MyThread()).start();
dialog.show(); // 顯示對話框他會直接先顯示出來這個是位于主線程裡面的。與thread互不影響。
}
});
}
// 避免在UI主線程裡面更新資料
public class MyThread implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
// 使用http完成網絡下載下傳的操作
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(image_path);
HttpResponse httpResponse = null;
//下面是我在http的使用過程中總結的一些心得,對于http沒有經驗,有錯請指出。。。
// 使用http新的就是通過httpclient執行要執行的get/或者post方法然後擷取服務端響應過來的實體對象entity;
// 然後從該entity中擷取需要的資料比如inputstream/檔案的長度等。。。一般我們的最終目的都是擷取一個位元組數組!byte[]隻有二進制的資料才是資料的終極形态!
// 在使用的時候為了友善操作有一個工具類可以幫助我們簡單的擷取二進制數組
try {
httpResponse = httpClient.execute(httpGet);// client執行請求~典型的面對對象:用戶端對象取執行請求的方法,獲得傳回的内容對象。通過工具來解析内容
// 判斷連接配接是否正常
if (httpResponse.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = httpResponse.getEntity(); //擷取響應頭的實體内容
// 使用EntityUtils
byte[] data = EntityUtils.toByteArray(entity);
// 到了這裡就是需要把我們的資料發送給UI主線程,二進制數組是最好的發送對象!!!
// Message message = new Message() 該方法是建立而不是在的目前的線程池取出。
Message message = Message.obtain();
message.obj = data;
message.what = IS_FINSIH; // 結束标志位
handler.sendMessage(message); // 将資料發送過去~
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}