Hanlder和message机制:
【概述】
android的Handler+Thread机制是android实现异步任务或者说实现更新UI界面最本质的方式,Handler+thread机制区别于Async Task机制的不同在于Async Task封装了Handler(因为在其源码实现中就已经获取了uiThread的Handler实现了其looper)并用线程池来实现。这两者在本质上区别不大。因为Handler+Thread是最本质的实现异步任务的方式,所以其效率肯定更高于Async Task。但是Async Task是android的改良品,代码更易读,在代码维护方面是很大的优势。
1、为何需要多线程
在日常应用中,我们通常需要处理一些“后台,用户不可见”的操作,例如说,我们需要下载一个音乐,要是你的应用必须等用户下载完成之后才可以进行别的操作,那肯定让用户非常的不爽。这时候,我们通常的做法是,让这些操作去后台执行,然后等后台执行完毕之后,再给用户弹出相应的提示信息。这时候,我们就需要使用多线程机制,然后通过创建一个新的线程来执行这些操作。
明白了,实现需求,我们就准备着手实现了。但是,经过进一步的了解,我们悲剧的发现,android中的线程机制是,只能在UI线程中和用户进行交互。当我们创建了一个新线程,执行了一些后台操作,执行完成之后,我们想要给用户弹出对话框以确认,但是却悲剧的发现,我们根本无法返回UI主线程了。(UI线程就是你当前看到的这些交互界面所属的线程)。
这时候,我们如果想要实现这些功能,我们就需要一个android为我们提供的handler和message机制。
2、解说Handler下的编程机制
我们通常在UI线程中创建一个handler,handler相当于一个处理器,它主要负责处理和绑定到该handler的线程中的message。每一个handler都必须关联一个looper,并且两者是一一对应的,注意,这点很重要哦!此外,looper负责从其内部的messageQueue中拿出一个个的message给handler进行处理。因为我们这里handler是在UI线程中实现的,所以经过这么一个handler、message机制,我们就可以回到UI线程中了。
handler:处理后台进程返回数据的工作人员。
message:后台进程返回的数据,里面可以存储bundle等数据格式
messageQueue:是线程对应looper的一部分,负责存储从后台进程中抛回的和当前handler绑定的message,是一个队列。
looper:looper相当于一个messageQueue的管理人员,它会不停的循环的遍历队列,然后将符合条件的message一个个的拿出来交给handler进行处理。
注意,handler是在UI线程中声明的,如果我们直接用类似代码执行一个线程的话,实际上并没有创建一个新的线程,因为handler已经跟默认的UI线程中的looper绑定了。
如果有兴趣的话,可以去看下Handler的默认空构造函数便知道原因了,里面直接绑定了当前UI线程的looper。
3、示例小程序:
1. public class MainActivity extends Activity implements OnClickListener {
2. private Button btnTXT;
3. private TextView tvTXT;
4.
5. private StringBuffer returnMsg;
6.
7. @Override
8. public void onCreate(Bundle savedInstanceState) {
9. super.onCreate(savedInstanceState);
10. setContentView(R.layout.activity_main);
11.
12. btnTXT = (Button)findViewById(R.id.btnTXT);
13. tvTXT = (TextView)findViewById(R.id.tvTXT);
14.
15. this);
16. }
17.
18. @Override
19. public void onClick(View v) {
20. new StringBuffer();
21.
22. // 创建一个包含Looper的线程,这里如果没有HandlerThread的调用,会直接将后边的MyRunnable放到UI线程队列(myHandler.post(new MyRunnable()))
23. new HandlerThread("handler_thread");
24. // 启动自定义处理线程
25.
26. new MyHandler(handlerThread.getLooper()); // 将handler绑定到新线程
27. new MyRunnable()); // 在新线程中执行任务
28. }
29.
30. /** 主线程Handler,可以与UI控件交互 */
31. new Handler(){
32. @Override
33. public void handleMessage(Message msg) {
34. if(msg.what == 0) {
35. // 与主线程控件打交道(直接访问)
36. }
37. }
38. };
39.
40.
41. /** 构造Hanlder,不可与UI控件直接交互 */
42. private MyHandler myHandler = null;
43. private class MyHandler extends Handler{
44. /**
45. * 使用默认的构造函数,会将handler绑定当前UI线程的looper。
46. * 如果想使用多线程这里是不能使用默认的构造方法的。
47. */
48. public MyHandler(){
49. super();
50. }
51.
52. /** 构造函数,自定义looper */
53. public MyHandler(Looper looper) {
54. super(looper);
55. }
56.
57. // 处理具体的message消息,继承自父类的方法
58. @Override
59. public void handleMessage(Message msg) {
60. int what = msg.what;
61. // 提取bundle中的信息
62. "name");
63. "sex");
64. boolean marry = bundle.getBoolean("marray");
65. int age = bundle.getInt("age");
66.
67. new StringBuffer(); // 拼接bundle信息
68. "what = ").append(what).append("\n\n");
69. "name = ").append(name).append("\n");
70. "sex = ").append(sex).append("\n");
71. "marry = ").append(marry).append("\n");
72. "age = ").append(age).append("\n\n");
73. "");
74.
75. // 保存要显示的结果
76. 0); // 向主线程mainHanlder发送消息,与UI控件交互显示结果
77.
78. super.handleMessage(msg);
79. }
80. }
81.
82.
83. // 构造Runnable,处理后台业务逻辑,如下载
84. private class MyRunnable implements Runnable{
85. @Override
86. public void run() {
87. try {
88. // 捕获myHandler消息
89.
90. 10;
91. new Bundle(); // 封装bundle信息
92. "name", "yanggang");
93. "sex", "pure boy");
94. "marry", false);
95. "age", 18);
96. msg.obj = bundle;
97.
98. long thID = Thread.currentThread().getId();
99. " : send msg start...").append("\n");
100. // 向myHandler发送消息
101.
102. 3000);
103. catch (Exception e) {
104. "", "Runnable send msg error...");
105. e.printStackTrace();
106. }
107. }
108. }
109. }
运行结果: