关于Looper的使用
Android中的Looper类,是用来封装消息循环和消息队列的一个类,用于在android线程中进行消息处理。handler其实可以看做是一个工具类,用来向消息队列中插入消息的。
(1) Looper类用来为一个线程开启一个消息循环。 默认情况下android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环。) Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。
(2) 通常是通过Handler对象来与Looper进行交互的。Handler可看做是Looper的一个接口,用来向指定的Looper发送消息及定义处理方法。 默认情况下Handler会与其被定义时所在线程的Looper绑定,比如,Handler在主线程中定义,那么它是与主线程的Looper绑定。 mainHandler = new Handler() 等价于new Handler(Looper.myLooper()). Looper.myLooper():获取当前进程的looper对象,类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。
(3) 在非主线程中直接new Handler() 会报如下的错误: E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper。
主线程(即UI线程)自身就有message loop,不需要创建,而其他线程就需要手动创建,使用prepare()创建loop,使用 loop()来启动loop,直到loop停止。
(4) Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。
注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
(5) 基于以上知识,可实现主线程给子线程(非主线程)发送消息。
1.主线程向子线程发信息:
首先创建一个子线程,在子线程中创建Looper并且运行,接收主线程向子线程发送的信息,并且处理。
ChildrenLooperThread childerLooperThread=new ChildrenLooperThread();
childerLooperThread.start();
public class ChildrenLooperThread extends Thread{
@Override
public void run() {
Looper.prepare();//创建Looper
mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
Log.e("TAG", "mHandler");
Log.e("TAG", msg.obj.toString());//接收主线程向子线程发送的信息,并且处理。
super.handleMessage(msg);
}
};
Looper.loop();//运行
super.run();
}
}
在主线程中向子线程发送信息
Message msg=mHandler.obtainMessage();
msg.obj="主线程向子线程传递数据";
mHandler.sendMessage(msg);
===============================================================================
android Handler/Looper 总结
来自以下博客总结.
Title | Link |
android中handler用法总结 | |
android消息处理机制handler与message | |
android多线程之Handler | |
android多线程之Looper | |
Looper详解 | |
Looper,Handler,Thread | http://li-bonan.blog.163.com/blog/static/13556477020112245111485/ |
从源码解析Handler |
UI多线程Androidthread工作
在一个Android 程序开始运行的时候,会单独启动一个Process。默认的情况下,所有这个程序中的
Activity或者Service(Service和 Activity只是Android提供的Components中的两种,除此之外还有
Content Provider和Broadcast Receiver)都会跑在这个Process
一个Android 程序默认情况下也只有一个Process,但一个Process下却可以有许多个Thread。在这
么多Thread当中,有一个Thread,我们称之为UI Thread。UI Thread在Android程序运行的时候
就被创建,是一个Process当中的主线程Main Thread,主要是负责控制UI界面的显示、更新和控件
交互。在Android程序创建之初,一个Process呈现的是单线程模型,所有的任务都在一个线程中运
行。因此,我们认为,UI Thread所执行的每一个函数,所花费的时间都应该是越短越好。而其他比
较费时的工作(访问网络,下载数据,查询数据库等),都应该交由子线程去执行,以免阻塞主线程。
当应用程序启动时,创建一个主线程用于管理UI.如果要执行一些耗时的操作,比如联网读取数据,如果时间过长就会导致界面假死进而因为ANR被系统提示强制关闭.但是也不能在其它线程中进行UI的操作,否则会对主线程产生危险.
于是可以使用Handler(运行在主线程) 通过 发送 Message消息,把消息放入到主线程中配合主线程进行UI操作
- Handler可以分发Message 和 Runnable 对象到主线程中
- 每个Handler实例会绑定到创建它的线程中,并与该线程的 MessageQueue 绑定
- 在工作线程发送消息
- 在UI线程获取,处理信息
常用的方法有两大体系: Post 和 sendMessage
- post(Runnable)
- postAtTime(Runnable,long)
- postDelayed(Runnable,long)
- sendEmptyMessage(int)
- sendMessage(Message)
- sendMessageAtTime(Message,long)
- sendMessageDelayed(Message,long)
不管是post还是sendMessage都具有多种方法,它们可以设定Runnable对象和Message对象被入队到消息队列中,是立即执行还是延迟执行。
Post
对于Handler的Post方式来说,它会传递一个Runnable对象到消息队列中,在这个Runnable对象中,重写run()方法。一般在这个run()方法中写入需要在UI线程上的操作。
- boolean post(Runnable r):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,立即执行。
- boolean postAtTime(Runnable r,long uptimeMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,在特定的时间执行。
- boolean postDelayed(Runnable r,long delayMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,延迟delayMills秒执行
- void removeCallbacks(Runnable r):从消息队列中移除一个Runnable对象。
Message
Handler如果使用sendMessage的方式把消息入队到消息队列中,需要传递一个Message对象,而在Handler中,需要重写handleMessage()方法,用于获取工作线程传递过来的消息,此方法运行在UI线程上。
Message是一个final类,所以不可被继承。Message封装了线程中传递的消息,如果对于一般的数据,Message提供了getData()和setData()方法来获取与设置数据,其中操作的数据是一个Bundle对象,这个Bundle对象提供一系列的getXxx()和setXxx()方法用于传递基本数据类型的键值对
对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者Handler.obtainMessage()获取。Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的,才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。并不需要担心消息池中的消息过多,它是有上限的,上限为10个
Message.obtain()方法具有多个重载方法,大致可以分为为两类,一类是无需传递Handler对象,对于这类的方法,当填充好消息后,需要调用Handler.sendMessage()方法来发送消息到消息队列中。第二类需要传递一个Handler对象,这类方法可以直接使用Message.sendToTarget()方法发送消息到消息队列中,这是因为在Message对象中有一个私有的Handler类型的属性Target,当时obtain方法传递进一个Handler对象的时候,会给Target属性赋值,当调用sendToTarget()方法的时候,实际在它内部还是调用的Target.sendMessage()方法。
在Handler中,也定义了一些发送空消息的方法,如:sendEmptyMessage(int what)、sendEmptyMessageDelayed(int what,long delayMillis),看似这些方法没有使用Message就可以发送一个消息,但是如果查看源码就会发现,其实内部也是从Message.obtain()方法中获取一个Message对象,然后给属性赋值,最后使用sendMessage()发送消息到消息队列中。
- Message obtainMessage():获取一个Message对象。
- boolean sendMessage():发送一个Message对象到消息队列中,并在UI线程取到消息后,立即执行。
- boolean sendMessageDelayed():发送一个Message对象到消息队列中,在UI线程取到消息后,延迟执行。
- boolean sendEmptyMessage(int what):发送一个空的Message对象到队列中,并在UI线程取到消息后,立即执行。
- boolean sendEmptyMessageDelayed(int what,long delayMillis):发送一个空Message对象到消息队列中,在UI线程取到消息后,延迟执行。
- void removeMessage():从消息队列中移除一个未响应的消息。
1. public class HandlerTest extends AppCompatActivity {
2.
3. Button handlerButton;
4. TextView handlerText;
5. Handler handler=new Handler();
6. @Override
7. protected void onCreate(Bundle savedInstanceState) {
8. super.onCreate(savedInstanceState);
9. setContentView(R.layout.activity_handler_test);
10.
11. handlerButton=(Button)findViewById(R.id.handlerButton);
12. handlerText=(TextView)findViewById(R.id.handlerText);
13.
14. handlerButton.setOnClickListener(new View.OnClickListener() {
15. @Override
16. public void onClick(View v) {
17. new Thread(new MyThread()).start();
18. }
19. });
20. }
21.
22. class MyThread implements Runnable
23. {
24. @Override
25. public void run() {
26. //另开一个线程执行耗时操作(获取质数),通过handler 的post 方法将 Runnable对象传递给信息队列
27. //信息队列获得 Runnable对象后立刻执行 run 方法,对UI 进行操作
28. final List<Integer>lists=getPrime();
29. handler.post(new Runnable() {
30. @Override
31. //这里的方法是在主线程中执行的.
32. public void run() {
33. handlerText.setText(lists.toString());
34. }
35. });
36. }
37. }
38.
39. //获取1000以内的质数
40. ArrayList<Integer >getPrime()
41. {
42. ArrayList<Integer>list=new ArrayList<>();
43. OK:
44. for(int i=2;i<=1000;i++)
45. {
46. int x=(int)Math.sqrt(i);
47. for(int j=2;j<=x;j++)
48. if(i%j==0)
49. continue OK;
50. list.add(i);
51. }
52. return list;
53. }
54. }
1. public class HandlerTest extends AppCompatActivity {
2.
3. public static int GETPRIME=0X123;
4. Button handlerButton;
5. TextView handlerText;
6.
7. @Override
8. protected void onCreate(Bundle savedInstanceState) {
9. super.onCreate(savedInstanceState);
10. setContentView(R.layout.activity_handler_test);
11.
12. handlerButton=(Button)findViewById(R.id.handlerButton);
13. handlerText=(TextView)findViewById(R.id.handlerText);
14.
15. handlerButton.setOnClickListener(new View.OnClickListener() {
16. @Override
17. public void onClick(View v) {
18. new Thread(new MyThread()).start();
19. }
20. });
21. }
22.
23. Handler handler=new Handler()
24. {
25. @Override
26. public void handleMessage(Message msg) {
27. if(msg.what==GETPRIME) {
28. List<Integer>lists=(List<Integer>)msg.obj;
29. handlerText.setText(lists.toString());
30. }
31. }
32. };
33.
34. class MyThread implements Runnable
35. {
36. @Override
37. public void run() {
38.
39. //在线程中执行耗时操作,通过Message对象传递list给handler,然后在handler中执行ui操作
40. final List<Integer>lists=getPrime();
41. Message msg=Message.obtain();
42. msg.what=GETPRIME;
43. msg.obj=lists;
44. handler.sendMessage(msg);
45. }
46. }
47.
48. //获取1000以内的质数
49. ArrayList<Integer >getPrime()
50. {
51. ArrayList<Integer>list=new ArrayList<>();
52. OK:
53. for(int i=2;i<=1000;i++)
54. {
55. int x=(int)Math.sqrt(i);
56. for(int j=2;j<=x;j++)
57. if(i%j==0)
58. continue OK;
59. list.add(i);
60. }
61. return list;
62. }
63. }
1. public class HandlerMessageActivity2 extends Activity {
2. private Button btn1, btn2, btn3, btn4,btn5;
3. private static TextView tvMes;
4. private static Handler handler = new Handler() {
5. @Override
6. public void handleMessage(android.os.Message msg) {
7. if (msg.what == 3||msg.what==5) {
8. tvMes.setText("what=" + msg.what + ",这是一个空消息");
9. } else {
10. tvMes.setText("what=" + msg.what + "," + msg.obj.toString());
11. }
12.
13. };
14. };
15.
16. @Override
17. protected void onCreate(Bundle savedInstanceState) {
18. // TODO Auto-generated method stub
19. super.onCreate(savedInstanceState);
20. setContentView(R.layout.message_activity2);
21. tvMes = (TextView) findViewById(R.id.tvMes);
22. btn1 = (Button) findViewById(R.id.btnMessage1);
23. btn2 = (Button) findViewById(R.id.btnMessage2);
24. btn3 = (Button) findViewById(R.id.btnMessage3);
25. btn4 = (Button) findViewById(R.id.btnMessage4);
26. btn5 = (Button) findViewById(R.id.btnMessage5);
27.
28. btn1.setOnClickListener(new View.OnClickListener() {
29. @Override
30. public void onClick(View v) {
31. // 使用Message.Obtain+Hander.sendMessage()发送消息
32. new Thread(new Runnable() {
33. @Override
34. public void run() {
35. Message msg = Message.obtain();
36. msg.what = 1;
37. msg.obj = "使用Message.Obtain+Hander.sendMessage()发送消息";
38. handler.sendMessage(msg);
39. }
40. }).start();
41. }
42. });
43.
44. btn2.setOnClickListener(new View.OnClickListener() {
45.
46. @Override
47. public void onClick(View v) {
48. // 使用Message.sendToTarget发送消息
49. new Thread(new Runnable() {
50. @Override
51. public void run() {
52. Message msg = Message.obtain(handler);
53. msg.what = 2;
54. msg.obj = "使用Message.sendToTarget发送消息";
55. msg.sendToTarget();
56. }
57. }).start();
58. }
59. });
60.
61. btn3.setOnClickListener(new View.OnClickListener() {
62. // 发送一个延迟消息
63. @Override
64. public void onClick(View v) {
65. new Thread(new Runnable() {
66. @Override
67. public void run() {
68. handler.sendEmptyMessage(3);
69. }
70. }).start();
71. }
72. });
73.
74. btn4.setOnClickListener(new View.OnClickListener() {
75.
76. @Override
77. public void onClick(View v) {
78. new Thread(new Runnable() {
79. @Override
80. public void run() {
81. Message msg = Message.obtain();
82. msg.what =4;
83. msg.obj = "使用Message.Obtain+Hander.sendMessage()发送延迟消息";
84. handler.sendMessageDelayed(msg, 3000);
85. }
86. }).start();
87. }
88. });
89.
90. btn5.setOnClickListener(new View.OnClickListener() {
91. // 发送一个延迟的空消息
92. @Override
93. public void onClick(View v) {
94. new Thread(new Runnable() {
95. @Override
96. public void run() {
97. handler.sendEmptyMessageDelayed(5, 3000);
98. }
99. }).start();
100. }
101. });
102. }
103. }
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法
来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非
实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
Android 系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一消息列列
和一个消息循环(Looper),特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。但是创建的工作
线程默认是没有消息循环和消息队列的,如果想让该线程具有消息队列和消息循环,需要在线程中首先调用
Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。
前面提到Android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一
个消息队列和一个消息循环(Looper),特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。
但是创建的工作线程默认是没有消息循环和消息队列的,如果想让该线程具有消息队列和消息循环,需要在线程
中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。
Activity是一个UI线程,运行于主线程中,Android系统在启动的时候会为Activity创建一个消息队列和消息循环
(Looper)。详细实现请参考ActivityThread.java文件。
Handler的作用是把消息加入特定的(Looper)消息队列中,并分发和处理该消息队列中的消息。构造Handler
的时候可以指定一个Looper对象,如果不指定则利用当前线程的Looper创建。
一个Activity中可以创建多个工作线程或者其他的组件,如果这些线程或者组件把他们的消息放入Activity的
主线程消息队列,那么该消息就会在主线程中处理了。因为主线程一般负责界面的更新操作,并且
Android系统中的weget不是线程安全的,所以这种方式可以很好的实现Android界面更新。在Android系统
中这种方式有着广泛的运用。
那么另外一个线程怎样把消息放入主线程的消息队列呢?答案是通过Handle对象,只要Handler对象以
主线程的Looper创建,那么调用Handler的sendMessage等接口,将会把消息放入队列都将是放入主线程
的消息队列。并且将会在Handler主线程中调用该handler的handleMessage接口来处理消息。
我们可以看出消息处理是在主线程中处理的,在消息处理函数中可以安全的调用主线程中的任何资源,包括刷新
界面。工作线程和主线程运行在不同的线程中,所以必须要注意这两个线程间的竞争关系。
在工作线程中访问了主线程handler对象,并在调用handler的对象向消息队列加入了一个消息。这个过程中
会不会出现消息队列数据不一致问题呢?答案是handler对象不会出问题,因为handler对象管理的Looper对象
是线程安全的,不管是加入消息到消息队列和从队列读出消息都是有同步对象保护的,具体请参考Looper.java
文件。上例中没有修改handler对象,所以handler对象不可能会出现数据不一致的问题。
通过上面的分析,我们可以得出如下结论:
1、如果通过工作线程刷新界面,推荐使用handler对象来实现。
2、注意工作线程和主线程之间的竞争关系。推荐handler对象在主线程中构造完成(并且启动工作线程之后不要
再修改之,否则会出现数据不一致),然后在工作线程中可以放心的调用发送消息SendMessage等接口。
3、除了2所述的hanlder对象之外的任何主线程的成员变量如果在工作线程中调用,仔细考虑线程同步问题。如
果有必要需要加入同步对象保护该变量。
4、handler对象的handleMessage接口将会在主线程中调用。在这个函数可以放心的调用主线程中任何变量和
函数,进而完成更新UI的任务。
5、Android很多API也利用Handler这种线程特性,作为一种回调函数的变种,来通知调用者。这样Android框
架就可以在其线程中将消息发送到调用者的线程消息队列之中,不用担心线程同步的问题。
1. package com.example.handlerlooperdemo;
2.
3. import android.app.Activity;
4. import android.os.Bundle;
5. import android.os.Handler;
6. import android.os.Looper;
7. import android.os.Message;
8. import android.util.Log;
9. import android.view.View;
10. import android.widget.Button;
11. import android.widget.Toast;
12.
13. public class WorkThreadActivity extends Activity {
14. private Button btnSendToWorkUI;
15. private Handler handler;
16. @Override
17. protected void onCreate(Bundle savedInstanceState) {
18. super.onCreate(savedInstanceState);
19. setContentView(R.layout.looper_activity2);
20.
21. // 在UI线程中开启一个子线程
22. new Thread(new Runnable() {
23. @Override
24. public void run() {
25. // 在子线程中初始化一个Looper对象
26. Looper.prepare();
27. handler=new Handler(){
28. @Override
29. public void handleMessage(Message msg) {
30. super.handleMessage(msg);
31. // 把UI线程发送来的消息显示到屏幕上。
32. Log.i("main", "what="+msg.what+","+msg.obj);
33. Toast.makeText(WorkThreadActivity.this, "what="+msg.what+","+msg.obj, Toast.LENGTH_SHORT).show();
34. }
35. };
36. // 把刚才初始化的Looper对象运行起来,循环消息队列的消息
37. Looper.loop();
38. }
39. }).start();
40.
41. btnSendToWorkUI=(Button)findViewById(R.id.btnSendToWorkUI);
42.
43. btnSendToWorkUI.setOnClickListener(new View.OnClickListener() {
44. @Override
45. public void onClick(View v) {
46. // onClick方法是运行在UI线程上的
47. Message msg=Message.obtain();
48. msg.what=1;
49. msg.obj="向子线程中发送消息!";
50. // 向子线程中发送消息
51. handler.sendMessage(msg);
52. }
53. });
54. }
55.
56.
57. }
通过上面的Demo可以看到,虽然Handler有两个构造函数,一个需要传递一个Looper对象,一个不需要,但
是对于UI线程而言,使用哪个其实效果是一样的,因为Activity会帮我们维护Looper对象。
而对于子线程而言,没有任何对象帮助我们维护Looper对象,所以需要我们自己手动维护。这时候就需要使用
prepare()和loop()方法了。下面通过一个Demo来演示一下在UI线程中给子线程发送消息的实现。注意,因为
这个Handler是在子线程中声明的,所以在Handler中处理消息的实现,也必须符合子线程的限制,一些无法在
子线程中做的事情,也无法这里实现,比如访问网络、操作UI组件。
1. public class HandlerTest extends AppCompatActivity {
2.
3.
4. Button handlerButton;
5. TextView handlerText;
6.
7. Handler handler;
8. @Override
9. protected void onCreate(Bundle savedInstanceState) {
10. super.onCreate(savedInstanceState);
11. setContentView(R.layout.activity_handler_test);
12.
13. handlerButton=(Button)findViewById(R.id.handlerButton);
14. handlerText=(TextView)findViewById(R.id.handlerText);
15.
16. new Thread(new MyThread()).start();
17. handlerButton.setOnClickListener(new View.OnClickListener() {
18. @Override
19. public void onClick(View v) {
20. //运行在UI线程,向子线程发送消息
21. Message msg=Message.obtain();
22. msg.what=0x321;
23. msg.obj=handlerButton.getText().toString();
24. handler.sendMessage(msg);
25.
26. }
27. });
28. }
29.
30. class MyThread implements Runnable {
31. @Override
32. public void run() {
33. //在子线程中初始化一个消息队列
34. Looper.prepare();
35. handler=new Handler()
36. {
37. @Override
38. public void handleMessage(Message msg) {
39. if(msg.what==0x321){
40. String s=(String)msg.obj;
41. Log.d("HANDLER", s);
42. //handlerText.setText(s);如果调用这一句将会报错,因为不能在子线程修改UI
43. }
44. }
45. };
46. //把初始化的Looper对象运行起来,循环消息队列的消息
47. Looper.loop();
48. }
49. }
50. }