Android-消息处理学习总结(Handler,Looper)
参考资料:
http://www.cnblogs.com/qlky/p/5657924.html
http://blog.csdn.net/guolin_blog/article/details/9991569
http://blog.csdn.net/gh102/article/details/7191486
http://www.cnblogs.com/plokmju/p/android_Handler.html
http://www.jianshu.com/p/02962454adf7
http://www.jianshu.com/p/ac50ba6ba3a2
可以看到有这么多的资料,内容也很多,看的眼花缭乱。我决定自己总结一下,从最简单的开始,再慢慢补细节。
Handler
Handler,它直接继承自Object,一个Handler允许发送和处理Message或者Runnable对象,并且会关联到主线程的MessageQueue中。每个Handler具有一个单独的线程,并且关联到一个消息队列的线程,就是说一个Handler有一个固有的消息队列。当实例化一个Handler的时候,它就承载在一个线程和消息队列的线程,这个Handler可以把Message或Runnable压入到消息队列,并且从消息队列中取出Message或Runnable,进而操作它们。
-
作用
android不允许在主线程里做耗时操作,如网络操作,以此来避免ANR。
ANR(Application Not Responding)
http://baike.baidu.com/link?url=rLzKRNkjt79XITQKhRXp32alhsuKEt2FoHPw3vuB2UlEvyKOZwnEh4OYoPy4_fwO6zPPECXWre4ycip4mB0LOq
Activity应该在它的关键生命周期方法(如onCreate()和onResume())里尽可能少的去做创建操作。潜在的耗时操作,例如网络或数据库操作,或者高耗时的计算如改变位图尺寸,应该在子线程里(或者以数据库操作为例,通过异步请求的方式)来完成。
默认情况下,在android中Activity的最长执行时间是5秒,BroadcastReceiver的最长执行时间则是10秒。
因此如果想进行上述的操作,应该开启一个子线程。而在子线程中,android不允许进行UI操作。如果想在子线程中进行UI操作,就可以使用Handler开启UI线程。
-
用法
Handler有两种用法:
- Post:Post允许把一个Runnable对象入队到消息队列中。它的方法有:post(Runnable)、postAtTime(Runnable,long)、postDelayed(Runnable,long)。
- sendMessage:sendMessage允许把一个包含消息数据的Message对象压入到消息队列中。它的方法有:sendEmptyMessage(int)、sendMessage(Message)、sendMessageAtTime(Message,long)、sendMessageDelayed(Message,long)。
具体看这里:http://www.cnblogs.com/plokmju/p/android_Handler.html
两个实例
第一个是post的,处理在子线程修改UI
public class HandlerPostActivity1 extends Activity { private Button btnMes1,btnMes2; private TextView tvMessage; // 声明一个Handler对象 private static Handler handler=new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.message_activity); btnMes1=(Button)findViewById(R.id.btnMes1); btnMes2=(Button)findViewById(R.id.btnMes2); tvMessage=(TextView)findViewById(R.id.tvMessage); btnMes1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 新启动一个子线程 new Thread(new Runnable() { @Override public void run() { // tvMessage.setText("..."); // 以上操作会报错,无法再子线程中访问UI组件,UI组件的属性必须在UI线程中访问 // 使用post方式修改UI组件tvMessage的Text属性 handler.post(new Runnable() { @Override public void run() { tvMessage.setText("使用Handler.post在工作线程中发送一段执行到消息队列中,在主线程中执行。"); } }); } }).start(); } }); } }
第二个是Message的,子线程改变UI
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(); } }); } }
-
原理
从作用可以知道,需要解决在子线程修改UI的问题。而UI只能在主线程修改,所以问题就变成了怎么让子线程随时能告诉主线程该怎么做。
android给出的方案是这样的:
1.因为代码执行完会结束,而主线程需要随时响应不能结束,所以主线程需要在一个死循环里面等待消息:Looper
2.主线程需要在开启死循环前,设立一个接受和处理消息的机制(包括跳出循环的消息):Handler
3.需要规定消息的种类和载体:Message
4.同一线程在同一时间只能处理一个消息,所以需要保存消息的顺序和时间,一条条拿出来处理:MessageQueue
5.由于同一进程中线程和线程之间资源是共享的,所以任何线程都可以获取到MessageQueue实例,然后向主线程发送消息
所以Handler实际上就是主线程接收和处理消息的一个封装。在子线程new Handler()时帮你获得MessageQueue实例,并封装发送消息的方法。在主线程MessageQueue处理消息时又封装了Handler来处理消息。
一个最标准的异步处理线程(也是将普通线程转成Looper线程的方法):
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
三种在子线程改变UI的方法
1. Handler的post()方法
2. View的post()方法
3. Activity的runOnUiThread()方法
Handler.post
我们先来看下Handler中的post()方法,代码如下所示:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
其实就是sendMessageDelayed(),再看getPostMessage()
private final Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
dispatchMessage()方法中有做一个检查,如果Message的callback等于null才会去调用handleMessage()方法,否则就调用handleCallback()方法
private final void handleCallback(Message message) { message.callback.run(); }
所以所谓callback就是子线程我们创建的runnable,然后在主线程里执行它的run方法
这时再看post用法就懂了:
public class MainActivity extends Activity { private Handler handler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); handler = new Handler(); new Thread(new Runnable() { @Override public void run() { handler.post(new Runnable() { @Override public void run() { // 在这里进行UI操作 } }); } }).start(); } }
View中的post()方法
public boolean post(Runnable action) { Handler handler; if (mAttachInfo != null) { handler = mAttachInfo.mHandler; } else { ViewRoot.getRunQueue().post(action); return true; } return handler.post(action); }
用的就是handler的post,不解释了
Activity中的runOnUiThread()方法
public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
如果当前的线程不等于UI线程(主线程),就去调用Handler的post()方法,否则就直接调用Runnable对象的run()方法。还有什么会比这更清晰明了的吗?
为什么要用Message.obtain()而不是new Message();
Message Pool消息池
public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
我们通过obtain方法取出一条消息的时候,如果发现当前的消息池不为空,那就直接重复利用Message(已经被创建过和handle过的);如果为空就重新new 一个消息。这就是一种享元设计模式的概念。例如在游戏里面,发子弹,如果一个子弹是一个对象,一按下按键就发很多个子弹,那么这时候就需要利用享元模式去循环利用了。
Handler与Android四大组件生命周期
http://www.jianshu.com/p/ac50ba6ba3a2
除了客户端的handler外,还有系统handler,用来处理系统的操作消息:比如启动Activity等四大组件
一小段代码,应用程序的入口:
public final class ActivityThread { public static final void main(String[] args) { ...... Looper.prepareMainLooper(); ...... ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } ...... Looper.loop(); ...... } }
ActivityThread
并不是一个线程,它并没有继承Thread,而只是一个普通的类public final class ActivityThread{...}
ActivityThread的构造函数并没有做什么事只是初始化了资源管理器。thread.attach(false);
便会创建一个Binder线程(具体是指ApplicationThread
,该Binder线程会通过想Handler
将Message
发送给主线程,之后讲)- 在Looper.loop()中进入死循环
插入一个问题:
主线程是UI线程和用户交互的线程,优先级应该很高,主线程的死循环一直运行是不是会特别消耗CPU资源吗?App进程的其他线程怎么办?
- 这基本是一个类似生产者消费者的模型,简单说如果在主线程的MessageQueue没有消息时,就会阻塞在loop的queue.next()方法里,这时候主线程会释放CPU资源进入休眠状态,直到有下个消息进来时候就会唤醒主线程,在2.2 版本以前,这套机制是用我们熟悉的线程的wait和notify 来实现的,之后的版本涉及到Linux pipe/epoll机制,通过往pipe管道写端写入数据来唤醒主线程工作。原理类似于I/O,读写是堵塞的,不占用CPU资源。
系统Handler
final H mH = new H();
在new ActivityThread的时候,系统的Handler就就初始化了,这是一种饿加载的方法,也就是在类被new的时候就初始化成员变量了。另外还有一种懒加载,就是在需要的时候才去初始化,这两种方式在单例设计模式里面比较常见。
系统是怎么发消息给主线程的,主线程是怎么处理这些个消息的?
在准备启动一个Activity的时候,系统服务进程下的ActivityManagerService
(简称AMS)线程会通过Binder发送IPC调用给APP进程,App进程接到到调用后,通过App进程下的Binder线程最终调用ActivityThread
类下面的scheduleLaunchActivity
方法来准备启动Activity
注:Binder线程:具体是指ApplicationThread,在App进程中接受系统进程传递过来的信息的线程(在主线程进入死循环之前创建了这个线程)。
看下scheduleLaunchActivity方法:
//这个方法不是在主线程调用,是Binder线程下调用的 public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { updateProcessState(procState, false); ActivityClientRecord r = new ActivityClientRecord(); .... sendMessage(H.LAUNCH_ACTIVITY, r); }
把启动一些信息封装成ActivityClientRecord
之后,最后一句调用sendMessage(H.LAUNCH_ACTIVITY, r);
再看这个方法:
private void sendMessage(int what, Object obj) { sendMessage(what, obj, 0, 0, false); } private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) { Message msg = Message.obtain(); msg.what = what; msg.obj = obj; msg.arg1 = arg1; msg.arg2 = arg2; if (async) { msg.setAsynchronous(true); } mH.sendMessage(msg); }
很清楚了,APP下的Binder就是用系统handler来启动activity的
总结