数据存储 & 网络通信 | 青训营笔记
这是我参与「第四届青训营 」笔记创作活动的第4天
-
网络通信
- 网络库框架对比
- Retrofit使用&原理介绍
- TTNet介绍
-
数据存储
- Android数据存储方式对比,了解不同场景该使用的工具
- 数据库框架对比
- Room数据库使用与原理介绍
网络请求
简介
- 客户端向服务端发起请求,服务端返回数据给到客户端
- 由于网络请求在大型App里使用非常频繁,为了更好的支持业务迭代,一般会进行网络请求的封装
网络框架对比
- Volley的Request和Response都是把数据方法放到byte[]数组里,不支持输入输出流,把数据放到数组中,如果大文件多了,数组就会非常大且多,消耗内存
- 行业内,目前基本上都是Retrofit 和 OkHttp组合的这种方式来进行网络请求
- IO 和 NIO这两个都是Java中的概念,如果我从硬盘读取数据,第一种方式就是程序一直等,数据读完后才能继续操作这种是最简单的也叫阻塞式IO,还有一种是你读你的,程序接着往下执行,等数据处理完你再来通知我,然后再处理回调。而第二种就是 NIO 的方式,非阻塞式, 所以NIO当然要比IO的性能要好了,而 Okio是 Square 公司基于IO和NIO基础上做的一个更简单、高效处理数据流的一个库。
总结:
目前Retrofit和OkHttp的组合,功能更加全面,封装更加彻底,当下最为流行的网络请求方式,我们本文也会重点来
关注Retrofit的使用和原理的介绍。
Retrofit的使用介绍
Retrofit其实是对OkHttp的一个封装,也是当前最为流行的一种网络请求组合方式。
类型:GET请求 接口返回:
{
"message": "success",
"data": {
"uid":"1123",
"first_name":"张",
"last_name":"三丰"
}
}
-
添加Retrofit库的依赖
- 在需要用到Retrofit接口的module中,新增依赖
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
//...其他依赖
}
复制代码
创建 用于描述网络请求 的接口
//接口类名:可自定义,尽量和这类请求的含义相关
interface IUserInfoService {
@GET("users/{uid}/name")
fun getUserName(@Path("uid") uid: Int): Call<ResponseBody>
//@GET("users/{name}/uid")
//fun getRequest(@Path("name" name:String)) Call<User>
//后续可以增加其他的接口,一个接口对应一个api请求
}
//函数名:可自定义,需要能识别出该接口的作用,该interface里可以增加多个不同的函数
//@GET 注解:用于指定该接口的相对路径,并采用Get方法发起请求
//@Path 注解:需要外部调用时,传入一个uid,该uid会替换@GET注解里相对路径的{uid}
//返回值Call<ResponseBody>,这里用ResponseBody,我们可以直接拿到请求的String内容
//如果要自动转为Model类,例如User,这里直接替换为User就好。
发起网络请求
fun getUserName(view: View) {
//创建Retrofit实例
val retrofit = Retrofit.Builder()
.baseUrl("https://www.bytedance.com/")
.build()
//创建iUserInfoService实例
val iUserInfoService = retrofit.create(IUserInfoService::class.java)
//创建网络请求Call对象
val call = iUserInfoService.getUserName(1123)
//发起异步请求
call.enqueue(object : Callback<ResponseBody> {
override fun onResponse(call: Call<ResponseBody>,
response: Response<ResponseBody>) {
//请求成功时回调
request_result_tv.text = "请求成功:" + response.body()!!.string()
}
override fun onFailure(call: Call<ResponseBody>, e: Throwable) {
//请求失败时候的回调
request_result_tv.text = "请求失败:" + e.message
}
})
}
总结
- 引入依赖库
- 创建 用于描述网络请求 的接口
-
发起网络请求
- 创建
Retrofit实例 - 创建
iUserInfoService实例 - 创建网络请求
Call对象 - 使用
Call对象发起异步请求
- 创建
- 其他更多的用法,更多的注解,可以看Retrofit官网square.github.io/retrofit/
\
TTNet
TTNet是字节跳动通用的网络请求封装框架,用来向服务端发起请求。
目前Android端上的头条,抖音,番茄小说都在使用该库作为网络请求封装框架,作为字节内部的基础库,提供了一整套Android客户端网络请求解决方案。
- 它有哪些突出的优点:
-
- 基于Retrofit改造,具备了Retrofit所具有的优点
- 支持多个Http网络库的动态切换(okhttp和cronet)
- 支持网络拦截配置:添加公共参数,动态切换协议及Host,动态选路等
- 支持流解析,json序列化
TTNet与Retrofit的使用对比
Retrofit
TTNet
数据存储
简介
- 数据存储的方式有很多种,其使用场景也不一样,本文主要介绍其中4种最为常见的数据存储方式和使用场景
- 数据库是4种常见数据存储方式中较为复杂的能力,本文也会重点介绍
存储方式对比
持久性的本地数据存储是Android中常见的能力,可以在应用被杀死的情况下,而保持数据不会被清除。我们可以根据不同场景的诉求,可以选用不同的存储方式,常见的数据存储主要有以下4种。
数据库开源框架对比
数据库 相对来说,其使用会比较复杂些,我们单独进行探索,下面是几个主流的数据库框架对比
不同的产品,对功能的诉求不太应用,头条因为也有用到LiveData,同时考虑到是Google出品,其流行度和稳定性都有较好的保障,所以更倾向于使用Room数据库,下面会重点介绍下Room数据库。
Room数据库的使用
Room是 Google Jetpack 家族里的一员,Room 在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库
主要的3个组件
- 数据库类(
Database),用于保存数据库并作为应用持久性数据底层连接的主要访问点。
- 数据实体(
Entity),用于表示应用的数据库中的表。
- 数据访问对象(
DAO),提供您的应用可用于查询、更新、插入和删除数据库中的数据的方法。
Room接入
Gradle目录的build.gradle文件里添加如下:
数据表设计
下面假设设计一个表,表名为user,数据表包含uid、first_name、 last_name 3个字段
新建Entity
定义一个User数据实体,User的每个实例都代表App数据库中的user表的一行
新增DAO
定义一个名为UserDao的DAO。用来对User表的增删改查
新建数据库类
进行数据库配置,并需满足以下几个条件:
- 新增一个
RoomDatabase的abstract子类
- 子类需加注解
@Database(entities = [xxx], version = n),entities包含数据实体,将会在这个数据库中创建对应的表,version是数据的版本号
- 对于与数据库关联的每个DAO类,数据库类必须定义一个无参的抽象方法,并返回DAO类实例
获取dao对象
可进行数据库的增删改查操作
Room原理介绍
核心
- 编译期,通过kapt处理
@``Dao、@Database注解,动态生成对应的实现类
- 底层使用Android提供的
SupportSQLiteOpenHelper实现数据库的增删改查等操作
\
kapt注解处理
Room在编译期,通过kapt处理@Dao和@Database注解,生成DAO和Database的实现类 AppDatabase --> AppDatabase_Impl UserDao-->UserDao_Impl
kapt生成的代码在build/generated/source/kapt/
实现类利用Android SQLite进行数据库操作
AppDatabase_Impl:数据库实例的具体实现,自动生成,主要有以下几个方法
createOpenHelper(): Room.databaseBuilder().build()创建Database时,会调用实现类的 createOpenHelper()创建SupportSQLiteOpenHelper,此Helper用来创建DB以及管理版本 userDao():创建UserDao_Impl
UserDao_Impl: UserDao的具体实现,自动生成,主要有以下3个属性以及UserDao里定义的接口
__db:RoomDatabase的实例 __insertionAdapterOfUser :EntityInsertionAdapterd实例,用于数据insert __deletionAdapterOfUser:EntityDeletionOrUpdateAdapter实例,用于数据的update/delete
以下几个是UserDao里我们自己定义的接口
insertAll(): 使用__db开启事务,使用__insertionAdapterOfUser执行插入操作
delete():使用__db开启事务,使用__deletionAdapterOfUser执行删除操作 getAll():使用Cursor循环读取数据库的每条记录,并将结果保存在List<User>中返回 loadAllByIds():和getAll()类似,查询语句不同 findByNames():和getAll()类似,查询语句不同