您的位置:首页 > 移动开发 > Android开发

android艺术开发探索之消息机制

2016-08-05 18:38 274 查看

android艺术开发探索之消息机制

handler的工作主要包含发送消息和接受消息,通过send方法发送,发送的消息被插入到MessageQueue中,MessageQueu

e的next方法会将消息返回给looper,looper接收到消息就处理,最终looper将消息交给handler的handleMessage处理。

handler的定义:

handler真的是我们所说的那样更新ui的吗?

个人认为它是具有更新ui的功能但是不是说就是刷新ui的,handler的主要作用是将一个任务切换到某个指定的线程中去执

行。

为何规定更新ui要在UI线程ActivityThread中?

ViewRootImpl对ui操作做了验证,因此只能在主线程中访问ui。但是主线程做了耗时操作会卡死出现ANR,因此将耗时操作

放在子线程。android的ui控件线程不安全,多线程并发可能导致ui处于不可预期状态,如果加锁的话会让ui访问的逻辑

变得复杂,其次锁机制会降低UI访问的效率(锁机制会阻塞某些线程的执行),因次采用单线程模型处理ui操作,也就是

使用handler切换ui访问的执行线程。

主线程的消息循环模型是啥?

ActivityThread的入口方法为main,在main方法中调用Looper.prepareMainLooper()创建主线程的looper以及Message

Queue,并通过Looper.loop()开启主线程消息循环,ActivityThread.H为主线程的handler用来和消息队列进行交互,A

ctivityThread通过ApplicationThread和AMS进行进程通信,AMS以进程通信的方式回调ApplicationThread中的binder

方法,之后ApplicationThread向ActivityThread.H发送消息,ActivityThread.H收到消息将ApplicationThread的逻辑

切换到ActivityThread中执行,即切换到主线程执行。

(我不明白为何是进程通信,比如请求网络耗时操作是在子线程完成,执行过程中要刷新ui,就要切换到主线程,这怎么也

是线程之间干的事,师父说app自己不会刷,只有系统才会刷,系统和app是进程间的通信,因此其实系统默默的干了很多

事,它很累,我们在写代码的时候不用的资源要及时释放@-@!!!)

MessageQueue、Looper、ThreadLocal

MessageQueue的工作原理

内部采用单链表的数据结构存储消息队列,里面存储了一组消息,可以插入删除消息但不能处理消息,采用looper处理消息

enqueueMessage方法是往消息队列中插入一条消息,而next是取出消息并且移除,next方法是一个无限循环方法,有消息

就返回消息并从MessageQueue中移除消息,没有就一直阻塞等待。

Looper的工作原理

Looper:处理MessageQueue中的消息,有消息就处理,无消息就阻塞等待,Looper的构造器中会创建MessageQueue,并

且当前线程对象保存,利用ThreadLocal在每个线程中存储数据,handler利用ThreadLocal获取每个线程的Looper,线程默

认无Looper,我们的ActivityThread被创建时就会初始化Looper,因此主线程可以默认使用handler.

handler在创建时会使用当前线程的Looper构建内部循环系统,通过send方法发送消息到MessageQueue,Looper发现有新

消息来时就会处理,Looper是运行在handler所在的线程中的,因此handler的作用就是将一个任务切换到某个指定的线程

中去执行。

handler工作需要Looper,Looper创建如下:

new Thread(){
@Override
public void run() {
Looper.prepare();
Handler handler=new Handler();
Looper.loop();//只有调用此方法消息循环系统才会真正起作用,该方法死循环,跳出条件为next方法
//返回null,也就是在looper推出的时候,next方法阻塞致使looper一直阻塞
// getMainLooper();//获取主线程的Looper
}
}.start();


ThreadLocal的工作原理

1.ThreadLocal:它是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定的

线程中获取存储的数据,对其他的线程来说则获取不到,Looper的作用域是线程,并且不同的线程拥有不同的Looper。

2.当某些数据以线程为作用域并且不同的线程具有不同的数据副本时,就可以采用ThreadLocal,否则得写一个LooperMan

ger类了,但是系统并未这样做,这就体现了ThreadLocal的好处。

3.ThreadLocal的另一使用场景是复杂逻辑下的对象传递,比如监听器的传递,采用ThreadLocal可以让监听器作为线程

内的全局对象而存在,线程内部通过get获取监听器。

demo如下:

private ThreadLocal<Boolean> mBooleanThreadLocal=new ThreadLocal<>();
mBooleanThreadLocal.set(true);
Log.e("结果", "主线程" + mBooleanThreadLocal.get());
new Thread("子线程1"){
@Override
public void run() {
Log.e("结果","子线程1"+mBooleanThreadLocal.get());
}
}.start();
new Thread("子线程2"){
@Override
public void run() {
mBooleanThreadLocal.set(true);
Log.e("结果","子线程2"+mBooleanThreadLocal.get());
}
}.start();


打印日志如下:

08-08 11:49:25.423 2306-2306/com.efrobot.robot.autonomic E/结果: 主线程true
08-08 11:49:25.431 2306-2334/com.efrobot.robot.autonomic E/结果: 子线程1null
08-08 11:49:25.459 2306-2335/com.efrobot.robot.autonomic E/结果: 子线程2true


如果ThreadLocal数据不是分开的,那么子线程1的结果应该是true,但是这里却输出null,这说明了不同的线程访问同一个ThreadLocal,他们取出的是各自

线程中存储的数据,不同线程的数据互不干扰,读写操作也仅限于各自线程内部。ThreadLocal.values是专门用来存储ThreadLocal值得,底层有一个数组table,它的存储位置总是不一样的,因此取出的

值不一样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: