本文主要说明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代码
看代码后理解下图:

handler既是生产与也是消费者

其中子线程的是真正的生产者,把生产后的结果给了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是阻塞的核心,本质也是线程的睡眠与唤醒+生产与消费: 深入理解messagequeue;
3,为何只能在主线程才能创建handler....?
4,主线程如何与handler/message/looper...进行结合的?(通过threadlocal这个map)

如下图解释:handler为何只能在主线程以及如何保生产消费是同一个handler
handler为何只能在主线程以及如何保生产消费是同一个handler
其中looper的角色是管理既是管理messagequeue的,又是分发消息给handler的中间者,

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

主线的main方法中有looper.papare(),子线程没有,给localThread这个map进行设置值:
threadlocal<T>这个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("<pre-initialized>");  
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线程与子线程交互的巧妙之处;


20171112
10 声望1 粉丝