来自以下博客总结.
Title | Link |
---|---|
android中handler用法总结 | http://blog.sina.com.cn/s/blog_77c6324101016jp8.html |
android消息处理机制handler与message | http://blog.csdn.net/ahuier/article/details/17012005 |
android多线程之Handler | http://www.cnblogs.com/plokmju/p/android_handler.html |
android多线程之Looper | http://www.cnblogs.com/plokmju/p/android_Looper.html |
Looper详解 | http://blog.sina.com.cn/s/blog_5da93c8f0100y4ul.html |
Looper,Handler,Thread | http://li-bonan.blog.163.com/blog/static/13556477020112245111485/ |
从源码解析Handler | http://blog.csdn.net/guolin_blog/article/details/11711405 |
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操作
在UI线程获取,处理信息
常用的方法有两大体系: Post 和 sendMessage
postDelayed(Runnable,long)
sendEmptyMessage(int)
sendMessageDelayed(Message,long)
不管是post还是sendMessage都具有多种方法,它们可以设定Runnable对象和Message对象被入队到消息队列中,是立即执行还是延迟执行。
对于Handler的Post方式来说,它会传递一个Runnable对象到消息队列中,在这个Runnable对象中,重写run()方法。一般在这个run()方法中写入需要在UI线程上的操作。
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()发送消息到消息队列中。
public class HandlerTest extends AppCompatActivity {
Button handlerButton;
TextView handlerText;
Handler handler=new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
handlerButton=(Button)findViewById(R.id.handlerButton);
handlerText=(TextView)findViewById(R.id.handlerText);
handlerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new MyThread()).start();
}
});
}
class MyThread implements Runnable
{
@Override
public void run() {
//另开一个线程执行耗时操作(获取质数),通过handler 的post 方法将 Runnable对象传递给信息队列
//信息队列获得 Runnable对象后立刻执行 run 方法,对UI 进行操作
final Listlists=getPrime();
handler.post(new Runnable() {
@Override
//这里的方法是在主线程中执行的.
public void run() {
handlerText.setText(lists.toString());
}
});
}
}
//获取1000以内的质数
ArrayListgetPrime()
{
ArrayListlist=new ArrayList<>();
OK:
for(int i=2;i<=1000;i++)
{
int x=(int)Math.sqrt(i);
for(int j=2;j<=x;j++)
if(i%j==0)
continue OK;
list.add(i);
}
return list;
}
}
public class HandlerTest extends AppCompatActivity {
public static int GETPRIME=0X123;
Button handlerButton;
TextView handlerText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
handlerButton=(Button)findViewById(R.id.handlerButton);
handlerText=(TextView)findViewById(R.id.handlerText);
handlerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new MyThread()).start();
}
});
}
Handler handler=new Handler()
{
@Override
public void handleMessage(Message msg) {
if(msg.what==GETPRIME) {
Listlists=(List)msg.obj;
handlerText.setText(lists.toString());
}
}
};
class MyThread implements Runnable
{
@Override
public void run() {
//在线程中执行耗时操作,通过Message对象传递list给handler,然后在handler中执行ui操作
final Listlists=getPrime();
Message msg=Message.obtain();
msg.what=GETPRIME;
msg.obj=lists;
handler.sendMessage(msg);
}
}
//获取1000以内的质数
ArrayListgetPrime()
{
ArrayListlist=new ArrayList<>();
OK:
for(int i=2;i<=1000;i++)
{
int x=(int)Math.sqrt(i);
for(int j=2;j<=x;j++)
if(i%j==0)
continue OK;
list.add(i);
}
return list;
}
}
public class HandlerMessageActivity2 extends Activity {
private Button btn1, btn2, btn3, btn4,btn5;
private static TextView tvMes;
private static Handler handler = new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
if (msg.what == 3||msg.what==5) {
tvMes.setText("what=" + msg.what + ",这是一个空消息");
} else {
tvMes.setText("what=" + msg.what + "," + msg.obj.toString());
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.message_activity2);
tvMes = (TextView) findViewById(R.id.tvMes);
btn1 = (Button) findViewById(R.id.btnMessage1);
btn2 = (Button) findViewById(R.id.btnMessage2);
btn3 = (Button) findViewById(R.id.btnMessage3);
btn4 = (Button) findViewById(R.id.btnMessage4);
btn5 = (Button) findViewById(R.id.btnMessage5);
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 使用Message.Obtain+Hander.sendMessage()发送消息
new Thread(new Runnable() {
@Override
public void run() {
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "使用Message.Obtain+Hander.sendMessage()发送消息";
handler.sendMessage(msg);
}
}).start();
}
});
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 使用Message.sendToTarget发送消息
new Thread(new Runnable() {
@Override
public void run() {
Message msg = Message.obtain(handler);
msg.what = 2;
msg.obj = "使用Message.sendToTarget发送消息";
msg.sendToTarget();
}
}).start();
}
});
btn3.setOnClickListener(new View.OnClickListener() {
// 发送一个延迟消息
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
handler.sendEmptyMessage(3);
}
}).start();
}
});
btn4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
Message msg = Message.obtain();
msg.what =4;
msg.obj = "使用Message.Obtain+Hander.sendMessage()发送延迟消息";
handler.sendMessageDelayed(msg, 3000);
}
}).start();
}
});
btn5.setOnClickListener(new View.OnClickListener() {
// 发送一个延迟的空消息
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
handler.sendEmptyMessageDelayed(5, 3000);
}
}).start();
}
});
}
}
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
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框
架就可以在其线程中将消息发送到调用者的线程消息队列之中,不用担心线程同步的问题。
package com.example.handlerlooperdemo;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class WorkThreadActivity extends Activity {
private Button btnSendToWorkUI;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.looper_activity2);
// 在UI线程中开启一个子线程
new Thread(new Runnable() {
@Override
public void run() {
// 在子线程中初始化一个Looper对象
Looper.prepare();
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 把UI线程发送来的消息显示到屏幕上。
Log.i("main", "what="+msg.what+","+msg.obj);
Toast.makeText(WorkThreadActivity.this, "what="+msg.what+","+msg.obj, Toast.LENGTH_SHORT).show();
}
};
// 把刚才初始化的Looper对象运行起来,循环消息队列的消息
Looper.loop();
}
}).start();
btnSendToWorkUI=(Button)findViewById(R.id.btnSendToWorkUI);
btnSendToWorkUI.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// onClick方法是运行在UI线程上的
Message msg=Message.obtain();
msg.what=1;
msg.obj="向子线程中发送消息!";
// 向子线程中发送消息
handler.sendMessage(msg);
}
});
}
}
通过上面的Demo可以看到,虽然Handler有两个构造函数,一个需要传递一个Looper对象,一个不需要,但是对于UI线程而言,使用哪个其实效果是一样的,因为Activity会帮我们维护Looper对象。
而对于子线程而言,没有任何对象帮助我们维护Looper对象,所以需要我们自己手动维护。这时候就需要使用prepare()和loop()方法了。下面通过一个Demo来演示一下在UI线程中给子线程发送消息的实现。注意,因为
子线程中做的事情,也无法这里实现,比如访问网络、操作UI组件。
public class HandlerTest extends AppCompatActivity {
Button handlerButton;
TextView handlerText;
Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
handlerButton=(Button)findViewById(R.id.handlerButton);
handlerText=(TextView)findViewById(R.id.handlerText);
new Thread(new MyThread()).start();
handlerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//运行在UI线程,向子线程发送消息
Message msg=Message.obtain();
msg.what=0x321;
msg.obj=handlerButton.getText().toString();
handler.sendMessage(msg);
}
});
}
class MyThread implements Runnable {
@Override
public void run() {
//在子线程中初始化一个消息队列
Looper.prepare();
handler=new Handler()
{
@Override
public void handleMessage(Message msg) {
if(msg.what==0x321){
String s=(String)msg.obj;
Log.d("HANDLER", s);
//handlerText.setText(s);如果调用这一句将会报错,因为不能在子线程修改UI
}
}
};
//把初始化的Looper对象运行起来,循环消息队列的消息
Looper.loop();
}
}
}