Android中的handler机制和线程
1、主线程(UI线程)
指进程所拥有的线程,在Java中默认情况下一个进程只有一个线程,这个线程就是主线程,主要处理界面交互相关的逻辑。
2、子线程(工作线程)
除了主线程以外的线程都是子线程。
3、Android的消息机
指Handler的运行机制及Handler所附带的MessageQueue和Looper的工作过程。
4、Message(消息)
Handler接受和处理的消息对象(bean对象),主要用于通信时相关信息的存放和传递。
5、MessageQueue(消息队列)
采用单链表的数据结构来存储消息的列表,内部存储了一组消息,以队列的形式对外提供插入和删除的工作。
6、Looper(循环器,所在线程决定了Handler所在线程)
扮演消息循环的角色,它不停地从Message中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。
7、Handler(处理者)
Message的处理者和发送者,将访问UI的工作线程换到主线程中,解决在子线程中无法访问UI的矛盾。
8、系统为什么不允许在子线程中访问UI?
因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期状态。
9、系统为什么不对UI控件的访问加上锁机制呢?
首先加上锁会让UI访问的逻辑变复杂;
其次锁机制会降低UI访问的效率。
10、ThreadLocal(数据存储类)
是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则可能无法获取到数据。
11、ThreadLocal的使用场景
某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,可以考虑用它(存储looper);
复杂逻辑的对象传递。
12、ThreadLoal怎样做到数据隔离的?
因为不同的线程访问同一个ThreadLocal的get方法,ThreadLocal内部会从各自的线程中取出一个数组,然后再从数组中根据当前ThreadLocal的索引查找出对应的value值,不同线程中的数组是不同的,则它可以在不同的线程中维护一套数据的副本并且彼此互不干扰。
13、强引用、软引用、弱引用、虚引用
强引用:正常建立对象的引用,当内存空间不足,Java虚拟机宁愿抛出oom使程序异常终止,也不会靠垃圾回收器回收它来解决内存不足问题。
软引用:SoftReference<A> weakA = new SoftReference<A>(new A());如果内存空间足够,垃圾回收器就不会回收它,如果空间不足了,就会回收这些对象的内存。
弱引用:WeakReference<R> weakRef = new WeakReference<R>(new R());在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
虚引用:和没有引用几乎时一样的,随时都可能被垃圾回收器回收。
14、软引用和弱引用的使用场景?
如果只是想避免oom异常的发生就可以使用软引用,如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象可以使用弱引用;
如果该对象可能会经常使用就尽量用软引用,如果该对象不被使用的可能性更大些就可以用弱引用。
15、Handler发送消息有哪几种方式?
可以通过post的一系列方法;
以及send的一系列方法;
Post的一系列方法最终会通过send的一系列方法来实现。
16、Handler处理消息有哪几种方式?
handleCallback中调用message的callback的run方法用于处理post发送的消息;
handleMessage用于处理send发送的消息。
17、Message、Handler、MessageQueue、Looper的之间的关系?
首先MessageQueue是消息队列,用来存储Handler发送过来的消息即Message,其内部提供了进队和出队的来管理这个队列,其出队和入队的原理是采用单链表的结构进行插入和删除的,即enqueueMessage()方法和next()方法。这里提到的message是一个bean对象,里面的属性用来记录message的各种信息。
然后Looper是个循环器,它是handler和messageQueue的桥梁,它循环不断的从messageQueue中取出message给handler进行处理。它用prepare方法进行初始化,初始化的时候会关联一个messageQueue,并把Looper存储到ThreadLocal中,looper方法进行循环,在looper方法中,通过ThreadLocal得到Looper,进一步得到messageQueue,在一个无限for循环中调用messageQueue中的next方法得到message,如果message为空则阻塞直到不为空,如果message不为空,则通过message的target方法得到handler并且调用handler的dispatchMessage方法处理该messag。
最后Handler,message的发送者和处理者。使用handler必须有Looper,在handler的构造方法中有对是否有looper做了判断,如果没有则抛出异常。Handler调用Looper的myLooper得到Looper,然后用send一系列的方法或者post的一系列方法发送message,这些方法最终调用Looper中的messageQueue中的enqueueMessage方法,向messageQueue中插入一条message。我们平时在主线程中可以直接使用handler是因为应用程序启动时,在入口的main方法中创建了Looper。
18、主线程的消息循环模型
//TODO
19、HandlerThread
HandlerThread继承了Thread,在run方法中创建了一个消息队列并开启了循环,这样在实际的使用中就允许在HandlerThread中创建Handler,创建的这个Handler还是在HeadlerThread线程中,不能更新UI。
20、HandlerThread的优缺点
HandlerThread本质上是个线程类,它继承了Thread;
HandlerThread有自己的内部looper,可以进行looper循环;
可以在HandlerThread中创建Handler;
HandlerThread异步不会堵塞,减少对性能的消耗;
HandlerThread不能同时继续进行多任务处理,需要等待进行处理,处理效率低;
HandlerThread与线程池不同,它是一个串行队列,背后只有一个线程。
21、IntentService
它是一个特殊的Service,并且是一个抽象类;
它封装了HandlerThread和Handler;
它可以用于后台执行耗时的异步任务,当任务完成后自动停止;
它拥有较高的优先级,不易被系统杀死
创建它时,只需要实现onHandlerIntent方法,可以执行耗时操作;
可以多次启动它,但它的实例只有一个,每个耗时操作都会以工作队列的方式在onHandlerIntent中执行,每次只执行一个工作线程。
22、AsyncTask
它封装了Thread和Handler,是一种轻量级的异步任务类,它可以在线程中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。
23、AsyncTask实现原理
24、AsyncTask使用方法
Params、Progress、Result三个参数可以用Void代替。
onPreExecute,在主线程中执行,在异步任务执行之前调用,一般做一些准备工作。
doInBackground,在线程池中调用,用到Params参数,执行异步任务。可以通过调用publishProgress(Progress)方法来调用onProgressUpdate(Progerss)方法更新任务进度,返回Result参数给onPreExecute方法。
onProgressUpdate(Progress),在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。
onPostExecute(Result),在主线程中执行,在异步任务执行之后,此方法会被调用。
25、AsyncTask使用过程中的一些限制条件
AsyncTask的类必须在主线程中加载;
它的对象必须在主线程中创建;
Execute方法必须在UI线程调用;
一个它的对象只能执行一次,否则报运行时异常;
Android1.6之前,它是串行执行任务的,Android1.6时采用线程池处理并行任务,从Android3.0开始,既能串行又能并行。
26、AsyncTask引起的内存泄漏
如果它被当做非静态的内部类,则会持有外部类的强引用(隐式的含有),如果其生命周期大于外部activity的生命周期,就有可能导致内存泄漏。
解决:考虑把它声明为静态内部类,或者在另一个不同的类中。
27、线程池的优点
重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销;
能有效控制线程的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象;
能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。