资讯专栏INFORMATION COLUMN

安卓中handler原理与生产者消费者模型比较

TNFE / 346人阅读

摘要:本文主要说明内部消息机制用模拟进行演示并用图简单说明并与生产者与消费者模型进行比对代码地址需要解决的问题主线程怎样跟子线程进行通信子线程生产的资源如何传给主线程子线程如何进行等待完成耗时操作才给主线程传递消息为何只能在主线程才能创建子线程想

本文主要说明android内部消息机制,用java模拟进行演示,并用图简单说明.并与生产者与消费者模型进行比对;

git代码地址

需要解决的问题:
1,主线程怎样跟子线程进行通信,子线程生产的资源,如何传给主线程?
2,子线程如何进行等待,完成耗时操作才给主线程传递消息?
3,为何只能在主线程才能创建handler,子线程想创建该怎么办?
4,主线程如何与handler/message/looper...进行结合的?
建议带着问题直接看源码,放到eclipse或者as或者intellj中实际运行后,理解handler运行原理;

1 生产者与消费者模型:

github地址:生产者与消费者
代码中展示,生产者与消费者是分别是两个线程,生产或者消耗的资源是num,而Resource3类是个生产线(queue),
通过java的BlockingQueue对生产与消费进行阻塞,最终通过main方法创建线程进行生产与消费;

2 handler在android中既是生产者也是消费者

github代码
看代码后理解下图:

其中子线程的是真正的生产者,把生产后的结果给了message.obj,然后handler.sendMessage进行入列messagequeue(给了生产线);
 @Override
            public void onResponse(Call call, Response response) throws IOException {
                  final String json =   response.body().string();
                //Log.e("json", "success: "+json.toString() );
                if (null!=json && !"".equals(json)&&(!json.contains("请求错误")&&(!json.contains("ConnectionRefused"))))
                    alarmList = GsonUtils.jsonToList(json, Alarm.class);
                    Message message = Message.obtain();
                    message.what = 0 ;
                    message.obj = alarmList;
                    handler.sendMessage(message);

            }
之后looper进行dispatch之后,再进行handlerMessage在handler进行消费;可见,android中handler的机制与生产者消费者模型最大区别是生产者消费者分别是两个线程,而handler既扮演生产者又是消费者却只是主线程的对象!
handler是通过消息传递(message)把json解析出来的东西,传递给主线程的messagequeue,然后handler又自己进行处理,这当中handler必须等待子线程生产完成(耗时操作完成);
文章开始提出的第二个问题:2,子线程如何进行等待,完成耗时操作才给主线程传递消息? 其中原理不是利用java的blockqueue之类.其中messagequeue是个链表,阻塞机制与底层的C++相关联: 深入理解messagequeue这文章没有看懂,希望可以抛砖引玉;
另外,handler处理完message之后如何又循环把message填入队尾的机制也不是很理解;
3,为何只能在主线程才能创建handler....? 4,主线程如何与handler/message/looper...进行结合的?(通过threadlocal这个map)

如下图解释:handler为何只能在主线程以及如何保生产消费是同一个handler

其中looper的角色是管理既是管理messagequeue的,又是分发消息给handler的中间者,

handler类图如下,可见handler中既有looper,又有messagequeue作为成员变量

主线的main方法中有looper.papare(),子线程没有,给localThread这个map进行设置值:
threadlocal这个map进行set,其中key=主线程,val=looper(looper又有messagequeue)
源码中,主线程:
在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()方法。查看ActivityThread中的main()

public static void main(String[] args) {  
SamplingProfilerIntegration.start();  
CloseGuard.setEnabled(false);  
Environment.initForCurrentUser();  
EventLogger.setReporter(new EventLoggingReporter());  
Process.setArgV0("");  
Looper.prepareMainLooper();  
ActivityThread thread = new ActivityThread();  
thread.attach(false);  
if (sMainThreadHandler == null) {  
    sMainThreadHandler = thread.getHandler();  
}  
AsyncTask.init();  
if (false) {  
    Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));  
}  
Looper.loop();  
throw new RuntimeException("Main thread loop unexpectedly exited");  
}  

请注意Looper.prepareMainLooper():

public static final void prepareMainLooper() {  
prepare();  //这步相当于对threadlocal进行set,key=主线程,val=looper(looper又有messagequeue)
setMainLooper(myLooper());  
if (Process.supportsProcesses()) {  
    myLooper().mQueue.mQuitAllowed = false;  
}  
}  

原理基本结束:其他的运用如下,道理也是相通的,
理解AsyncTask的源码
Android:主线程如何向子线程发送消息
至于hander.post()的有不错的文章:Handler中post方法的调用流程和使用场景

Queue的为何必须配合message.what?

message入队的时候可能是按照某种顺序直接添加,但是出队的时候可能是按照子线程生产产品时间最短的先出队进行处理(或者队列重排序(个人感觉可能性不大)),这点与生产消费的
模型完全不同,如果按照生产一个,消费一个,生产时间长的排在前面,先来后到的话,生产时间短的排在后面就会一直得不到消费,用户体验一定差;
这个时候,一定需要一个电话号码(message.what),这时候handler就相当于交换机,通知消费者该如何处理,因为子线程的生产时间是随机,谁也不知道messagequeue排序是如何的,开发者不能根据子线程耗时长短估计处理顺序,万一遇到处理时间相同,消费逻辑不同又该怎么办,所以一定要用what区分。

为何子线程不直接把结果压入messagequeue中?

一开始感觉安卓完全可以做个标志,类似于msg.what的标志,作为一个map的key直接压入队列messagequeue,之后主线程直接取这个get这个标志就可以了,然后自己写对应的逻辑就可以了,但这会造成在子线程主线程直接与messageQueue进行交互,既要处理子线程间的竞争,这时又要对主线程进行休眠唤醒等操作,对messageQueue的开启无限循环再get到相应的message,还要处理对子线程的操作,这些复杂的操作都在与主线程的交互sendMessage的时候完成;

当然文章开始提出的那几个问题没有完全解决,但是通过这篇文章,能看到安卓中UI线程与子线程交互的巧妙之处;

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/72308.html

相关文章

  • 金三银四,2019大厂Android高级工程师面试题整理

    摘要:原文地址游客前言金三银四,很多同学心里大概都准备着年后找工作或者跳槽。最近有很多同学都在交流群里求大厂面试题。 最近整理了一波面试题,包括安卓JAVA方面的,目前大厂还是以安卓源码,算法,以及数据结构为主,有一些中小型公司也会问到混合开发的知识,至于我为什么倾向于混合开发,我的一句话就是走上编程之路,将来你要学不仅仅是这些,丰富自己方能与世接轨,做好全栈的装备。 原文地址:游客kutd...

    tracymac7 评论0 收藏0
  • 2019 Android 高级面试题总结

    摘要:子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此的循环并不会对性能有过多的消耗。 说下你所知道的设计模式与使用场景 a.建造者模式: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 使用场景比如最常见的AlertDialog,拿我们开发过程中举例,比如Camera...

    wums 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<