这是我参与「第四届青训营 」笔记创作活动的第8天
第四节课 [数据储存 & 网络通信]
网络通信
-
网络请求框架对比
- HttpURLConnection:系统自带,小App没太多网络请求时可简单封装使用
- Volley:事业频繁返回数据体小的App,不适合文件下载,已停更
- OkHttp:性能好,支持大文件下载,需要封装使用
- Retrofit:基于OkHttp的封装,有一定门槛
-
Retrofit
-
介绍:是对OkHttp的一个封装框架
-
使用
-
Retrofit库的引入: 在build.gradle中
dependencies { impementation 'com.squareup.retrofit2:converter-gson:2.4.0' } -
创建用于描述网络请求的接口
- 需要加@GET注解:用于指定该接口的相对路径,并采用Get方法发起请求
- 参数@Path注解:需要外部调用时,传入一个uid,该uid会替换@GET注解中的{uid}
- 返回值
Call<ResponseBody>:可以直接拿到请求的String内容
-
使用Retrofit实例发起网络请求
- 创建Retrofit实例
- 创建请求接口的实例并获取到Call实例
- 调用call.enqueue进行异步请求
- 处理返回的数据
-
-
原理
- 使用注解来描述网络请求
- 通过java同台代理Proxy.newProxyInstance来进行注解的识别和网络请求的构建
- 底层使用OkHttp发起网络请求
-
Retrofit主流程
- 通过Builder模式,创建RetrofitConfig,保存baseUrl等内容
- 创建动态代理对象
- 创建OkHttpCall
- 发起网络请求
-
-
注解:
-
介绍:一个标签,加在类、方法、参数、成员变量上,并且在合适的时机读取注解中的内容进行处理
-
处理时机
- SOURCE:源码中有效,编译时抛弃
- CLASS编译class文件时有效,一般会用到注解处理器
- RUNTIME:在运行期间,获取对应的注解,并做相关的处理
-
常用元注解
- @Target():表示注解用在什么地方
(例:value={ElementType.METHOD,ElementType.TYPE}) - @Retention():表示注解在什么地方还有效(例:
value=RetentionPolicy.RUNTIME) - @Documented:是否将注解生成在JAVAdoc中
- @Inherited:子类可以继承父类的注解
- @Target():表示注解用在什么地方
-
注解的获取和使用:通过反射获取到Method对象后,有以下几个接口来获取注解内容
- Method.getGenericReturnType():获取返回类型
- Method.getAnnotations():获取方法的注解
- Method.getParameterAnnotations():获取参数注解
-
Retrofit的注解类型
(Retrofit在运行期间,配合Java动态代理,获取方法的参数和注解,并构造Requeset对象)- 网络请求和方法:@GET,@POST,@PUT,@DELETE 等
- 标记类:@formUriEncoded,@Multipart,@Streaming
- 网络请求参数:@Header,@headers,@URL,@Body 等
-
数据存储
-
存储方式对比
- SharedPreferences:键值对存储
- 文件存储:存储二进制格式文件
- ContentProvider:跨进程共享数据
- SQLite:复杂对象存储,对数据增删改查
-
Room
Room在SQLite上提供了一个抽象层,以便在充分利用SQLite功能的同时流畅的访问数据库
-
主要的三个组件:
- Database(数据库类):用于保存数据库并作为应用持久性数据底层连接的主要访问点
- Entity(数据实体):用于表示应用的数据库中的表
- DAO(数据访问对象):提供应该用于对数据库增删改查的方法
-
使用
- Room接入
dependencies { def room_version = "2.4.2" implementation "androidx.room:room-runtime:$room_version" kapt "androidx.room:room-complier:$room_version" }-
设计数据表
-
新建Entity数据实例:
用于定义表名和列名: 自定义一个User数据实体,User的每个实例都代表App数据库中的user表的一行- @PrimaryKey:表示单个主键,当主键为null且autoGenerate为true时可以帮助自动生成键值
- @Columnlnfo:列名的注解
@Entity data class User( @PrimaryKey(autoGenerate = true) val uid:Int?, @ColumnInfo(name = "first_name") var firstName:String?, @ColumnInfo(name = "last_name") var lastName:String? ) -
新增DAO接口
定义需要用到的数据库操作方法:定义一个名为UserDao的DAO,用来对User表的增删改查@Dao interface UserDao { @Query("SELECT * FROM user") fun getAll(): List<User>? @Query("SELECT * FROM user WHERE uid IN (:userIds)") fun loadAllByIds(userIds: IntArray):List<User>? @Query("SELECT * FROM user WHERE first_name LIKE :first AND last_name LIKE :last") fun findByName(first: String, last: String): User? @Insert fun insertAll(vararg users: User) @Delete fun delete(user: User) -
新建数据库类,进行数据库配置,并需满足以下几个条件:
a. 新增一个RoomDatabase的abstract子类(定义数据库版本等)
b. 子类加注解
@Database(entities=[], version=n),entities包含数据实体,将会在这个数据库中创建对应的表,version是数据的版本号c. 对于与数据库关联的每个DAO类,数据库类必须定义一个无参的抽象方法,并返回DAO类实例
@Database(entities = [User::class], version=1) abstract class AppDatabase : RoomDatabase(){ abstract fun userDao(): UserDao } -
获取db和dao对象 : 调用dao接口进行增删改查
-
Room原理
-
编译器,通过kapt处理@Dao、@Database注解,动态生成对应的实现类
-
Room在编译期,通过kapt处理@Dao和@Database注解,生成DAO和Database的实现类
-
AppDataBase --> AppDatabase_Impl
- 方法 createOpenHelper():生成包
- 方法 userDao():判断是否为空(单例模式)
-
UserDao --> UserDao_Impl
- 成员变量 __db:RoomDatabase的实例
- 成员变量 __insertionAdapterOfUser:EntityInsertionAdapterd实例,用于数据insert
- 成员变量 __deletionAdapterOfUser:EntityDeletionOrUpdateAdapter实例,用于数据修改或删除
-
-
底层使用Android提供的SupprotSQLite、OpenHelper实现数据库的增删改查等操作
-
-