数据存储&网络通信 | 青训营笔记

43 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的第4天

概述

  • 网络通信(网络库框架对比、Retrofit使用&原理介绍、TTNet介绍,了解字节跳动网络请求框架)
  • 数据存储(Android数据存储方式对比、数据库框架对比、Room数据库使用与原理介绍)

网络通信

网络框架对比

Retrofit的使用介绍

Retrofit其实是对OkHttp的一个封装,也是当前最为流行的一种网络请求组合方式。

  1. 添加Retrofit库的依赖

在需要用到Retrofit接口的module中,新增依赖

dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    //...其他依赖
}
  1. 创建用于描述网络请求的接口
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请求
}
  • 接口类名:可以自定义,尽量和这类请求的含义相关
  • 函数名:可以自定义,需要能识别出接口的作用,可以添加多个不同函数
  • @GET 注解:用于指定该接口的相对路径,并采用Get方法发起请求
  • @Path 注解:需要外部调用时,传入一个uid,该uid会替换@GET注解里相对路径的{uid}
  • 返回值Call,这里用ResponseBody,我们可以直接拿到请求的String内容如果要自动转为Model类,例如User,这里直接替换为User就好
  1. 发起网络请求
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
        }
    })
}

TTNet

  • 基于Retrofit改造,具备了Retrofit所具有的优点
  • 支持多个Http网络库的动态切换(okhttp和cronet)
  • 支持网络拦截配置:添加公共参数,动态切换协议及Host,动态选路等
  • 支持流解析,json序列化
  • ......

实现原理:

  • 替换底层用到的OKHttpClient
  • 替换底层用到的OKHttpCall

注解介绍

注解的处理,一般有3个时机(也就是注解的生命周期@Retention)

  1. SOURCE:只有在源码中有效,编译时抛弃,例如前面的@Override
  1. CLASS:编译class.文件时有效,一般会使用到注解处理器。
  1. RUNTIME:在运行期间,获取对应的注解,并做相关的处理。

注解的获取和使用,通过反射获取到Method对象后,有以下一些接口来获取注解内容

  1. Method.getGenericReturnType()获取返回类型
  1. Method..getAnnotations()获取方法的注解
  1. Method..getParameterAnnotations()获取参数注解

Retrofit是在运行期间,配合Java动态代理,获取方法和参数的注解,并构造Request对象的。

总结

数据存储

存储方式对比

数据库开源框架对比

Room数据库的使用

Room是 Google Jetpack 家族里的一员,Room 在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库

主要三个组件

  • 数据库类(Database),用于保存数据库并作为应用持久性数据底层连接的主要访问点。
  • 数据实体(Entity),用于表示应用的数据库中的表。
  • 数据访问对象(DAO),提供您的应用可用于查询、更新、插入和删除数据库中的数据的方法

  1. Room的接入

Gradle目录的build.gradle文件里添加如下:

dependencies {
def room_version = "2.4.2"
    implementation "androidx.room:room-runtime:room_version"
    kapt "androidx.room:room-compiler:room_version"
    
    //。。。其他依赖
 } 
  1. 设计数据库表
  1. 新建Entity

一个Entity代表数据库中的一张表(table)。我们使用@Entity定义一个Entiry类,类中的属性对应表中的Column

@Entity
data class User(
  @PrimaryKey val uid: Int,
  @ColumnInfo(name = "first_name") val firstName: String?,
  @ColumnInfo(name = "last_name") val lastName: String?
)
  1. 新建DAO
@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>
  
  @Insert
  fun insertAll(vararg users: User)
  
  @Delete
  fun delete(user: User)
}
  1. 新建数据库类

Database是我们访问底层数据库的入口,管理着真正的数据库文件。我们使用@Database定义一个Database类:

  • 派生自RoomDatabase
  • 关联其内部数据库table对应的entities
  • 提供获取DAO的抽象方法,且不能有参数
@Database(entities = arrayOf(User::class), version = 1)
abstract class UserDatabase : RoomDatabase() {
  abstract fun userDao(): UserDao
}
  1. 获取dao对象
val db = Room.databaseBuilder(applicationContext,AppDatabase:class.java,name:"database-name")
.build()
userDao = db.userDao()

原理介绍

  • 编译期,通过kapt处理@Dao@Database注解,动态生成对应的实现类
  • 底层使用Android提供的SupportSQLiteOpenHelper实现数据库的增删改查等操作

总结