一、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方法中执行消息的发送,
// 首先是被动方式:
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.view.View;
import android.widget.Button; /**
* 此处重点熟悉message的属性~,将message绑定到目标handler,然后直接使用message的方法将消息发送~,被动发送。
* @author
*
*/ // 在安卓开发中是绝对不能使用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; /**
* 此处主要是使用handler的主动抓取、发送message功能!在messaQequue里面
*
* @author *
*/
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毫秒后延迟。
60 // 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");
72 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没有经验,有错请指出。。。
/*
* 这种方式获取响应内容的实体~entity中的流对象!不建议使用了。比较麻烦, InputStream inputStream
* =null; inputStream = httpResponse.getEntity().getContent();
* 然后将该inputStream写入到ByteArrayOutputStream,然后该内存缓冲流可以转为字节数组byte[]=
* outputStream.toByteArray();
*/
// 使用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();
}
}
}
}
---------------如有侵犯,请告知。