1、Glide缓存机制
Glide 优点:支持 Gif、WebP、缩略图。甚至是 Video;支持优先级处理;与 Activity/Fragment 生命周期一致;内存友好,默认 RGB_565
设置内存缓存开关:skipMemoryCache(true)
设置磁盘缓存模式:diskCacheStrategy(DiskCacheStrategy.NONE)
可以设置4种模式:
DiskCacheStrategy.NONE:表示不缓存任何内容。
DiskCacheStrategy.SOURCE:表示只缓存原始图片。
DiskCacheStrategy.RESULT:表示只缓存转换过后的图片(默认选项)。
DiskCacheStrategy.ALL :表示既缓存原始图片,也缓存转换过后的图片。
Glide内存缓存源码分析
内存存缓存的 读存都在Engine类中完成。
Glide内存缓存的特点
内存缓存使用弱引用和LruCache结合完成的,弱引用来缓存的是正在使用中的图片。图片封装类Resources内部有个计数器判断是该图片否正在使用。
Glide内存缓存的流程
读:是先从lruCache取,取不到再从弱引用中取;
存:内存缓存取不到,从网络拉取回来先放在弱引用里,渲染图片,图片对象Resources使用计数加一;
渲染完图片,图片对象Resources使用计数减一,如果计数为0,图片缓存从弱引用中删除,放入lruCache缓存。
Glide 流程:创建 Request 并将它交给RequestManager,Request 启动 Engine 去数据源获取资源(通过 Fetcher ),获取到后 Transformation 处理后交给 Target。Glide 默认通过 UrlConnection 获取数据
2、三级缓存的流程
读取: 内存(强引用,软引用)--文件--网络
读取图片的时候,先读取内存缓存,判断强引用中是否存在图片,如果强引用中存在,则直接读取,如果强引用中不存在,则判断软引用中是否存在,如果软引用中存在,则将软引用中的图片添加到强引用中并且删除软引用中的数据,如果软引用中不存在,则读取文件存储,如果文件存储不存在,则网络加载。
下载: 网络--内存(强引用,软引用)--文件
首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存缓存中,缓存到强引用中 也就是LruCache中。如果强引用中空间不足,就会将较早存储的图片对象驱逐到软引用(softReference)中存储,然后将图片缓存到文件(内部存储外部存储)中;
LRU 是 Least Recently Used 最近最少使用算法:
其中用到的数据对象是LinkedHashMap,里面存储的数据是强引用,当每次访问里面的一个值的时候,那么就将该数值放到队列的头部,另一方面,当添加的数据充满整个队列的时候,就将队列的末尾数据移除,该移除的数据才有可能被系统进行垃圾回收。
3、EventBus原理
1、EvenetBus是一种发布-订阅事件总线.代码简洁,开销小,并很好的实现了发送者和接收者的解耦.(是一种观察者模式)
getDefault()是一个“双重校验锁”的单例模式,
2、EventBus的三要素
Event:事件,可以是任意类型的对象。
Subscriber:事件订阅者
在EventBus3.0之前,消息处理的方法只能限定于onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,他们分别代表四种线程模型。
在EventBus3.0之后,事件处理的方法可以随便取名,但是需要添加一个注解@Subscribe,并且要指定线程模型(默认为POSTING),四种线程模型下面会讲到。
Publisher:事件发布者
可以在任意线程任意位置发送事件,直接调用EventBus的post(Object)方法。可以自己实例化EventBus对象,但一般使用EventBus.getDefault()就好了,根据post函数参数的类型,会自动调用订阅相应类型事件的函数。
3、 EventBus的四种ThreadMode(线程模型)
POSTING(默认):在哪个线程发布出来的,事件处理函数就会在这个线程中运行,尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。
MAIN:事件的处理会在UI线程中执行。事件处理时间不能太长,长了会ANR的。
BACKGROUND:如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
ASYNC:无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行,同样,此事件处理函数中禁止进行UI更新操作。
EventBus的工作原理
6.1 订阅逻辑
1、首先用register()方法注册一个订阅者
2、获取该订阅者的所有订阅的方法,findSubscriberMethods(subscriberClass)
3、根据该订阅者的所有订阅的事件类型,将订阅者存入到每个以 事件类型为key 以所有订阅者为values的map集合中
4、然后将订阅事件添加到以订阅者为key 以订阅者所有订阅事件为values的map集合中
5、如果是订阅了粘滞事件的订阅者,从粘滞事件缓存区获取之前发送过的粘滞事件,响应这些粘滞事件。
6.2 事件发送逻辑
1、首先获取当前线程的事件队列
2、将要发送的事件添加到事件队列中
3、根据发送事件类型获取所有的订阅者
4、根据响应方法的执行模式,在相应线程通过反射执行订阅者的订阅方法
6.3 取消逻辑
1、首先通过unregister方法拿到要取消的订阅者
2、得到该订阅者的所有订阅事件类型
3、遍历事件类型,根据每个事件类型获取到所有的订阅者集合,并从集合中删除该订阅者
4、将订阅者从步骤2的集合中移除
6.4 利与弊
EventBus好处比较明显,它能够解耦和,将业务和视图分离,代码实现比较容易。而且3.0后,我们可以通过apt预编译找到订阅者,避免了运行期间的反射处理解析,大大提高了效率。当然EventBus也会带来一些隐患和弊端,如果滥用的话会导致逻辑的分散并造成维护起来的困难。另外大量采用EventBus代码的可读性也会变差。
4、Retrofit实现原理(squareup公司的开源)
Retrofit使用OkHttpClient来实现网络请求,Retrofit使用动态代理,方法注解、建造者和适配器等成熟的技术或模式。
1.动态代理创建一个接口的代理类
2.通过反射解析每个接口的注解、入参构造http请求
3.获取到返回的http请求,使用Adapter解析成需要的返回值。
5、oKhttp原理
OkHttp的底层是通过Java的Socket发送HTTP请求与接受响应的(这也好理解,HTTP就是基于TCP协议的),但是OkHttp实现了连接池的概念,即对于同一主机的多个请求,其实可以公用一个Socket连接,而不是每次发送完HTTP请求就关闭底层的Socket,这样就实现了连接池的概念。而OkHttp对Socket的读写操作使用的OkIo库进行了一层封装。
Okhttp支持5个并发KeepAlive,默认链路生命为5分钟(链路空闲后,保持存活的时间),连接池有ConectionPool实现,对连接进行回收和管理。
优点:
支持http请求,https请求。支持文件下载。
使用的是HttpURLConnection,不要担心android版本的变换。(至少目前是都支持的)。
支持get,post请求。基于Http的文件上传。加载图片。
缺点:(400K)
比如callback回来是在线程里面, 不能刷新UI,需要我们手动处理。
封装比较麻烦。
1、okhttp工作的大致流程
1.1、整体流程(建造者模式)
(1)、当我们通过OkhttpClient创立一个Call,并发起同步或者异步请求时;
(2)、okhttp会通过Dispatcher(调度器)对我们所有的RealCall(Call的具体实现类)进行统一管理,并通过execute()及enqueue()方法对同步或者异步请求进行解决;线程池
(3)、execute()及enqueue()这两个方法会最终调用RealCall中的getResponseWithInterceptorChain()方法,从阻拦器链中获取返回结果;
(4)、阻拦器链中,依次通过RetryAndFollowUpInterceptor(重定向阻拦器)、BridgeInterceptor(桥接阻拦器)、CacheInterceptor(缓存阻拦器)、ConnectInterceptor(连接阻拦器)、CallServerInterceptor(网络阻拦器)对请求依次解决,与服务的建立连接后,获取返回数据,再经过上述阻拦器依次解决后,最后将结果返回给调用方。
Volley
1.占用储存空间
使用Volley 需要Volley.jar(120k),加上自己的封装最多140k。
2.功能介绍
Volley是Goole在2013年Google I/O大会上推出了一个新的网络通信框架,它是开源的。Volley 的特点:特别适合数据量小,通信频繁的网络操作。
3.优点
非常适合进行数据量不大,但通信频繁的网络操作。
内部分装了异步线程。
支持get,post网络请求。
图片下载。
可直接在主线程调用服务端并处理返回结果。
可以取消请求,容易扩展,面向接口编程。
4.缺点
对大文件下载 Volley的表现非常糟糕。
只支持http请求。
接图片加载,性能一般。
使用的是httpclient,HttpURLConnection。不过在android 6.0不支持httpclient了,如果想支持得添加org.apache.http.legacy.jar。
APK执行过程
代码编译形成APK的过程中,其实在里面生成了一个classes.dex文件,解压APK文件如下图:
APK结构
这个classes.dex文件就是所有代码的集合,是一个可执行文件,apk运行过程实质上是解压apk运行classes.dex这个文件。
apk首次运行的时候会对这个dex文件进行优化,优化后生成一个odex文件,存在于缓存中,下次再启动就直接打开这个odex文件,达到快速打开目的。而执行apk的过程就是遍历这个dex并作出相应操作的过程,遍历后的dex方法存放在一个Elements数组中,它的长度限制是65536.即日常说的65K.
如果我们apk因为太庞大或者是引用三方库太多导致方法数超过65K,就会报错.
而谷歌已经在Android 5.0开始支持Multdex.
6、Tinker原理
简单来说,在编译时通过新旧两个Dex生成差异path.dex。在运行时,将差异patch.dex重新跟原始安装包的旧Dex还原为新的Dex。这个过程可能比较耗费时间与内存,所以我们是单独放在一个后台进程:patch中。为了补丁包尽量的小,微信自研了DexDiff算法,它深度利用Dex的格式来减少差异的大小。
补丁包的合成流程:当tinker收到补丁包bug path后,它会开启一个service,和当前有问题的bug的dex合成一个新的fixed dex文件,然后置于tinker dex文件加载路径。
补丁包的加载流程:获取到fixed dex文件后,通过反射DexPathList中的dexElements数组,将fixed dex插入到dexElements数组的最前面。classloader在寻找bug class的时候,找到的就是最前面的dex文件中我们已修复的fixed class。
AndFix(阿里开源)
提供了一种运行时在Native修改Filed指针的方式,实现方法的替换,达到即时生效无需重启,对应用无性能消耗的目的。
HotFix:QQ空间超级补丁技术
大众点评:NuWa(不能修改资源文件,可动态加载)
7、Android组件化开发
组件化好处:
1、架构清晰业务组件间完成接耦合。
2、每个业务组件都可以根据BU需求完成独立app发布。
3、开发中使开发者更加轻松,开发中加快功能开发调试的速度。
4、业务组件整体删除添加替换变得非常轻松,减少工程中的代码资源等冗余文件。
5、业务降级,业务组件在促销高峰期间可以业务为单元关闭,保证核心业务组件的顺利执行。
#####项目组件化方案
概述:
1、module library 切换。
2、组件间跳转uri跳转。
3、组件间通讯 binder机制。
主要有三个角色:
1、主工程(壳工程mudele):主要负责事情不塞入任何具体业务逻辑,主要用于使用组合业务组件、初始化配置和发布应用配置等操作。
2、组件(module/library):主要实现具体业务逻辑,尽可能保证业务独立性,例如现在手淘这样一个大型的app几乎每个bu功能块都能够拿出来作为一个独立业务app。但是没有这么大型也可以按照小一些的业务逻辑来区分组件,例如:购物车组件、收银台组件、用户中心组件等,具体更具自己的项目需要来划分。
3、公共库(library):公共使用的工具类、sdk等库,例如eventbus、xutils、rxandroid、自定义工具类等等,这些库可以做成一个公共common sdk、也可以实现抽离很细按照需求依赖使用。
他们之间的关系则是 主工程依赖组件、组件依赖公共库。
组件开发中分为两种模式一种开发测试模式、一种是发布模式:
1、开发测试模式:这种模式下面组件应该是独立module模式,module是可以独立运行的,只要保证他对其他业务没有依赖就可以独立开发测试。
2、发布模式:这时候组件应该library模式被主工程依赖组合,发布运行,所有业务将组合成完整app发布运行。
Activity之间的跳转:路由框架ARouter
是ARouter是阿里巴巴开源的Android平台中对页面、服务提供路由功能的中间件,提倡的是简单且够用。
GitHub:https://github.com/alibaba/ARouter
四:ARouter 优势
从 ARouter Github 了解到它的优势:
支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
支持多模块工程使用
支持添加多个拦截器,自定义拦截顺序
支持依赖注入,可单独作为依赖注入框架使用
支持InstantRun
支持MultiDex(Google方案)
映射关系按组分类、多级管理,按需初始化
支持用户指定全局降级与局部降级策略
页面、拦截器、服务等组件均自动注册到框架
支持多种方式配置转场动画
支持获取Fragment
完全支持Kotlin以及混编
典型的应用:
从外部URL映射到内部页面,以及参数传递与解析
跨模块页面跳转,模块间解耦
拦截跳转过程,处理登陆、埋点等逻辑
跨模块API调用,通过控制反转来做组件解耦
---------------------
8、Android插件化开发
Android 插件化—— 指将一个程序划分为不同的部分,比如一般 App 的皮肤样式就可以看成一个插件
Android 组件化—— 这个概念实际跟上面相差不那么明显,组件和插件较大的区别就是:组件是指通用及复用性较高的构件,比如图片缓存就可以看成一个组件被多个 App 共用
Android 动态加载—— 这个实际是更高层次的概念,也有叫法是热加载或 Android 动态部署,指容器(App)在运⾏状态下动态加载某个模块,从而新增功能或改变某⼀部分行为
在java开发中随处可见使用jar包的插件机制进行开发,但在android中,目前较成熟的插件机制基本没有,看到的帖子中提到了重写dexclassloader可以完美的解决插件问题,但都只简要描述了原理,没有源码或关键代码,下面对网络中的思路进行总结.
目前插件包有两种格式:一种是apk,一种是dex包.对插件的接入机制来说也有两种:一种是需要安装,一种是不需要安装.结合插件包的格式来说插件的方式只有三种:1,apk安装,2,apk不安装,3,dex包.三种方式其实主要是解决两个方面的问题:1,加载插件中的类,2,加载插件中的资源.第一个加载类的问题,这三个方式都可以很好的解决.但目前三种方式都没有很完美的解决第2个问题.
1,apk安装方式.插件apk安装后,可以在主程序中通过包名加载到插件的context,有了插件的context就可以解决加载插件资源的问题.但出现的新问题是:如果插件a,b,c间公用一个底层jar包,那么在abc间传送数据时,需要进行序列化和反序列化,因为a中jar包的data类与b中jar包的data类虽然都是同样的jar包也是同样的类,但两个类在java机制来是由不同的classloader加载的,是不同的类.那么就会出现插件间jar包冗余和数据传递的效率不好问题.总之能解决问题.
2,apk不安装,这个是不推荐的方式.可以通过dexclassloader加载到插件a中的类,但插件没有安装就无法获得插件apk的context,也就无法加载到资源,网络上有牛人通过将主程序的context替换关键的对象(如classloader,assertmanager等),伪造成插件的context,然后通过伪context也可以获得插件资源.目前个人验证的问题为:获取到的layout资源中的textview显示文本有问题,无法显示在layout设定的文本.同时个人猜测这种hack的方式或许有其它的未知的问题,但不排除高手已经解决了这个问题.
3,dex包,这个基本是java开发中jar包的方式.同样通过dexclassloader加载到插件中的类,但依旧没有context,也无法加载到资源.要使用布局,只能在插件中使用java代码来写布局.这种方式最简单(1,类似jar包的方式,也可以随意导出单个类的jar包然后转化为dex包,不存在打包成apk需要完整的工程和依赖关系的要求.2,底层的公用jar包所有插件可以公共一个,传递数据不用反复的序列化和反序列化.),也是最复杂的(布局文件全部代码写,难调试,难维护).
Android插件化框架有很多
Dynamic-load-apk(阿里):主要是通过代理来完成Activity,Service的相关操作 ,不支持Service和BroadcastReceiver。
ACDD、
DroidPlugin(360):
共享进程:为android提供一个进程运行多个apk的机制,通过API欺骗机制瞒过系统
占坑:通过预先占坑的方式实现不用在mainfest注册,通过一带多的方式实现服务管理
Hook机制:动态代理实现函数hook,Binder代理绕过部分系统服务限制,IO重定向(先获取原始Object–>Read,然后动态代理Hook Object后–>Write回去,达到瞒天过海的目的)
轻量级的插件化框架——small框架,VirtualAPK(滴滴)
9、直播SDK
使用七牛的直播平台提供的sdk进行推流和拉流,使用环信IM来作为用户系统的管理直播聊天室中消息收发、发送礼物、弹幕、私信等功能
10、Databinding
DataBinding主要解决了两个问题:
1、需要多次使用findViewById,损害了应用性能且令人厌烦
2、更新UI数据需切换至UI线程,将数据分解映射到各个view比较麻烦
总体思路
DataBinding解决这些问题的思路非常简单。就是针对每个Activity的布局,在编译阶段,生成一个ViewDataBinding类的对象,该对象持有Activity要展示的数据和布局中的各个view的引用(这里已经解决了令人厌烦的findViewById问题)。同时该对象还有如下可喜的功能:
将数据分解到各个view
在UI线程上更新数据
监控数据的变化,实时更新
一、对布局文件进行预处理
首先,DataBinding会对根元素为
二、生成ActivityMainBinding与BR类**
现在,DataBinding将会依据上面两个xml文件(即activtiy_main.xml和activtiy_main-layout.xml)生成两个类,一个类是ActivityMainBinding,它继承自ViewDataBinding,里面包含如下fields:实际上,BR中的常量是一种标识符,它对应一个会发生变化的数据,当数据改变后,你可以用该标识符通知DataBinding,很快,DataBinding就会用新的数据去更新UI。
三、生成ActivityMainBinding实例并绑定
11、Android马甲包
马甲包是指与原APP包除了包名,签名、包名称图标等给用户加以区分的东西不一样之外,其他功能基本不变的APP包。
1.签名文件路径配置(只有一个签名文件,不同马甲包对应不同别名就行)
2.主module的build.gradle中一些相关配置
3.AndroidManifest.xml中的一些相关配置(${}的使用)
4.获取MetaData值和getPackageName()获取包名
5.如何打包(通过命令方式、Tasks、Generate signed APK打包生成多个马甲包。)
12、RXJava原理
基于观察者模式,事件流将从上往下,从订阅源传递到观察者。
Rx框架的优点可以避免回调嵌套,更优雅地切换线程实现异步处理数据。配合一些操作符,可以让处理事件流的代码更加简洁,逻辑更加清晰。
LooperScheduler的代码很清晰,内部持有一个Handler,用于线程的切换,利用 subscribeOn() 结合 observeOn() 来实现线程控制
RxJava是一种基于观察者模式的响应式编程框架,其中的主要角色有:
13、RXAndroid
RxAndroid,这是一个扩展库,更好的兼容了Android特性,比如主线程,UI事件等。
Android上会有生命周期的问题,可能会导致内存泄漏:Observable持有Context导致的内存泄露。在这个问题上,我们的解决方法是这样的:就是在订阅的时候,用一个Subscription来保存它,然后在退出这个Activity的时候取消订阅。
14、Bugly功能
1、应用升级功能
2、热更新使用
3、异常上报、运营统计(鹅厂内部使用的RDM异常上报)