有的没的
主线程:也叫UI线程,或称为Activity Thread,用于运行四大组件和处理与用户之间的交互,Activity Thread管理应用进程的主线程的执行,相当于普通java程序的main入口,在Activity系统中,在默认情况下,一个应用程序内的各个组件都会在同一个进程中执行,且由此进程的主线程负责执行。Activity Thread在处理不过来时可以创建多个子线程来处理后台服务工作,而本身专心处理UI画面的事件。
子线程:用于执行耗时操作,比如I/O操作和网络请求等,更新UI的工作必须交给主线程,子线程是不允许更新UI的。
消息机制:不同线程之间的通信,在Android中就是指Handler运行机制。消息机制是为了避免ANR(Application Not Responding),一旦发生ANR,程序就崩溃了。
以下两个条件任意一个触发的时候就会发生ANR:
① 在activity中超过5秒的时间未能相应下一个事件;
② BroadcastReceive超过10秒为响应
避免ANR:
① 主线程不能执行耗时操作
② 子线程不能直接更新UI界面
在Android的世界里,当子线程在执行耗时操作的时候,不是说主线程就阻塞在那里等待子线程的完成,也不是调用Thread.wait()或者是Thread.sleep(),android采取的是让主线程为子线程提供一个Handler,以便完成时能够提交给主线程。
为什么系统不允许子线程更新UI:
因为UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,那为什么不对UI控件的访问加上上锁机制呢,原因有两个:
① 上锁会让UI控件变得复杂和低效;
② 上锁后会阻塞某些进程的执行。
Handler的消息处理主要有五个部分组成,Message,Handler,Message Queue,Looper和ThreadLocal。
Message:Message在线程之间传递消息,它可以在内部携带少量的数据,用于线程之间交换数据,它有四个常用的字段,what、arg1、arg2、obj,what、arg1、arg2可以携带整型数据,obj可以携带object对象。
Handler:主要用于发送和处理消息,消息的发送一般使用sendMessage()方法,还有一系列的sendXXX方法,但最终都是为了调用sendMessageAtTime方法(除了sendMessageAtFrontOfQueue方法)。发出的消息经过一系列的处理后,最终会传递到Handler的handleMessage方法中。
Message Queue:是指消息队列,主要用于存放所有通过Handler发送的消息,这部分消息会一直存在于消息队列中,等待被处理,每个线程中只会有一个MessageQueue对象。
Looper:每个线程通过Handler发送的消息都保存在MessageQueue中,Looper通过调用loop()方法,就会进入到一个无限循环当中,每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage方法中。每个线程只会有一个Looper对象。
ThreadLocal:MessageQueue对象和Looper对象在每个线程中都只有一个,那么怎么保证他只有一个呢,就是通过ThreadLocal来保存。ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定的线程中可以获取到存储的数据,对于其他线程来说则无法获取到。
创建Handler
每个程序都有一个入口,Android是基于java的,java程序的入口是静态的main函数,因此Android程序的入口也应该为静态的main函数,在Android程序中这个静态的main在ActivityThread类中,废话不多说,贴源码:
在该方法中的红色框处,系统调用了Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环。看下这个方法的源码:
首先在prepareMainLooper方法中调用了prepare()方法,在prepare()方法中先判断ThreadLocal中是否已经存在Looper对象了,以保证每个线程中只有一个Looper对象,再看Looper对象的创建,在其构造函数中创建出了一个MessageQueue对象并保存了当前线程。从上面的分析可以看出,一个线程只有一个Looper对象,而MessageQueue是在Looper构造函数中创建出来的,因此每一个线程也就只有一个MessageQueue对象。
那么问题来了,为什么一定要新建Looper对象才能使用Handler呢,看Handler的构造函数:
在红框处调用了myLooper()方法,看源码:
这里就是获取ThreadLocal中是否含有Looper对象,明白啦,要使用Handler必须要有Looper对象才可以,Looper对象在哪建的还记得么,在Looper.prepare()方法中!经过上面这一通的分析想说明什么呢,就是在主线程中,系统通过调用prepareMainLooper()方法,已经为我们自动调用了Looper的prepare方法,也就是已经为我们新建好了Looper对象、ThreadLocal对象和MessageQueue对象,但在子线程中,就必须要主动调用Looper的prepare方法了。(没错这就是想要说明的结论!)
发送消息
new一个Message对象,然后可以使用setData()方法或者arg参数等方式为消息携带一些数据,再借助Handler将消息发送出去就可以了(正好复习一下message发送与接收数据)。
Handler中提供了很多个发送消息的方法,其中除了sendMessageAtFrontOfQueue()这个方法之外,其他的发送消息方法最终都会辗转调用到sendMessageAtTime()方法中,源码如下:
sendMessageAtTime()方法将接收到的两个参数传递给MessageQueue的enqueueMessage()方法中,首先这两个参数是啥呢,msg就是我们发送的Message对象,uptimeMillis表示发送消息的时间,等于从系统开机到当前时间的毫秒数再加上延迟时间。MessageQueue是一个消息队列,用于将所有收到的消息以链表的形式进行排列,并提供消息入队和出队的方法,那么enqueueMessage()就是入队的方法了,看源码:
从红框框出来的地方可以看到,所谓的入队就是将所有的消息按照时间来进行排序,如果使用的是sendMessageAtFrontOfQueue方法来发送消息就是将其时间设定为0,也就完成了添加消息到队列头部的操作。
接收消息
在主线程中系统不仅帮我们自动调用了Looper.prepareMainLooper()方法,还帮我们调用了Looper.loop()方法,看下源码:
从while(true)开始进入一个死循环,不断的调用MessageQueue的next()方法,这个next方法就是消息队列出队的方法,它的简单逻辑就是如果当前MessageQueue中存在待处理的消息mMessage,就将这个消息出队,然后让下一条消息成为mMessage,否则就进入一个阻塞状态,一直等到有新的消息入队。在loop()方法中,每当有一个消息出队,就将它传递到msg.target的dispatchMessage()方法中,这个msg.target就是Handler,看这个方法的源码:
如果mCallback不为空,就调用mCallback的handleMessage方法,否则直接调用Handler的handleMessage方法,并将消息对象作为参数传递过去。所以当我们复用handleMessage方法的时候就可以从中获取到发送的消息了。整个过程如下:
网友评论