LayoutInflater.inflate方法
作用:将xml布局转换为相应的View布局
//一
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root)
//二
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
//三
public View inflate(XmlPullParser parser, @Nullable ViewGroup root)
//四
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)
最底层的是第四个方法
第一个参数XmlPullParser
final Resources res = getContext().getResources();
XmlResourceParser parser = res.getLayout(resource);
传递需要转换的xml布局,用xml进行解析的一个类
@Nullable 注解:代表该值可以为null
第二个参数与第三个参数组合判断
热修复基础
插件化、模块化、热修复知识区分
单工程模式 小项目、单人完成
模块化
Project Module模块 一个模块就是一个小项目,也是AS概念中的模块 好处:更加灵活,耦合度更低 类似引入了第三方的框架
- application apk,是一个完整的项目
- library 引用库 eg. common
组件化
与模块化区别:模块角色的可转化性
打包时是library,调试是为application
插件化
是模块化的进阶概念,将一个完整工程,按业务分成不同插件 分治 插件化可以实现组件化操作,同时可以实现热更新
插件化与热修复的关系
都是去欺骗安卓系统的方式让宿主正常加载和运行插件(补丁)里面的内容。 宿主:当前正在运行的app 插件:相对于插件化技术来说,加载运行的apk文件 补丁:热修复:加载运行的.patch、.dex、*.apk等等的一系列包含dex修复内容的文件
热修复
- 为功能bug打补丁
- 节日更新
- 无感知修复Bug,代价小 热修复框架应运而生
热修复框架的核心技术有三类:
- 资源修复
- 代码修复
- 动态链接库修复
热修复框架更新换代快,了解原理、基本方案 Instant Run,它的出现推动了修复框架的发展,Android Studio2.0以后新增的一个运行机制
资源修复
中间存在对App销毁、App重启
- Hot swap:不需要重启APP,activity (修改一个现有方法里面的代码)
- Warm swap:Activity重启 (修改或删除一个现有的资源文件)
- Cold swap:App重启,但是不需要重新安装(添加、删除、修改一个字段和方法,添加一个类等等)
Instant Run资源修复核心在MonkeyPatcher的monKeyPatchExistingResources();
总结:1.创建一个新的AssetManager,通过反射调用addAssetPath()加载外部资源,那么新创建的AssetManager就含有了外部的资源 2.将AssetMagager类型的mAssets字段引用全部替换新的AssetManager。
Java基础之一————反射
使用前提:得到代表的字节码的Class
- 在运行状态下,对任意类,知道该类所有属性与方法
- 对任意对象,能够调用它的任一方法和属性 反射机制:动态获取信息及动态调用对象的方法(将java类的各种成分映射成一个个Java对象)
- Class类的实例表示正在运行的Java应用程序的类和接口
- 每一个类都有唯一Class对象(包含基础数据库)
- Class类没有公共构造方法,Class对象是在加载类时由Java虚拟机及通过调用加载中的defineClass方法自动构造的
java弱引用集
-
java.lang.ref.WeakReference类
弱引用对象的存在不会阻止它指向的对象被垃圾回收器回收。
常见用途:实现规范映射 决定这个对象是弱可达的(指向该对象的引用全为弱引用),那么该对象被标记为可终结的(finalizable),这个对象之后会被回收;与此同时或稍后,;垃圾收集器会将刚清除的弱引用闯入创建弱引用对象所登记的引用队列中(Reference Queue)中。
-
为什么该产品不会被回收? GC(垃圾收集器)在追踪代码栈时,会发现list引用,而继续往下追踪,就会发现list引用指向的内容空间又存在指向本该卖出的产品的引用。
-
用一个指向该对象的弱引用对象作用HashMap中的Key
Object o = new Object();
WeakPeference < Object> weakO = new WeakPeference<>(o);
与其他引用区别
-
强引用:用new创建一个新对象时返回的就是强引用。不容易被回收
-
软引用:与弱引用区别?
-
若一个对象是弱可达,无论是否内存是否充足,都会被回收。
-
若一个对象是软引用可达的,当内存不充足,才会被回收。
-
-
弱引用
-
虚引用 唯一作用:当他指向的对象被回收后,虚引用会加入引用队列,用作记录它指向的对象已经被销毁。
代码修复
主要3个方案:底层替换方案、类加载方案、Instant Run方案。
类加载方案:基于Dex分包方案
- 65536限制:说明在应用中引用的方法数超过了最大值65536个
主要产生原因是DVM Bytecode限制:DVM指令集的方法调用指令invoke-kind索引为 16 bits,所以最多引用65535个方法。 2. LinearAlloc限制 安装应用时提示
INSTALL_FAILED_DEXOPT
原因:DVM中LinearAlloc是一个固定的缓存区,当方法数超过缓存区大小,会报错。 为了解决,出现了Dex分包方案: 打包时将代码分成多个Dex,将应用启动时必须用到的类和这些类的引用放入主Dex中,其他代码放入次Dex中,当应用启动时先加载主Dex,再动态加载次Dex,从而缓解限制
Dex分包方案:
- Google官方方案
- Dex自动拆包和动态加载方案
DexPathlist.findClass()
ClassLoader加载过程的一个环节
根据ClassLoader双亲委托模式,在首先找到补丁包Patch.dex中的Key.class去替换之前存在Bug的Key.class后,排在后方的存在Bug的dex文件不会被加载!
类加载方案思想:类加载方法需要重启App后让ClassLoader重新加载新的类
JNI原理
Java Native Interface缩写 安卓语言层: Java层、Native层 Native语言:更接近汇编语言 JNI应用场景:
- 调用Java语言不支持,依赖操作系统平台特性的功能 eg.UNIX系统的某某功能
- 程序对时间敏感or对性能要求高时,用更底层的语言提高运行效率
- 整合以前非Java开发的功能程序 JNI让Java更加全面,封装了各个平台的差异性。
底层替换方案
不加载新类,而是直接在Native层修改原有的类
- java.lang.Class.getDeclaredMethod 反射key的show方法时
Key.class.getDeclaredMethod("show").invoke(Key.class.newInstance());
- Android8.0的Method.java的invoke()方法是一个native方法,其对应JNI层代码
思想:替换ArtMethod结构体中的字段或者替换掉整个ArtMethod结构体,这就是底层替换方案。
InstantRun方案
除了资源的修复,代码修复同样借鉴了InstantRun原理; Instant Run在第一次构建apk时,使用ASM在每一个方法中注入类似如下代码,帮助之后的代码修复。
IncrementalChange localIncrementalChange = $change; //1
if (localIncrementalChange != null) { //2
localIncrementalChange.access$dispatch(
"onCreate.(Landroid/os/Bundle;)V", new Object[]{this, paramndle});
return;
ASM:Java字节码操控框架,能动态生成类或者增强现有类的功能。ASM可以直接产生class文件,也可以在类被加载到虚拟机之前动态改变类的行为。
动态链接库修复
主要描述的是so库,修复就是更新so库/重新加载so库。
- System的load和loadLibrary方法
- load0()
- loadLibrary()
- 两个方法的最底层都调用了nativeLoad(); so库是被JVM加载的,它的加载方法是 LoadNativeLibrary()
- 判断传入so库是否已加载过。两个classLoader是否是同一个,避免重复加载
- 获取so句柄,创建新的SharedLibrary,如果是原来的library指针为空,就把新的Sharelibrary赋值给它,并储存在libraries中。
- 查找so库中JNI_OnLoad的函数指针并执行它,根据不同的情况设置不同的was_successful的值,最后返回was_successful。
总结:so库主要有两种方案:
- 将so补丁插入到NativeLibraryElement数组的前部,让so补丁的路径先被返回和加载,并调用Runtime的doLoad(),在doLoad中会调用native方法nativeLoad()。
- 调用System的load方法来接管so的加载入口。