Android基础入门
目录
4.1Handler基础使用
01.Handler必备知识点
消息机制背景:子线程和主线程无法直接通信(举个例子,子线程做耗时操作完后需要更新主线程UI),这个时候就有了消息机制,主要是为了实现跨线程通信!
消息机制四要素:Message消息,Handler处理者(发消息和回调消息),MessageQueue消息队列(存储消息体),Looper(轮询,获取消息和处理消息)
03.子线程中定义Handler
子线程创建Handler会出现什么问题?一个线程中Looper可以是多个吗?
- 崩溃,handler运行是依赖Looper的,而Looper一个线程只允许有一个(应用初始化时ActivityThread会在主线程初始化Looper),其他线程不允许访问。
- 一个线程中,Handler可以是多个,Looper必须只有一个。handler其实间接跟线程绑定到一起,Handler所创建的线程中有Looper对象并且启动循环(其实是Looper)。
避免子线程手动创建looper。如果刻意在子线程中创建Looper,当消息处理完后,这个子线程的Looper则会一直处理等待的状态,建议不需要的时候终止Looper。
06.不建议在子线程访问UI
UI控件不是线程安全,多线程并发可能导致UI更新处于不可控,为何系统不设计成对UI控件访问加锁机制呢?
- 加锁让UI访问和更新变得复杂。
- 锁机制,在UI高频率刷新过程中,会降低效率,因为锁机制会阻塞线程执行。
07.子线程更新UI方式
- 主线程中定义Handler,子线程通过mHandler发送消息,主线程Handler的handleMessage更新UI
- 用Activity对象的runOnUiThread方法
- 创建Handler,传入getMainLooper,使用handler.post更新UI
- View.post(Runnable r)
08.解决Handler内存泄漏
Handler内存泄漏原因:核心原因是生命周期长短造成,主要是Handler创建会隐式持有一个Activity,当Activity销毁后消息队列中仍然有未处理完的消息,Message会持有handler引用,Handler又持有Activity的引用,导致Activity的内存资源无法及时回收,引发内存泄漏。
Handler内存泄漏解决方案:1.Activity销毁时移除所有消息;2.使用弱引用关联Handler。
4.2消息机制流程分析
01.消息机制核心思想
02.Handler发送消息
构造Handler对象,如果不传Looper对象,则会取当前线程关联的Looper【跟线程关联用到了ThreadLocal】。在主线程,系统在启动时,ActivityThread中main方法会给我们构建一个UI线程的Looper。
使用Handler发送消息,不管用那种方式,最终都会调用到sendMessageAtTime
,发送消息肯定要有个容器来装消息,这里就用到了消息队列!
03.MessageQueue存储消息
MessageQueue消息队列什么时候创建的?在创建Looper的时候,就创建了MessageQueue对象,这说明一个Looper对应一个MessageQueue消息队列。
消息队列如何存储消息?将消息Message对象存储到消息队列中,注意:发送延迟N毫秒的消息,消息队列会根据消息自带的时间排序来保证消息的先后顺序。
04.Looper循环分发消息
Looper创建时机。在ActivityThread中的main方法中创建,然后用ThreadLocal将线程和Looper进行绑定。一个线程只能有一个Looper!
Looper轮询器工作原理。
消息机制总结分享
- 一、在创建 Handler 实例时要么为构造函数提供一个 Looper 实例,要么默认使用当前线程关联的 Looper 对象,如果当前线程没有关联的 Looper 对象,则会导致抛出异常
- 二、Looper 与 Thread ,Looper 与 MessageQueue 都是一一对应的关系,在关联后无法更改,但 Handler 与 Looper 可以是多对一的关系
- 三、Handler 能用于更新 UI 有个前提条件:Handler 与主线程关联在了一起。在主线程中初始化的 Handler 会默认与主线程绑定在一起,所以此后在处理 Message 时,
handleMessage(Message msg)
方法的所在线程就是主线程,因此 Handler 能用于更新 UI - 四、可以创建关联到另一个线程 Looper 的 Handler,只要本线程能够拿到另外一个线程的 Looper 实例
4.3Handler深度解析
Handler深度解析和思考如下所示:
- 创建时机:在创建Handler对象时,在构造方法中会获取当前线程的Looper和MessageQueue,当然还会对消息callback赋值,设置消息是否是同步消息等等参数。
- 发送消息:不管那种方式发消息,最后都会调用
sendMessageAtTime()
,将消息体Message放到消息队列中【消息体中有指向handler引用,还有时间,what等】。 - 发送延迟消息:直接放到消息队列中,然后按照触发时间进行排序,保证队头的时间最小、队尾的时间最大。
- 处理消息:looper从消息队列里拿到消息后,然后交给handler去处理,最后会回调分发到
dispatchMessage
方法中。
什么是同步消息?什么是异步消息?分别应用到什么场景?
举一个例子。开发人员在100毫秒发送100个handler消息,Activity在不同生命周期加载View也会涉及到handler消息处理,那么如何设计可以让系统UI消息优先处理,优先做系统UI刷新?
发送消息太多会出现什么问题?1.会导致Looper负载高,任务挤压导致阻塞,导致业务出现bug;2.消息队列有一些消息处理非常耗时,导致后面的消息延时处理;
如何解决发送大量高频消息?可以使用独享Looper的handler,这样就跟主线程UI的Looper处理消息起到了隔离,不会影响主线程!
4.4Message深度理解
先来了解下Message,如果是你,你会如何设计消息体。那么考虑点有那些?
- 消息体需要能附带传递的内容【可以设计属性object】,发送消息和接受消息处理需要通过code来判断【可以设计属性what】,延迟消息需要附带时间【可以设计属性when】
- 使用handler发送,在处理消息的时候需要知道是那个handler发送的【可以设计属性target】,通过回调处理消息【可以设计属性runnable】
- 当开发者使用handler发送大量消息,系统可以优先处理系统UI级别消息,需要区分同步还是异步消息【可以设计属性flags】
消息使用频率高,而且有时候会很多,比如1000个消息,我肯定希望消息体创建能否做到复用【避免高频创建和销毁】?
那么如何做,这种场景明显就是,如果存在,则重用该对象,而不是创建一个新的对象。如果不存在,则创建一个新的对象并将其添加到共享池中,以供以后使用。利用享元模式!
消息的创建:可以看到源码中使用到了对象池,能否做到message复用。