Android中的Handler及它所引出的Looper、MessageQueue、Message
- 最早也最基本:有的任务需要大量的时间,但其实并不占用计算资源,比如等待外界输入,比如发起网络连接,于是当然就想到完全可以在等待的时候做另一件事,而因为程序都是单线顺序执行的,如何才能同时做两件事呢,那就是开启第二个线程,也就是多线程
- 对于多核CPU,你把一个任务给多个CPU干,那就只能开多线程
- 所以就是这两种情况要多线程:一个CPU同时做多件事;多个CPU同时做一件事
- 但其实类似方法,启动方法的时候可以传入数据(参数)和任务代码(回调),方法中本身也有写死的任务代码,返回时只返回数据,数据直接返回
- 线程的话,启动线程的时候可以直接传入数据(参数)和任务代码(回调),线程中本身也有写死的任务代码,返回时只返回数据,数据通过多线程通信机制返回
- 把A线程进行改造,把这个顺序执行的线程改造成一个【先顺序执行一些代码,然后进入一个主动等待状态】的线程,在这个线程处于主动等待状态时,一收到B线程通过多线程机制发来的数据,就做一些事情
- 另外,为了防止阻塞,还构建了一个排队队列,能把发来的数据缓存起来,然后再进一步把A线程改造成【循环从排队队列中取数据】的线程,这样取到数据,再一个个处理
- 有了以上这个半成品机制之后,你要怎么实现【B线程把代码段委托给A线程执行】呢
- 一个很直接的想法是,B线程把要执行的代码段写在一个对象的方法中,然后把这个对象发送给A线程,A线程取到对象之后进行处理时就执行这个对象的这个方法
- 这个直接的想法从思路上来说很简单,但是实际上,线程代码都很短,都只是为了做某件具体的麻烦的事情,而且都是写在run()方法中,这样到时候要发送对象时,就要new一个内部类了,然后写方法,这样的方式不能说不行吧,只是下面有一种更方便的做法
- 另外好像同样是这种做法,安卓官方想了个方式。。。传一个runable对象,也就是一个线程。。。这个暂时不管
- 对的!是真的!安卓真的这么做了,下面有写:每个msg中有一个Runnable类型的成员callback,当调用post方法发送一个Runnable对象时就进行这样的处理--把Runnable对象存入msg的这个callback成员中,之后收到msg后,第一件事就是判断msg的这个成员是不是null,如果是,才调用回调方法,如果不是就意味着传来一个Ruannable对象,就会直接执行它的run方法,因为此时不是用.start()方法,所以就是在本线程直接执行,这其实也是一种回调。而且所以是Runnable对象的话,也完全不会执行handMessage钩子方法
- 所以其实是两种实现都有,但是把这种实现包装成下一种实现,而且这种不用实现handMessage钩子方法。所以下文主要用下一种实现为中心来介绍没有问题
- 于是有了一种实现起来简洁很多,思路上看起来绕了但其实也同样很简单的做法:把代码段事先就在A线程的一个方法中,并且给个序号b-1,然后B线程只要发来一个简单的数据“b-1”,然后A线程取到这个数据之后,通过比对也可以非常方便地调用序号为b-1的方法
- 这个方法还会凑巧提供一种便利,那就是,因为run()的子线程代码是直接写在当前Activity类的onCreat()方法中,由于【启动一个线程其实就是启动一个方法,成员的权限管理应该是跟在单线程一样的,只是你得知道这个方法中的代码运行在另一个线程上而已】 于是run()方法不用自己new handler对象(而且他默认new出的对象是子线程的,不是目标线程的),它可以直接调用在当前Activity类中New的handler对象(在主线程中New的,绑定主线程),这样在run()方法中传递数据又方便了很多
- OK,就是如此,A线程就是Activity线程,B线程就是A线程开启的一个网络获取数据的子线程,对A线程进行改造的就是Looper,发送者是在B线程中new的Handler对象,发去的数据是Message对象,接收者还是Looper,缓存在其中的MessageQueue对象中,取出者分发者是Looper,它调用的具体处理消息者也是Handler对象(在A线程new的),被委托的代码段被预先写在Handler对象的方法中
- 消息原型
- 消息队列
- 发送消息
- 消息循环
- 消息获取
- 消息派发
- 消息处理
![](/Users/BigBear/AppData/Local/Temp/enhtmlclip/Image(21).png)
![](/Users/BigBear/AppData/Local/Temp/enhtmlclip/Image(22).png)
![](/Users/BigBear/AppData/Local/Temp/enhtmlclip/Image(23).png)
![](/Users/BigBear/AppData/Local/Temp/enhtmlclip/Image(24).png)
- Message
- 就是消息原型
- 其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
- MessageQueue
- 就是消息队列,
- 消息队列由Looper所持有,但是消息的添加是通过Handler进行;
- 用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
- Looper
- 消息泵,实现Thread的消息循环和消息派发
- 不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
- 缺省情况下Thread是没有这个消息循环的既没有Looper;需要主动去创建,然后启动Looper的消息循环loop;与外部的交互通过Handler进行;
- Handler
- 处理者,驾驭整个消息系统模型,统领Message,MessgeQueue和Looper;
- 负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
![](/Users/BigBear/AppData/Local/Temp/enhtmlclip/Image(25).png)
![](/Users/BigBear/AppData/Local/Temp/enhtmlclip/Image(26).png)
![](/Users/BigBear/AppData/Local/Temp/enhtmlclip/Image(27).png)
- Looper类是用来使一个普通线程变成Looper线程的类
- Looper线程是什么
- 一个普通线程是从头执行到尾,然后结束
- 而Looper线程却会从头执行到某个循环起点,然后进入循环,一直执行,直到接受到某些信号,然后结束
- 在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务
- Looper线程是什么
- Looper类是怎么做到的呢
- 原理
- 非常简单,就是在普通线程的最后加一个循环,这个循环用来接收并处理来自handle的msg
- 非常简单,就是在普通线程的最后加一个循环,这个循环用来接收并处理来自handle的msg
- 具体实现(在线程中(比如run()方法中)一前一后加入两句代码,如源码一)
- Looper.prepare();
- 在线程中(这个说法挺可疑)new了一个Looper对象(如源码二)
- 这个Looper对象与某个(一般就是这个)Thread绑定了,一个Thread只能绑定一个Looper对象,因为这个Looper对象是ThreadLocal类型的
- 具体怎么做到new出的Looper对象是ThreadLocal类型的呢
- 机制其实就是 Looper对象本身就是一个ThreadLocal对象管理器
- 调用Looper对象提供的方法Looper.prepare(),它就能在线程中(这个说法挺可疑)new了一个ThreadLocal类型的 Looper对象
- 具体怎么做到new出的Looper对象是ThreadLocal类型的呢
- 这个Looper对象内有成员MessageQueue,他就是系统提供的消息队列类,loop()方法调用后线程开始不断从队列中取出消息执行
- Looper的构造函数Looper()被声明为private,也就是说,在外部,我们不能直接的使用Looper的构造函数,只能用这个prepare方法来
- 这个Looper对象与某个(一般就是这个)Thread绑定了,一个Thread只能绑定一个Looper对象,因为这个Looper对象是ThreadLocal类型的
- 在线程中(这个说法挺可疑)new了一个Looper对象(如源码二)
- Looper.loop();(如源码二)
- 当执行到这一步之后,调用loop()方法,这是个循环方法,于是线程就停在这进行循环
- 循环着做什么呢,它不断从自己的MQ中取出队头的消息(也叫任务)执行
- 写在Looper.loop()之后的代码不会被立即执行,当调用后 mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
- Looper.prepare();
- 原理
- 安卓默认把所有的activity都默认设置成了Looper(大概就是在基类中)
- 所以活动线程是这么工作的:当Android应用启动后,系统会默认创建一个主线程(Main thread)。这个主线程启动后,首先完成UI的绘制,然后会进入一个消息循环(Loop),等待和执行各种来自系统的消息和事件、各种用户点击/触摸事件、其他线程发送的消息事件等等。
- 这是线程工作的一种常见的模式,即进入一种“等待命令/消息 ”->“执行命令/消息”->“等待命令/消息”的循环
- 那么,其他非UI线程如何与进入了消息循环的主线程交互呢?这就得靠Handler了。
- Activity是一个UI线程,运行于主线程中,Android系统在启动的时候会为Activity创建一个消息队列和消息循环(Looper)。详细实现请参考ActivityThread.java文件。
- Android应用程序进程在启动的时候,会在进程中加载ActivityThread类,并且执行这个类的main函数,应用程序的消息循环过程就是在这个main函数里面实现的
- Looper和Handler都在本线程中(Looper 是静态方法,Handler 是直接实例化),可以当做是在run()方法中
- Looper的prepare()方法负责改造线程
- Looper的成员(关联)中有MessageQueue ,它能做默默两件大事:1)有一个接收机制,默默接受各个Handle对象(只要地址对,从哪儿发的都行;从任意线程发到任意目标线程)发送来的msg;2)有一个队列专门把接收到的msg排列好等待循环来取
- Looper的loop()方法负责发动一个【不断取MessageQueue中的Message对象,并把它发给各个Handle对象(msg.target指定的)去处理】的循环
- Looper的方法中(依赖)实例化了Message对象,能承载信息,Message对象本身能被放在MessageQueue中(或者说它是MessageQueue类能处理的唯一指定类型)
- handler可以在任意线程发送消息,这些消息会被添加到关联的MQ上,handler是在它关联的looper线程中处理消息的
- 重温msg对象的发送和处理过程:【发送者】是B线程中的【绑定了A线程Looper的Handler对象】,【接收者和中介者】是A线程的Looper(被存在它里边的MQ中),然后被looper转发给【最终接收者也是处理者】--A线程中的【绑定了A线程Looper的Handler对象】(这一步的形式是looper拥有(不是直接持有,而是msg中传过来的)handler的引用,并直接调用它的处理方法)
- handler扮演了往MessageQueue上添加消息和处理消息(在被分配msg之后执行该任务)的角色(只处理由自己发出的消息),整个过程是异步的
- 为什么发送者和处理者都用Handler,其实也可以不用,只是这样looper调用对应handler的处理方法时更方便
- 两个线程中Handler使用情况对比:
- B线程中的【绑定了A线程Looper的Handler对象】new出来之后,只调用它的发送方法把数据发送出去
- A线程中的【绑定了A线程Looper的Handler对象】new出来之后,只需要写一下它的处理代码,然后等待被looper调用这个方法
- 注意
- 这个Handler对象要么是1)直接实例化Handler抽象类然后传入一个实现Handler.Callback接口的对象,要么是2)继承了Handler类并实现了其中的回调方法的子类的对象
- 其实这里的两个对象可以是同一个对象(作为参数传递来传递去的),原理上来说也可以是各自new类的对象 ,但是一般来说这个你实现了的接口或者是继承了的子类会被设置成private内部类,在外不能被调用,这样你就可能需要把同一个对象传来传去
- 直接在本线程中实例化Handler(如源码四),Handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以set的,如源码三
- 一个线程可以有多个Handler(类),也可以有多个同一个类的handler对象(N*N),但只能有1个looper对象
- PS:其实handler能做的远远不仅如此,由于它能post Runnable对象,它还能与Looper配合实现经典的Pipeline Thread(流水线线程)模式
- 在本线程new出了绑定目标线程的Looper的handler对象之后,我们就可以使用handler对象的这些方法向MQ上发送消息
- 发送Runnable对象消息方法
- post(Runnable)
- postAtTime(Runnable, long)
- postDelayed(Runnable, long)
- 发送message对象消息方法
- sendEmptyMessage(int)
- sendMessage(Message)
- sendMessageAtTime(Message, long)
- sendMessageDelayed(Message, long)
- 发送Runnable对象消息方法
- 看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了,如源码五
- 后文在android举例中,有展示很多种使用Handler发送消息的方式,但是主要是三种,而这三种其实都是基于message的,所以本文讲原理时候只以message为中心的(但是所有方法都会介绍到)。一共有这三种
- 使用message
- 使用runable
- 实现Handle.Callback
- 用handle对象从从任意线程往任意目标线程任意目标handle对象发送message的过程(两个地址)
- 发到目标线程确实是通过填写目标线程地址来实现的
- 但是这个目标线程地址不是写在msg中
- 也不是发送时作为参数和msg一块传给Handle对象
- 他是在new Handle对象的时候指定的(指定的方式是绑定目标线程的Looper对象),也就是说,你要往哪个线程发送msg,你就造一个绑定了这个线程的Handle对象(默认情况下绑定当前线程),也就是说,一个Handle对象只能往某个固定的线程发送msg
- 但同时,可以有多个Handle对象绑定同一个线程,而前面说了:每个Handle对象只处理由自己发出的消息,那loop()方法分发msg的时候怎么知道是哪一个Handle对象呢(这些handle对象绑定的是同一个线程)
- 非常简单,就是靠发送msg时,系统默认会把当前的handle的引用也发过去(其实也就是靠这个引用调用对应Handle对象的处理方法,所以可以看出这是一个回调方法)
- 如源码五,msg.target为该handler对象,这确保了looper执行到该message时能找到处理它的handler
- loop中的相关代码是:msg.target.dispatchMessage(msg);
- 发到目标线程确实是通过填写目标线程地址来实现的
![](/Users/BigBear/AppData/Local/Temp/enhtmlclip/2011091409532564.png)
- 在上面说的目标线程中,必须也new一个绑定本线程的handler对象,并写一个会被调用的的方法(其实不叫回调,因为这个还是你写的代码调用你写的代码)(这个有点像模板模式中的钩子方法,由某个指令决定是否执行?)
- 消息的处理是通过两个方法:dispatchMessage(Message msg);handleMessage(Message msg)完成的,见源码六
- 这里有个绕绕,就是其实是先回调dispatchMessage(Message msg) 方法,这个方法中会做一些判断(因为runable数据的问题),判断某一种情况下,再调用这个handleMessage(Message msg)
![](/Users/BigBear/AppData/Local/Temp/enhtmlclip/2011091409541179.png)
![](/Users/BigBear/AppData/Local/Temp/enhtmlclip/2011091323582123.png)
于是 onCreate 中的相关代码变成如下了:
final Handler mainThreadHandler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
}
}).start();
于是上面的代码简化为:
new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
runOnUiThread(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
}
}).start();
你不用自己创建一个 Handler了。
而 runOnUiThread 的具体实现,也跟我们做得差不多。UI线程即主线程。
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
现在先思考一下,我们上面的代码可能遇到的问题。 比如,我们现在要显示一个图片列表。 一百多张图片。 如果每下载一张就开一个线程的话,那一百多个线程,那系统资源估计支持不住。特别是低端的手机。
正确的做法是使用一个线程池。
final ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downloadImage(beautyUrl);
runOnUiThread(new Runnable() {
@Override
public void run() {
mBeautyImageView.setImageBitmap(bitmap);
}
});
executor.shutdown();
}
});
new AsyncTask<String,Void,Bitmap>(){
@Override
protected Bitmap doInBackground(String... params) {
return downloadImage(params[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
mBeautyImageView.setImageBitmap(bitmap);
}
}.execute(beautyUrl);
看一下 AsyncTask 的源代码,正是集合我们之前考虑的这些东西。
- 一个全局的线程池
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
- 一个绑定主线程的 Handler ,在线程中处理传递的消息
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
- 使用message
- 准备:
- 在某处有一个类,是一个子类,【继承自Handler抽象类】,并把你要插入运行的代码写在其中的回调方法handleMessage( )中
- 发送端:
- 在【任意线程任意位置】获得(实例化/直接持有)1中这个类的一个对象,并为这个对象绑定目标线程的looper,然后使用这个对象的setmessage( )方法把它自己和要发送的message对象发送出去
- 处理端:
- 在【目标线程目标位置】获得(实例化/直接持有)1中这个类的一个对象,并为这个对象绑定目标线程的looper(当然在目标位置的话一般默认就行),然后等待被looper调用并进行处理
- 准备:
- 使用Handle.Callback
- 准备:
- 在某处有一个类,是一个实现类,【实现了Handler.Callback接口】,并且把你要插入运行的代码写在其中的handleMessage( )方法中
- 发送端:
- 在【任意线程任意位置】获得(实例化/直接持有)Handler抽象类的一个对象,并在构造方法中传入1中这个类的一个对象,并为这个对象绑定目标线程的looper,然后使用这个对象的setmessage( )方法把它自己和要发送的message对象发送出去
- 处理端:
- 在【目标线程目标位置】获得(实例化/直接持有)Handler抽象类的一个对象,并在构造方法中传入1中这个类的一个对象,并为这个对象绑定目标线程的looper,然后等待被looper调用并进行处理
- 准备:
- 使用runnable
- 准备:
- /
- 发送端:
- 在【任意线程任意位置】获得(实例化/直接持有)Handler抽象类的一个对象,并为这个对象绑定目标线程的looper,然后使用这个对象的post( )方法把它自己和要发送的runnable对象发送出去,要插入运行的代码写在runnable的run( )方法中
- 处理端:
- 在【目标线程目标位置】获得(实例化/直接持有)Handler抽象类的一个对象,并为这个对象绑定目标线程的looper,然后等待被looper调用并进行处理
- 准备:
- 使用默认方法绑定looper
- 在目标线程目标位置进行准备
- (处理端一定是在目标线程目标位置的)发送端也尽量在目标位置(肯定是不在目标线程的),以便直接调用handler对象
- 发送端不在目标位置,但是处理端把hander对象以参数的方式发送给发送端
public
class
HandlerDemo {
private
Handler myHandler1 =
new
Handler(
new
MyHandlerCallback());
private
Handler myHandler2 =
new
MyHandler();
private
class
MyHandlerCallback
implements
Handler.Callback {
@Override
public
boolean
handleMessage(Message msg) {
// Handle messages.
return
false
;
}
}
private
class
MyHandler
extends
Handler {
@Override
public
void
handleMessage(Message msg) {
// Handle messages.
}
}
}
public
void
dispatchMessage(Message msg) {
if
(msg.callback !=
null
) {
handleCallback(msg);
}
else
{
if
(mCallback !=
null
) {
if
(mCallback.handleMessage(msg)) {
return
;
}
}
handleMessage(msg);
}
}
- 首先看这个message是不是使用了runnable,也就是看这个message对象的callback成员是不是为空
- 如果不为空,而是里边有runnable,就直接调用其run()方法,然后结束
- 如果为空,就下一步判断,先不管message,而是判断本handler对象创建时是不是传入了实现了Handler.Callback接口的对象(就是本handler对象的对应属性中有没有这个接口实现对象)
- 如果不为空,而是传入了Handler.Callback接口的对象,那就调用这个接口对象的的处理方法【
mCallback
.handleMessage(msg)
】来处理msg,正常情况下这个处理方法处理完了会返还true,于是return,方法结束 - 如果为空,就直接调用本handler对象的处理方法来处理这个msg,结束
- 如果不为空,而是传入了Handler.Callback接口的对象,那就调用这个接口对象的的处理方法【
- 只要它承载了《代码c-run》,那么就只执行它,其他的不管有没有都不执行
- 如果它没有承载《代码c-run》,如果同时承载了《代码c-Callback》和《代码c-msg》,那么《代码c-Callback》一定会被执行,《代码c-msg》会不会执行要看msg中的数据:
- 如果数据让《代码c-Callback》的执行结果为true,那么《代码c-msg》不执行
- 如果数据让《代码c-Callback》的执行结果为false,那么《代码c-msg》执行
- 对,估计我这个猜测是对的,这是基于 dispatchMessage处理时 《代码c-Callback》的执行结果会返还一个boolean值,这个值会影响那么《代码c-msg》的执行
- 如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存
- 擅用message.what来标识信息,以便用不同方式处理message。
- 获取新的Message对象时, 尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,Message提供了obtain方法:避免我们自己去分配Message新的对象, 通过obtain获取,可能从MessagePool中获取,节约开销。因为:
- 通常消息处理完毕的时候,消息也基本上处于无用状态可以释放回收了。对于需要频繁的创建释放的对象来说,创建和释放类实例都是要开销的,太频繁的使开销增大不好,像Message这种很有可能会频繁的创建
- 于是我们可以将创建的对象用完之后保存在一个Pool里面,以便再重复利用节约频繁创建释放开销。
//标识消息
long when;
//传递简单数据
//传递较复杂数据 对象
public Object obj;
Bundle data;
//处理消息的目标Handler
Handler target;
//消息派发时 执行的Runnable对象
Runnable callback;
//使消息形成链表
Message next;
//建立一个消息pool,回收msg,以避免重复创建节约开销
//派发消息 msg.target.dispatchMessage(msg);
//消息处理完毕 回收
msg.recycle();
}
}
public void recycle() {
//回收Message 建立全局的MessagePool
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
- Handler与Thread及Looper的关系可以用下面图来表示:
- 注意工作线程和主线程之间的竞争关系。推荐handler对象在主线程中构造完成(并且启动工作线程之后不要再修改之,否则会出现数据不一致),然后在工作线程中可以放心的调用发送消息SendMessage等接口。
- 除了2所述的hanlder对象之外的任何主线程的成员变量如果在工作线程中调用,仔细考虑线程同步问题。如果有必要需要加入同步对象保护该变量。
- handler对象的handleMessage接口将会在主线程中调用。在这个函数可以放心的调用主线程中任何变量和函数,进而完成更新UI的任务。
- Android很多API也利用Handler这种线程特性,作为一种回调函数的变种,来通知调用者。这样Android框架就可以在其线程中将消息发送到调用者的线程消息队列之中,不用担心线程同步的问题。
- 并非每一个线程都有消息处理循环,因此 Framework 中线程可以分为两种:有 Looper 的和无 Looper 的。为了方便 app 开发,Framework 提供了一个有 Looper 的 Thread 实现:HandlerThread。两种不同 Thread 的 run() 方法有区别。
- Looper循环线程跟while什么的循环一样吗,它循环的到底是哪段代码
- 哈哈,其实就是while循环,没什么不一样,循环的就是循环内的代码,具体情况看我的关于Looper类的表述
- 发送的时候为什么要分msg和执行代码,直接发送执行代码不行吗
- 已在正文中解决
- 安卓中的线程进程是什么样的,跟activity有什么关系,活动切换会新建线程吗?
- 正常情况下一个APP启动就开启了一个进程,它也一直就只有这一个进程
- 然后每个这样的进程中有一个主线程,可以有很多子线程
- 这个主线程也叫UI线程,只有这个线程能绘制UI界面,对UI进行修改
- 一个 app 可以有多个 activity, 但是他们的 activity 都是在同一个线程中进行绘制的,所以只有一个主线程,也就是他们都运行在同一个线程上
- 所有耗时的、复杂的操作都不应该在主线程中运行,这样会导致程序占用主线程资源,而导致界面卡顿。
- 其实:几乎所有的GUI程序(android,javaswing,winform)都会使用一个线程来完成界面的显示。这个线程叫做主线程,或者event dispacture thread(edt ,事件派发线程)。这个概念在所有的GUI程序中都存在。
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// ...其他处理,如实例化handler
// 开始循环处理消息队列
Looper.loop();
}
}
private static final ThreadLocal sThreadLocal = new ThreadLocal();
// Looper内的消息队列
final MessageQueue mQueue;
// 当前线程
Thread mThread;
// 。。。其他属性
// 每个Looper对象中有它的消息队列,和它所属的线程
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
if (sThreadLocal.get() != null) {
// 试图在有Looper的线程中再次创建Looper将抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
MessageQueue queue = me.mQueue; //得到当前looper的MQ
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
public Handler() {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mCallback = null;
}
}
private Handler handler2;
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// 实例化两个handler
handler1 = new Handler();
handler2 = new Handler();
// 开始循环处理消息队列
Looper.loop();
}
}
private final Message getPostMessage(Runnable r) {
}
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this; // message的target必须设为该handler!
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
if (msg.callback != null) {
if (this.mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
}
}
// 处理runnable消息
}
private
class
MyHandlerCallback
implements
Handler.Callback {
@Override
public
boolean
handleMessage(Message msg) {
// Handle messages.!注意,实现这个接口覆盖这个方法的时候要返回值,这个值比较重要
return
false
;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textview = (TextView) findViewById(R.id.textview);
}
}
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
String result = msg.getData().getString("message");
// 更新UI
appendText(result);
}
}
}
private static final String TAG = SampleTask.class.getSimpleName();
Handler handler;
public SampleTask(Handler handler) {
super();
this.handler = handler;
}
@Override
public void run() {
try { // 模拟执行某项任务,下载等
Thread.sleep(5000);
// 任务完成后通知activity更新UI
Message msg = prepareMessage("task completed!");
// message将被添加到主线程的MQ中
handler.sendMessage(msg);
} catch (InterruptedException e) {
Log.d(TAG, "interrupted!");
}
}
private Message prepareMessage(String str) {
Message result = handler.obtainMessage();
Bundle data = new Bundle();
data.putString("message", str);
result.setData(data);
return result;
}
Android中的Handler及它所引出的Looper、MessageQueue、Message的更多相关文章
- Android中使用Handler造成内存泄露的分析和解决
什么是内存泄露?Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用所指向 ...
- Android中使用Handler造成内存泄露
1.什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用 ...
- Android中的Handler的机制与用法详解
概述: 很多android初学者对android 中的handler不是很明白,其实Google参考了Windows的消息处理机制, 在Android系统中实现了一套类似的消息处理机制.在下面介绍ha ...
- android中的Handler和Runnable
最近在做一个项目,在网络请求时考虑用Handler进行处理,然后就研究了一下Handler和Runnable 首先在看一下java中的Runnable The Runnable interface s ...
- Android中利用Handler实现消息的分发机制(三)
在第二篇文章<Android中利用Handler实现消息的分发机制(一)>中,我们讲到主线程的Looper是Android系统在启动App的时候,已经帮我们创建好了,而假设在子线程中须要去 ...
- 转:Android中的Handler的机制与用法详解
注:Message类的用法: message的几个参数都可以携带数据,其中arg1与arg2可以携带int类型,what是用户自定义的int型,这样接受者可以了解这个消息的信息. 说明:使用Messa ...
- 深入源代码解析Android中的Handler,Message,MessageQueue,Looper
本文主要是对Handler和消息循环的实现原理进行源代码分析.假设不熟悉Handler能够參见博文< Android中Handler的使用>,里面对Android为何以引入Handler机 ...
- Android中使用Handler以及CountDownTimer实现包含倒计时的闪屏页面
上一篇博文<Android中Handler使用浅析>通过实现倒计时闪屏页面的制作引出了Handler的使用方法以及实现原理,博文末尾也提到了实现过程中的Bug,有兴趣的朋友可以点击链接回去 ...
- Android中使用Handler以及CountDownTimer实现包括倒计时的闪屏页面
上一篇博文<Android中Handler使用浅析>通过实现倒计时闪屏页面的制作引出了Handler的用法以及实现原理,博文末尾也提到了实现过程中的Bug,有兴趣的朋友能够点击链接回去看看 ...
随机推荐
- Mac 下,修改plist文件
/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName $DISPLAY_NAME" "${PROJECT_TEMP_D ...
- 【Raspberry Pi】openwrt 路由
http://blog.sina.com.cn/s/blog_40983e5e0102v6qt.html
- 查询_修改SQL Server 2005中数据库文件存放路径
1.查看当前的存放路径: select database_id,name,physical_name AS CurrentLocation,state_desc,size from sys.maste ...
- Canny边缘检测——学习笔记
Sobel Canny 非极大值抑制NMS,上表为角度,下表为灰度 26度,在0-45°之间,离45°更近. 把不是极大值的点改为0,这样边缘会细很多. 双阈值判定 深度优先遍历
- Win7配置SVN详细步骤(服务器和客户端)
下载并安装服务器端SVN VisualSVN Server 下载并安装客户端SVN TortoiseSVN 创建SVN库 在C盘创建文件夹MySVN(可自由命名),打开文件夹----右键Torto ...
- PHP-006
Q:应用程序执行时的路径 "D:\*******\protected\runtime" 是无效的. 请确定它是一个可被 Web server process 写入资料的目录. A: ...
- 解决Bootstrap布局注册表单input标签前增加必填项*提示与input框不在同一行问题
注册表单部分代码如下: <form id="registForm" class="form-horizontal" action="${page ...
- redis安装之zmalloc.h:55:2: error: #error "Newer version of jemalloc required"错误
redis是C语言编写的软件,安装前需要编译,需要gcc编译环境,确认安装gcc编译环境后(安装gcc命令:yum install gcc-c++) 在redis解压目录下,确认有Makefile文件 ...
- 查看进程动态:top
top命令用于查看进程动态,即进程使用系统资源的情况,常见用法如下: [root@localhost ~]$ top # 动态查看进程使用资源的情况,每三秒刷新一次 [root@localhost ~ ...
- LeetCode——Basic Calculator II
Description: Implement a basic calculator to evaluate a simple expression string. The expression str ...