这是我参与「第四届青训营 」笔记创作活动的的第4天
数据存储 & 网络通信
课程回顾 → Android的常规 & 高级UI编程
1. UI组件:View & ViewGroup
2. 布局:LinearLayout 、 RelativeLayout 、 FrameLayout 、 ConstraintLayout
3. 绘制:布局加载 、 布局解析 、 UI渲染
4. 交互:获取View实例、常用交互事件 、 触摸事件 、 事件分发
5. 动画:帧动画 、 补间动画 、 属性动画
6. 自定义View:创建View 、 处理布局 、 绘制、交互 、 动画
01 网络通信
App内容的获取,都是通过网络请求。
1.1 网络请求框架对比
1.2 Retrofit介绍
Retrofit是目前Android平台上,可以说是最热门的网络请求封装框架是对OkHttp的一个封装。
1.3 Retrofit快速使用
1.3.1 Retrofit库的引入
1.3.2 创建用于描述网络请求的接口
1.3.3 使用Retrofit实例发起网络请求
1.4 Retrofit使用介绍
接口:xxxx/users/{uid}/name
类型:get请求
- 在需要用到Retrofit的接口的module中,新增依赖:
GitHub官方仓库查看最新版本
引入
- 创建用于描述网络请求的接口
定义说明:
-
接口类名:可自定义,尽量和这类请求的含义相关
-
函数名:可自定义,需要能识别出该接口的作用,该interface里可以增加多个不同的函数
-
@GET 注解:用于指定该接口的相对路径,并采用Get方法发起请求
-
@Path 注解:需要外部调用时,传入一个uid,该uid会替换@GET注解里相对路径的{uid}
-
返回值Call:·返回值Call:这里用ResponseBody,可以直接拿到请求的String内容如果要自动转为Model类,例如User,这里直接替换为User就好。
- 发起网络请求
- 3.1 创建Retrofit实例
- 3.2 创建请求接口的实例,并获取到Call实例
- 3.3 调用call.enqueue进行异步请求
- 3.4 处理返回的数据
1.5 注解类型
1.6 字节跳动用的网络库 Cronet
Cronet与OkHttp的区别:
1.6.1 字节跳动的网络库:TTNet
优点:
-
基于Retrofit改造,具备了Retrofit所具有的优点
-
支持多个Http网络库的动态切换(okhttp和cronet)
-
支持网络拦截配置:添加公共参数、动态切换协议及Host 、动态选路等
-
支持流解析,json序列化
1.6.2 Retrofit 与 TTNet 简单用法对比
1.7 注解介绍
由于Retrofit用到了非常多的注解,这里先做个知识普及注解,也可以理解为是一个标签,这个标签可以加在类、方法、参数、成员变量上,并且可在合适的时机读取注解的内容,进行处理。
@Overrride:标注一个方法是重写了父类的实现
@Nullable:标注所描述对象有可能为空
有定义和使用注解的地方,肯定还需要有获取注解并处理注解内容的地方,不然这个注解也就没什么用了。
1.7.1 注解的处理 —— 3个时机
3个时机 → 注解的生命周期@Retention
-
SOURCE:只有在源码中有效,编译时抛弃。例如:@Override
-
CLASS:编译class文件时有效,一般会使用到注解处理器。
-
RUNTIME:在运行期间,获取对应的注解,并做相关的处理。
1.7.2 Retrofit注解 @GET定义
@Target:指定作用的对象,这里是个METHOD,说明这个注解是作用在方法上,其他枚举值:
-
PARAMETER:参数
-
FIELD:类成员
-
...
@Retention:指定注解的生命周期,这里是RUNTIME,说明这个注解要保留到运行时。
1.7.3 注解的获取和使用
通过反射获取到Method对象后,有以下一些接口来获取注解类型:
-
Method.getGenericReturnType()获取返回类型
-
Method.getAnnotations()获取方法的注解
-
Method.getParameterAnnotations()获取参数注解
1.7.3 Retrofit的注解介绍
Retrofit是在运行期间,配合Java动态代理,获取方法和参数的注解,并构造Request对象。
java动态代理:Proxy.newProxyInstance
-
利用Java的反射技术(Java Reflection),代理某个interface,一旦调用interiace里的某个方法时,实际通过代理调用InvocationHandler的invoke方法。
-
通过Method对象,就可调用Method.getAnnotations()Method.getParameterAnnotations()来获取该方法和方法参数的注解内容。
1.8 Retrofit主流程
-
通过Builder模式,创建RetrofitConfig,保存baseUrl等内容。
-
创建动态代理对象
-
创建OkHttpCall
-
发起网络请求
1.8.1 Retrofit调用OkHttp
1.8.2 Retrofit里OkHttpClient创建时机
Retrofit构建行数中,如果未指定callFactory , 则会自动创建一个OKHttpClient
创建好的OKHttpClient将会保存在Retrofit实例中。
ExecutorCallAdapterFactory主要是用来控制Retrofit在子线程触发请求,在主线程回调结果。
1.8.3 OkHttpCall的创建
通过代理对象调用接口方法IUserInfoService#getUserName()时,会触发InvocationHandler#invoke方法。
1.9 TTNet类图设计
-
替换底层用到的OkHttpClient
-
替换底层用到的OkHttpCall
1.10 TTNet主流程
1.11 网络通信总结
02 数据存储
2.1 存储方式对比
持久性的本地数据存储是Android中常见的能力,可以在应用被杀死的情况下,而保持数据不会被清除。可以根据不同场景的诉求,可以选用不同的存储方式,常见的数据存储主要有以下4种。
2.2 数据库 —— 框架对比
2.3 Room介绍和实践案例
2.3.1 Room简介
Room是Google Jetpack家族里的一员,Room在 SQLite 上提供了一个抽象层,以便在充分利用SQLite的强大功能的同时,能够流畅地访问数据库。
2.3.2 Room主要的3个组件
-
数据库类(Database),用于保存数据库并作为应用持久性数据底层连接的主要访问点。
-
数据实体(Enity),用于表示应用的数据库中的表。
-
数据访问对象(DAO),提供应用可用于查询、更新、插入和删除数据库中的数据的方法。
2.3.3 Room 实践案例
- Room接入
Gradle目录的build.gradle文件里添加:
- 数据表设计
3个字段
- 新建Enity:定义一个User数据实体,User的每个实例都代表App数据库中的user表的一行
1.所有的属性必须是public、或者有get、set方法。 2. @PrimaryKey:表示单个主键,当主键值为null且autoGenerate为true时可以帮助自动生成键值。 3. @ColumnInfo:列名的注解
- 新增DAO:定义一个名为UserDao的DAO,用来对User表进行增删改查。
- 新建数据库类,进行数据库配置。
5.1 新增一个RoomDatabase的abstract子类
5.2 子类需加注解@Database(enity = [xxx],version = n),enities包含数据实体,将会在这这个数据库中创建对应的表,version是数据的版本号。
5.3 对于与数据库关联的每个DAO类,数据库类必须定义一个无参的抽象方法,并返回DAO类实例。
- 获取dao对象,既可进行数据库的增删改查操作。
2.4 Room原理介绍
核心:
1.编译期,通过kapt处理@Dao、@Database注解,动态生成对应的实现类
- 底层使用Android提供的SupportSQLiteOpenHelper实现数据库的增删改查等操作。
2.4.1 kapt注解处理
Room在编译期,通过kapt处理@Dao和@Database注解,生成DAO和Database的实现类。
AppDatabase --> AppDatabase_Impl UserDao-->UserDao_Impl
kapt生成的代码在build/generated/source/kapt
AppDatabase_Impl:数据库实例的具体实现,自动生成:
-
createOpenHelper(): Room.databaseBuilder().build()创建Database时,会调用实现类的createOpenHelper()创建SupportSQLiteOpenHelper,此Helper用来创建DB以及管理版本
-
userDao():创建UserDao_Impl
AppDatabase_Impl的2个具体实现:
-
createOpenHelper()
-
userDao()
UserDao_Impl:UserDao的具体实现,自动生成,主要有以下3个成员变量以及UserDao里定义的接口。
3个核心的成员变量:
-
_db : RoomDatabase的实例
-
_insertionAdapterOfUser : EntityInsertionAdapterd实例,用于数据insert
-
_deletionAdapterOfUser: EntityDeletionOrUpdateAdapter实例,用于数据的update/delete
n 个是UserDao里自己定义的接口的具体实现:
-
insertAll()
-
delete()
-
getAll()
-
loadAllByIds()
-
findByNames()
UserDao_Impl#insertAll(): 使用_db开启事务, 使用_insertionAdapterOfUser执行插入操作
UserDao_Impl#delete(): 使用_db开启事务, 使用_deletionAdapterOfUser执行删除操作
UserDao_Impl#getAll(): 1.自动生成sql语句 "SELECT * FROM USER” 2.获取到数据库的 Cursor光标 3.使用Cursor循环读取数据库的每条记录 4.将Cursor里的内容,读取出来, 并保存在List中返回
2.5 数据存储总结
相关资料:
-
Retrofit官网:github.com/square/retr…
-
Android开发者文档——Room的使用:developer.android.com/training/da…