Android工程化最佳实践
作者:金凯著
出版时间:2019年版
内容简介
本书从工程实践角度详细阐述了Android的知识内容,全书分为基础知识和工程优化两部分。在工程优化部分专门增加了常用的App编译提速和瘦身的内容,对于大型分层项目的测试技巧也有所涉及。本书涵盖Android开发的实际业务知识,涉及Dialog、Intent、Fragment等源码的核心细节分析,并扩展了一部分框架设计的内容,章节最后总结了开箱即用的开源库方案,实现从理论到实际的完整论述。最后还给出了抓包工具的使用技巧,帮助读者能方便地寻找到适合自己的工具集。本书适合中、高级Android程序员阅读,也可以作为初级程序员进阶学习的参考书。
目录
第1章 探寻高效易用的反射API
1.1 反射的能力
1.1.1 得到Class对象
1.1.2 操作Field
1.1.3 调用Method
1.1.4 动态代理
1.2 反射封装库――JOOR
1.2.1 反射的流程
1.2.2 VirtualApp中的反射
1.2.3 一行代码建立对象
1.2.4 简化Field的相关操作
1.2.5 简化方法调用
1.2.6 封装动态代理
1.3 注意事项
1.3.1 反射的性能问题
1.3.2 反射的使用时机
1.3.3 如何降低反射的性能损耗
1.3.4 反射的危险性
1.3.5 反射和混淆的关系
1.4 总结
第2章 打造高扩展性的Log系统
2.1 基本概念
2.2 命令行操作Log
2.2.1 输出日志
2.2.2 过滤日志
2.3 Android Studio中的Log
2.3.1 设置模板
2.3.2 正则过滤
2.3.3 热部署Log
2.4 微信的Xlog
2.4.1 设计和开发目标
2.4.2 编译、引入和使用
2.4.3 对Log文件进行优化
2.5 美团的Logan
2.6 扩展Log的功能
2.6.1 TAG的自动化
2.6.2 文本内容的设计
2.6.3 开关的设计
2.7 封装Log库
2.7.1 Timber
2.7.2 LogDelegate
2.7.3 Logger
2.7.4 扩展Timber的功能
2.7.5 分发日志
2.8 实用日志
2.8.1 操作耗时日志
2.8.2 页面跳转日志
2.8.3 网络请求日志
2.9 总结
第3章 万变不离其宗的Intent
3.1 源码分析
3.1.1 静态变量的写法
3.1.2 Intent的深拷贝
3.1.3 makeMainActivity
3.1.4 Intent的Chooser
3.1.5 用URI代替Intent
3.1.6 存取值的底层实现
3.1.7 区分显式和隐式Intent
3.1.8 抛弃Bundle的传值策略
3.2 序列化方案
3.2.1 Serializable/Externalizable
3.2.2 Android中的Parcelable
3.2.3 Google的Protocol Buffer
3.2.4 Twitter的Serial
3.3 常见问题
3.3.1 父类的序列化
3.3.2 类型转换异常
3.3.3 重复启动的问题
3.3.4 传递大对象
3.4 简单的传值库――Parceler
3.4.1 降低Key的维护成本
3.4.2 自动维护Intent的Key
3.4.3 Jetpack中的自动化
3.4.4 自动保存状态
3.4.5 处理ClassCastException
3.4.6 IntentLauncher
3.4.7 统一存取的API
3.5 总结
第4章 SharedPrefrences的再封装
4.1 源码分析
4.1.1 缓存机制
4.1.2 SharedPreferencesImpl
4.1.3 值操作
4.1.4 提交操作
4.2 异常处理
4.2.1 name为null
4.2.2 管理好Key的取名
4.2.3 清空操作失效
4.2.4 磁盘写入异常
4.2.5 出现ANR
4.2.6 存序列化对象
4.2.7 多App和多进程访问异常
4.3 性能优化
4.3.1 避免储存大量数据
4.3.2 尽可能提前初始化
4.3.3 避免Key过长
4.3.4 多次操作,批量提交
4.3.5 缓存Editor对象
4.3.6 不存放HTML和JSON
4.3.7 拆分高频和低频操作
4.4 封装SharedPreferences
4.4.1 PreferenceDataStore
4.4.2 通过接口提高内聚
4.4.3 得到SharedPreferences
4.4.4 多用户存储设计
4.4.5 统一管理Key
4.4.6 自动判断返回值类型
4.4.7 决定是否使用Apply
4.4.8 存放序列化对象
4.4.9 支持数据格式转换器
4.5 思维扩展
4.5.1 偏好界面的实现方案
4.5.2 监听数据的改变
4.5.3 利用Tray实现多进程访问
4.5.4 React Native中的使用
4.6 总结
第5章 寻找Fragment的继任者
5.1 使用场景
5.1.1 日夜间模式
5.1.2 缓存界面数据
5.1.3 作为搜索页
5.1.4 作为Presenter
5.2 源码分析
5.2.1 Transaction简介
5.2.2 提交操作
5.2.3 commitAllowingStateLoss
5.2.4 Add操作的原理
5.2.5 Replace操作的本质
5.2.6 Fragment的可见性监听
5.2.7 ViewPager中的懒加载
5.3 常见问题
5.3.1 Activity为空
5.3.2 startActivityForResult
5.3.3 ViewPager的getItem
5.3.4 FragmentPagerAdapter
5.3.5 显示一个对话框
5.3.6 重叠显示的问题
5.3.7 Fragment的StateLoss
5.4 Fragment的替代品
5.4.1 Jetpack的Navigation
5.4.2 Square的Flow
5.4.3 简化版的Fragment
5.5 Shatter库
5.5.1 建立Shatter类
5.5.2 设计ShatterManager
5.5.3 分发生命周期
5.5.4 使用方式
5.6 总结
第6章 让alertDialog为我所用
6.1 Dialog
6.1.1 Dialog和Window
6.1.2 Show和Dismiss方法
6.2 alertDialog
6.2.1 alertController
6.2.2 alertDialog.Bulder
6.3 dialogFragment
6.3.1 Fragment和Dialog
6.3.2 Show和Dismiss方法
6.4 实际问题
6.4.1 无法弹出输入法
6.4.2 如何支持层叠弹窗
6.4.3 容易引起内存泄露
6.4.4 修改尺寸、背景和动画
6.4.5 点击后会自动关闭
6.4.6 在关闭或开启时出现崩溃
6.5 封装dialogFragment
6.5.1 用现成的alertParams
6.5.2 让Builder类支持继承
6.5.3 建立dialogFragment框架
6.6 easyDialog
6.6.1 基本用法
6.6.2 自定义一个Dialog
6.6.3 BottomSheetDialog
6.6.4 设置全局样式
6.6.5 支持动态样式
6.6.6 避免丢失监听器
6.7 可全局弹出的Dialog
6.8 总结
第7章 Gradle的使用技巧
7.1 全局配置
7.1.1 设定UTF-8
7.1.2 依赖Google仓库
7.1.3 支持Groovy
7.1.4 定义全局变量
7.1.5 配置Lint选项
7.2 操控Task
7.2.1 更改输出的APK的名字
7.2.2 更改AAR输出的位置
7.2.3 跳过AndroidTest
7.2.4 找出耗时的Task
7.2.5 抽离Task脚本
7.3 动态化
7.3.1 动态设置buildConfig
7.3.2 填充Manifest中的值
7.3.3 让buildType支持继承
7.3.4 让Flavor支持继承
7.3.5 内测版本用特定的Icon
7.3.6 不同渠道不同包名
7.3.7 自动填充版本信息
7.4 远程依赖
7.4.1 配置Maven仓库
7.4.2 依赖相关的API
7.4.3 组合依赖
7.4.4 依赖传递
7.4.5 动态版本号
7.4.6 强制版本号
7.4.7 exclude关键字
7.4.8 依赖管理
7.5 本地依赖
7.5.1 引用AAR
7.5.2 依赖Module/Jar
7.5.3 自建本地仓库
7.5.4 本地依赖React Native
7.5.5 重新打包第三方Jar
7.6 资源管理
7.7 总结
第8章 缩减APK的编译时间
8.1 分析项目现状
8.1.1 Gradle Profile
8.1.2 BuildTimeTracker
8.1.3 Dexcount GradlePlugin
8.1.4 经验小结
8.2 编译环境优化
8.2.1 升级硬件设备
8.2.2 升级软件
8.2.3 优化工程配置
8.2.4 配置Studio的可用内存
8.2.5 提升JVM的堆内存
8.2.6 开启并行编译
8.2.7 启用Demand模式
8.2.8 配置DexOptions
8.3 善用缓存
8.3.1 减少动态方法
8.3.2 硬编码BuildConfig和Res
8.3.3 拆分脚本
8.3.4 拆分代码
8.3.5 写死库的版本号
8.4 精简工程
8.4.1 差异化加载Plugin
8.4.2 使用WebP和SVG
8.4.3 精简语言和图片资源
8.4.4 善用no-op
8.4.5 Exclude无用库
8.4.6 删减Module
8.4.7 去掉MultiDex
8.4.8 删除无用的资源
8.5 综合技巧
8.5.1 构建开发时的Flavor
8.5.2 优化MultiDex
8.5.3 跳过无用的Task
8.5.4 关闭AAPT的图片优化
8.5.5 调试时关闭Crashlytics
8.5.6 谨慎使用AspectJ
8.6 多渠道打包工具
8.6.1 MultiChannelPackageTool
8.6.2 美团的Walle
8.6.3 腾讯的VasDolly
8.7 总结
第9章 APP终极瘦身实践
9.1 安装包的构成
9.1.1 Assets
9.1.2 Lib
9.1.3 Resources.arsc
9.1.4 META-INF
9.1.5 Res
9.1.6 Dex
9.2 优化Assets目录
9.2.1 删除无用的字体
9.2.2 减少IconFont的使用
9.2.3 动态下载资源
9.2.4 压缩资源文件
9.3 优化Lib目录
9.3.1 配置ABI Filters
9.3.2 根据CPU引入so
9.3.3 动态加载so
9.3.4 避免复制so
9.3.5 谨慎处理so
9.4 优化Resources.arsc
9.4.1 删除无用的映射
9.4.2 进行资源混淆
9.5 优化META-INF
9.5.1 MANIFEST.MF
9.5.2 CERT.SF
9.5.3 CERT.RSA
9.5.4 优化建议
9.6 优化Res目录
9.6.1 通过IDE删除无用资源
9.6.2 打包时剔除无用资源
9.6.3 删除无用的语言
9.6.4 控制Raw中的资源大小
9.6.5 减少Shape文件
9.6.6 减少Menu文件
9.6.7 减少Layout文件
9.6.8 动态下载图片
9.6.9 分目录放置图片
9.6.10 合理使用图片资源
9.6.11 丢弃特定的资源
9.6.12 开启严格模式
9.6.13 移除Lib库中的配置文件
9.7 优化图片资源
9.7.1 使用VectorDrawable
9.7.2 使用WebP
9.7.3 替换support库中的图
9.7.4 精简动画图片
9.7.5 复用相同的Icon
9.7.6 使用Tint
9.7.7 复用按压效果
9.7.8 通过旋转复用
9.8 优化Dex
9.8.1 分析Dex
9.8.2 利用Lint分析无用代码
9.8.3 删除R文件
9.8.4 启用ProGuard
9.8.5 使用拆分后的support库
9.8.6 尽量不用Mulitdex
9.8.7 使用更小库或合并现有库
9.8.8 根据环境依赖库
9.9 总结
第10章 编写针对性的TestCase
10.1 基础概念
10.1.1 什么代码应被测试
10.1.2 编写易于被测试的代码
10.1.3 测试框架的选型
10.2 逻辑测试
10.2.1 Junit测试
10.2.2 Mockito
10.2.3 Robolectric的使用
10.2.4 Espresso
10.3 集成测试网络层
10.3.1 编写网络层逻辑
10.3.2 建立测试对象
10.3.3 测试HTTP的异常情况
10.3.4 测试业务代码的正确性
10.3.5 用Interceptor模拟返回值
10.4 总结
第11章 Android Studio使用经验
11.1 调试篇
11.2 插件篇
11.2.1 统计相关
11.2.2 工具相关
11.3 总结
第12章 抓包工具Whistle实践
12.1 抓包工具
12.1.1 Charles
12.1.2 Fiddler
12.1.3 AnyProxy
12.1.4 Whistle
12.2 Whistle的安装和使用
12.2.1 安装和更新
12.2.2 查看Request和Response
12.2.3 代理技巧
12.2.4 过滤规则
12.3 Whistle的各项功能
12.3.1 替换域名
12.3.2 修改请求参数
12.3.3 修改返回值
12.3.4 模拟低网速的情形
12.3.5 查看WebView的Console
12.4 总结