本文主要是介绍安卓Handle的深入剖析和使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
在公司开发项目你不能说handle用的不多,反正这种更新主线程的机制是必须要懂的。面试的时候也总会叫你回答handle、looper、MessageQueen和Message的区别,所以你不仅仅只是会用而且必须要知道handle的运行机制。本文参考了很多的博主的文章见解,包括里面的原理和图解(http://blog.csdn.net/itachi85/article/details/8035333)。
handle:是安卓程序的一套更新ui的机制,它也是一套消息的处理机制,所以我们既可以用它来创建、发送消息,也能用它来处理消息,如果我们不遵循这些机制的话,程序就会crash。
那么ui线程是不能直接更新吗?
一般情况是不能的,但是还是有特殊情况:比如直接在子线程里更新textView的值,不会报错但是如果加了Threed.sleep()就会报错,因为textview的源码里面会有Inviliad()一直追溯父类会发现里面有个viewRoot的checkThread()方法,而activity的生成准确的说是OnResume()然后里面会创建rootImp,所以判断一下就会报错。所以还是推荐用handle机制来进行ui刷新。
一、handle是处理Ui线程的消息机制
直接在UI线程中开启子线程来更新TextView显示的内容,运行程序我们会发现,如下错 误:android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.翻译过来就是:只有创建这个控件的线程才能去更新该控件的内容。
安卓为什么只能通过Handle机制更新ui线程呢?
最根本的原因就是处理多线程的并发问题。假如在一个activity里面有多个线程去更新ui的操作并且都没有进行加锁的机制,那么可能出现界面错乱和线程堵塞。而handle机制给我们一套解决并发问题的机制,提供了Handler 和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。
线程可以分为:ui线程(ActivityThread)和渲染线程(RenderThread):<surfaceView(就包含了这种机制,所以只需拿到getHandle()就能够进行ui的刷新),然后我们用的AsyncTask(AsyncTask的本质是一个线程池,所有提交的异步任务都会在这个线程池中的工作线程内执行,当工作线程需要跟UI线程交互时,工作线程会通过向在UI线程创建的Handler)>.
主线程一般不做耗时操作(如果UI线程花太多时间处理后台的工作,当UI事件发生时,让用户等待时间超过5秒而未处理,Android系统就会给用户显示ANR提示信息,广播10秒,服务20秒)。
那什么是ui线程(主线程)呢?
有时候面试的时候也会问我们安卓的程序入口,通常我们也会说application,但是你一定要知道我们的主线程就是ActivityThread,所以我们就有必要去了解安卓的程序的消息机制了。它管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),并根据AMS的要求(通过IApplicationThread接口,AMS为Client、ActivityThread.ApplicationThread为Server)负责调度和执行activities、broadcasts和其它操作。
那么我们来看下IApplicationThread接口
由上图我们可以看到ActivityThread 有几个比较重要的成员变量,会在创建ActivityThread对象时初始化。
(1)final ApplicationThread mAppThread = new ApplicationThread();
ApplicationThread继承自ApplicationThreadNative, 而ApplicationThreadNative又继承自Binder并实现了IApplicationThread接口。IApplicationThread继承自IInterface。这是一个很明显的binder结构,用于于Ams通信。IApplicationThread接口定义了对一个程序(linux的进程)操作的接口。ApplicationThread通过binder与Ams通信,并将Ams的调用,通过下面的H类(也就是Handler)将消息发送到消息队列,然后进行相应的操作,入activity的start, stop。
(2)final H mH = new H();
private final class H extends Handler
mH负责处理ApplicationThread发送到消息队列的消息,所以handle是贯穿整个安卓程序的消息机制的传递。
(3). handleLaunchActivity(r, null);
从中就可以看出,这里就将进行启动activity的工作了。
方法中主要调用了这一句:
Activity a = performLaunchActivity(r, customIntent);
(4)performLaunchActivity()
进行了一些初始化和赋值操作后,创建activity。
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
然后调用:
mInstrumentation.callActivityOnCreate(activity, r.state);
这一句就会调用到acitivity的onCreate方法了,就进入了大多数应用开发的入口了,所以handle的重要性不言而喻了吧。
二、Handler的重点剖析
先看handle、looper、MessageQueen和Message的关系。
2)Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出)所送来的消息。
3) Message Queue(消息队列):用来存放线程放入的消息。
4)线程:UIthread 通常就是main thread,而Android启动程序时会替它建立一个MessageQueue。
1.Handler创建消息
Handler通过创建消息来完成接收和处理UI的更新。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。消息的创建流程如图所示。
2.Handler发送消息
UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。Looper初始化的时候会创建一个消息队列MessageQueue。至此,主线程、消息循环、消息队列之间的关系是1:1:1。
Handler、Looper、MessageQueue的初始化流程如图所示:
3.Handler处理消息
UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。
子线程通过Handler、Looper与UI主线程通信的流程如图所示。
那么什么情况下面我们的子线程才能看做是一个有Looper的线程呢?我们如何得到它Looper的句柄呢?
Looper.myLooper(); //获得当前的Looper
Looper.getMainLooper () ; //获得UI线程的Lopper
我们看看Handle的初始化函数,如果没有参数,那么他就默认使用的是当前的Looper,如果有Looper参数,就是用对应的线程的Looper。
如 果一个线程中调用Looper.prepare(),那么系统就会自动的为该线程建立一个消息队列,然后调用 Looper.loop();之后就进入了消息循环,这个之后就可以发消息、取消息、和处理消息。这个如何发送消息和如何处理消息可以再其他的线程中通过 Handle来做,但前提是我们的Hanle知道这个子线程的Looper,但是你如果不是在子线程运行 Looper.myLooper(),一般是得不到子线程的looper的。
三、handel的使用
我们先来看handle的构造:
** * Use the provided {@link Looper} instead of the default one and take a callback* interface in which to handle messages. Also set whether the handler* should be asynchronous. * * Handlers are synchronous by default unless this constructor is used to make* one that is strictly asynchronous. * * Asynchronous messages represent interrupts or events that do not require global ordering* with represent to synchronous messages. Asynchronous messages are not subject to* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}. * * @param looper The looper, must not be null. * @param callback The callback interface in which to handle messages, or null. * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. * * @hide */ public Handler(Looper looper, Callback callback, boolean async) {mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
handle的looper是一定不为空的默认会执行prepareMainLooper()方法,然后放入ThreadLocal池中用来处理多线程,而callBack{}是一个接口的回调,当我们使用的时候调用里面的handleMessage(msg)时是一个带返回值的,我们可以通过对这个返回值值的控制来打断handle的消息传递机制,如果我们返回的是false的话那么就不会打断handle对消息的处理,如果返回值设置为true那么将不会执行handle的消息处理只会执行callback{}里面的handleMessage()方法,不过我们是一般是不会这么去做的。
常见的使用刷新ui的方法:
A:常见方法:(1)runOnUiThread(runnale) (2)view.post()
上述2方法看源码就知道里面也是调用了handle的方法,或者本来就是ui线程里面操作
B、handle刷新ui的方法包括:(1)handle post (2)handle sendMessage,
1 handle post
handle不和Message配套使用的时候,直接使用handle.post(runable)其实你没有看到Message对象作为参数的传入,但是里面已经是通过looper.loop()已经push了一个message对象到消息队列里用来进行ui刷新。你传入一个runnable对象那么Handle就会调用
private static Message getPostMessage(Runnable r) {Message m = Message.obtain(); m.callback = r; return m; }
其实这里面同时调用了sendMessageDelayed(getPostMessage(r), 0)的方法,而后面那个参数赋值为0代表的是delayMillis(延迟毫秒数),可见postDelayed(r,delayMillis)和
handle.post(runable)是同一个方法的不同表现形式。
2 handle sendMessage
使用handle必然是离不开Message
Message对象参数我们必须熟悉里面包括:what,obj,data,target,arg;其中我们通过what来区分不同的消息处理,obj是一个object对象是便于我们传递任一的对象,data是一个bundle对象,target是一个handle对象,arg用于传递一个整数值,所以我们使用时可以根据自己的需要来设置。
Message对象的生成:(1)handle.obtanMessage() (2)new meassage()
这两种方法推荐使用前一个,第一种是一个Message的池来管理
public static Message obtain(Handler h) {Message m = obtain(); m.target = h; return m; }
其中这target就相当于this,然后当池中的Message对象为空的时候就相当于第二种方法
public static Message obtain() {synchronized (sPoolSync) {if (sPool != null) {Message m = sPool; sPool = m.next; m.next = null; sPoolSize--; return m; }}return new Message(); }
然后handle可以使用handler.dispatchMessage(msg);handel.sendMessage(),handle.sendEmptyMessage()等就不说了,自己去看代码。
使用注意:
1、使用handle的时候如果在子线程去创建,会报handle没有准备的错误
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:121)
at com.cao.android.demos.handles.HandleTestActivity$MyThread$1.<init>(HandleTestActivity.java:86)
at com.cao.android.demos.handles.HandleTestActivity$MyThread.run(HandleTestActivity.java:86)
所以我们就要指定looper对象,使用new handle(Looper.getMainLooper())来处理。
2、.Handler对Activity finish影响。
在开发的过程中碰到一个棘手的问题,调用Activity.finish函数Acitivity没有执行生命周期的ondestory函数,后面查找半天是因为有一个handler成员,因为它有一个delay消息没有处理,调用Activity.finish,Activity不会马上destory,所以记得在Ativity finish前清理一下handle中的未处理的消息,这样Activity才会顺利的destory
这篇关于安卓Handle的深入剖析和使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!