是时候更新手里的武器了—Jetpack架构组件简析(下)

简介: 最近两年,MVVM的呼声越来越高,说实话,在经历了MVP的臃肿,MVP的繁琐,我有点怕了。但是这次Google官方带来的一系列为MVVM架构设计的武器—Jetpack,真的让我惊喜到了。

Jetpack-架构组件


Room


Room 持久性库在 SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite 的强大功能的同时,获享更强健的数据库访问机制。


所以Room就是一个数据库框架。问题来了,市面上那么多数据库组件,比如ormLite,greendao等等,为什么google还要出一个room,有什么优势呢?


  • 性能优势,一次数据库操作主要包括:构造sql语句—编译语句—传入参数—执行操作。ORMLite主要在获取参数属性值的时候,是通过反射获取的,所以速度较慢。GreenDao在构造sql语句的时候是通过代码拼接,所以较慢。Room是通过接口方法的注解生成sql语句,也就是编译成字节码的时候就生成了sql语句,所以运行起来较快。
  • 支持jetpack其他组件(比如LiveData,Paging)以及RxJava,这就好比借助了当前所在的优势环境,就能给你带来一些得天独厚的优势。当然实际使用起来也确实要方便很多,比如liveData结合,就能在数据查询后进行自动UI更新。


既然Room这么优秀,那就用起来吧。Room的接入主要有三大点:DataBase、Entity、Dao。分别对应数据库,表和数据访问。


1)首先导入库:


apply plugin: 'kotlin-kapt'
    dependencies {
      def room_version = "2.2.5"
      implementation "androidx.room:room-runtime:$room_version"
      kapt "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
      // optional - Kotlin Extensions and Coroutines support for Room
      implementation "androidx.room:room-ktx:$room_version"
      // optional - RxJava support for Room
      implementation "androidx.room:room-rxjava2:$room_version"
    }


2)建立数据库类,声明数据库表成员,数据库名称,数据库版本,单例等等


@Database(entities = arrayOf(User::class), version = 1)
abstract class UserDb : RoomDatabase() {
    abstract fun userDao(): UserDao
    companion object {
        private var instance: UserDb? = null
        @Synchronized
        fun get(context: Context): UserDb {
            if (instance == null) {
                instance = Room.databaseBuilder(context.applicationContext,
                    UserDb::class.java, "StudentDatabase").build()
            }
            return instance!!
        }
    }
}


3)建表,可以设置主键,外键,索引,自增等等


@Entity
data class User(@PrimaryKey(autoGenerate = true) val id: Int,
                val name: String)


4)Dao,数据操作


@Dao
interface UserDao {
    @Query("SELECT * FROM User")
    fun getAllUser(): DataSource.Factory<Int, User>
    @Query("SELECT * FROM User")
    fun getAllUser2(): LiveData<List<User>>
    @Query("SELECT * from user")
    fun getAllUser3(): Flowable<List<User>>
    @Insert
    fun insert(users: List<User>)
}


然后就可以进行数据库操作了,很简单吧。


官方文档


Demo代码地址


Paging


分页库可帮助您一次加载和显示一小块数据。按需载入部分数据会减少网络带宽和系统资源的使用量。


所以Paging就是一个分页库,主要用于Recycleview列表展示。下面我就结合Room说说Paging的用法。使用Paging主要注意两个类:PagedList和PagedListAdapter


1)PagedList


用于加载应用数据块,绑定数据列表,设置数据页等。结合上述Room的Demo我继续写了一个UserModel进行数据管理:


class UserModel(app: Application) : AndroidViewModel(app) {
    val dao = UserDb.get(app).userDao()
    var idNum = 1
    companion object {
        private const val PAGE_SIZE = 10
    }
    //初始化PagedList
    val users = LivePagedListBuilder(
        dao.getAllUser(), PagedList.Config.Builder()
            .setPageSize(PAGE_SIZE)
            .setEnablePlaceholders(true)
            .build()
    ).build()
    //插入用户
    fun insert() = ioThread {
        dao.insert(newTenUser())
    }
    //获取新的10个用户
    fun newTenUser(): ArrayList<User> {
        var newUsers = ArrayList<User>()
        for (index in 1..10) {
            newUsers.add(User(0, "bob${++idNum}"))
        }
        return newUsers
    }
}


2)PagedListAdapter


使用Recycleview必要要用到adatper,所以这里需要绑定一个继承自PagedListAdapter的adapter:


class UserAdapter : PagedListAdapter<User, UserAdapter.UserViewHolder>(diffCallback) {
    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        holder.bindTo(getItem(position))
    }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder =
        UserViewHolder(parent)
    companion object {
        private val diffCallback = object : DiffUtil.ItemCallback<User>() {
            override fun areItemsTheSame(oldItem: User, newItem: User): Boolean =
                oldItem.id == newItem.id
            override fun areContentsTheSame(oldItem: User, newItem: User): Boolean =
                oldItem == newItem
        }
    }
    class UserViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder(
        LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)) {
        private val tv1 = itemView.findViewById<TextView>(R.id.name)
        var user: User? = null
        fun bindTo(user: User?) {
            this.user = user
            tv1.text = user?.name
        }
    }
}


这里还用到了DiffUtil.ItemCallback 类,用于比较数据,进行数据更新用。


ok,数据源,adapter都设置好了,接下来就是监听数据,刷新数据就可以了


// 监听users数据,数据改变调用submitList方法
        viewModel.users.observe(this, Observer(adapter::submitList))


对,就是这么一句,监听PagedList,并且在它改变的时候调用PagedListAdapter的submitList方法。这分层够爽吧,其实这也就是paging或者说jetpack给我们项目带来的优势,层层解耦,adapter都不用维护list数据源了。


官方文档


Demo代码地址


ViewModel


ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。


终于说到ViewModel了,其实之前的demo都用了好多遍了,ViewModel主要是从界面控制器逻辑中分离出视图数据,为什么要这么做呢?主要为了解决两大问题:


  • 以前Activity中如果被系统销毁或者需要重新创建的时候,页面临时性数据都会丢失,需要通过onSaveInstanceState() 方法保存,onCreate方法中读取。而且数据量一大就更加不方便了。
  • 在Activity中,难免有些异步调用,所以就会容易导致界面销毁时候,这些调用还存在。那就会发生内存泄漏或者直接崩溃。


所以ViewModel诞生了,还是解耦,我把数据单独拿出来管理,还加上生命周期,那不就可以解决这些问题了吗。而且当所有者 Activity 完全销毁之后,ViewModel会调用其onCleared()方法,以便清理资源。


接下来举个🌰,看看ViewModel具体是怎么使用的:


def lifecycle_version = "2.2.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
class SharedViewModel : ViewModel() {
    var userData = MutableLiveData<User>()
    fun select(item: User) {
        userData.value = item
    }
    override fun onCleared() {
        super.onCleared()
    }
}
class MyFragment1 : Fragment() {
    private lateinit var btn: Button
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val model=activity?.let { ViewModelProvider(it).get(SharedViewModel::class.java) }
        btn.setOnClickListener{
            model?.select(User(0,"bob"))
        }
    }
}
class MyFragment2 : Fragment() {
    private lateinit var btn: Button
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val model=activity?.let { ViewModelProvider(it).get(SharedViewModel::class.java) }
        model?.userData?.observe(viewLifecycleOwner, Observer<User> { item ->
            // Update the UI
        })
    }
}


Fragment中,获取到viewmodel的实例,然后进行数据监听等操作。等等,你能发现什么不?对了,数据通信。不同的 Fragment 可以使用其父Activity共享ViewModel 来进行数据的通信,厉害吧。还有很多其他的用法,去项目中慢慢发现吧!


官方文档


Demo代码地址


WorkManager


使用 WorkManager API 可以轻松地调度即使在应用退出或设备重启时仍应运行的可延迟异步任务。


听听这个介绍就很神奇了,应用退出和设备重启都能自动运行?通过广播?那数据又是怎么保存的呢?听说还可以执行周期性异步任务,顺序链式调用哦!接下来一一解密


  • 关于应用退出和设备重启


如果APP正在运行,WorkManager会在APP进程中起一个新线程来运行任务;如果APP没有运行,WorkManager会选择一个合适的方式来调度后台任务--根据系统级别和APP状态,WorkManager可能会使用JobScheduler,FireBase JobDispatcher或者AlarmManager


  • 关于数据保存


WorkManager创建的任务数据都会保存到数据库,用的是Room框架。然后重启等时间段都会去数据库寻找需要安排执行的任务,然后判断约束条件,满足即可执行。


一般这个API应用到什么场景呢?想想,可靠运行,还可以周期异步。对了,发送日志。可以通过WorkManager设定周期任务,每天执行一次发送日志的任务。而且能够保证你的任务可靠运行,一定可以上传到,当然也是支持监听任务结果等。🌰:


1)导入库


dependencies {
      def work_version = "2.3.4"
        // Kotlin + coroutines
        implementation "androidx.work:work-runtime-ktx:$work_version"
        // optional - RxJava2 support
        implementation "androidx.work:work-rxjava2:$work_version"
        // optional - GCMNetworkManager support
        implementation "androidx.work:work-gcm:$work_version"
      }


2) 新建任务类,继承Worker,重写doWork方法,返回任务结果。


class UploadLogcatWork(appContext: Context, workerParams: WorkerParameters) :
    Worker(appContext, workerParams) {
    override fun doWork(): Result {
        if (isUploadLogcatSuc()) {
            return Result.success()
        } else if (isNeedRetry()){
            return Result.retry()
        }
        return Result.failure()
    }
    fun isUploadLogcatSuc(): Boolean {
        var isSuc: Boolean = false
        return isSuc
    }
    fun isNeedRetry(): Boolean {
        var isSuc: Boolean = false
        return isSuc
    }
}


3)最后就是设定约束(是否需要网络,是否支持低电量,是否支持充电执行,延迟等等),执行任务(单次任务或者循环周期任务)


//设定约束
        val constraints =
            Constraints.Builder()
                //网络链接的时候使用
                .setRequiredNetworkType(NetworkType.CONNECTED)
                //是否在设备空闲的时候执行
                .setRequiresDeviceIdle(false)
                //是否在低电量的时候执行
                .setRequiresBatteryNotLow(true)
                //是否在内存不足的时候执行
                .setRequiresStorageNotLow(true)
                //是否时充电的时候执行
                .setRequiresCharging(true)
                //延迟执行
                .setTriggerContentMaxDelay(1000 * 1, TimeUnit.MILLISECONDS)
                .build()
        //设定循环任务
        val uploadRequest =
            PeriodicWorkRequestBuilder<UploadLogcatWork>(1, TimeUnit.HOURS)
                .setConstraints(constraints)
                .addTag("uploadTag")
                .build()
        //执行
        WorkManager.getInstance(applicationContext).enqueue(uploadRequest)
        //监听执行结果
        WorkManager.getInstance(this)
//            .getWorkInfosByTagLiveData("uploadTag") //通过tag拿到work
            .getWorkInfoByIdLiveData(uploadRequest.id) //通过id拿到work
            .observe(this, Observer {
                it?.apply {
                    when (this.state) {
                        WorkInfo.State.BLOCKED -> println("BLOCKED")
                        WorkInfo.State.CANCELLED -> println("CANCELLED")
                        WorkInfo.State.RUNNING -> println("RUNNING")
                        WorkInfo.State.ENQUEUED -> println("ENQUEUED")
                        WorkInfo.State.FAILED -> println("FAILED")
                        WorkInfo.State.SUCCEEDED -> println("SUCCEEDED")
                        else -> println("else status ${this.state}")
                    }
                }
            })


4)另外还支持任务取消,任务链式顺序调用等


//取消
    fun cancelWork(){
  WorkManager.getInstance(applicationContext).cancelAllWorkByTag("uploadTag")
    }
    fun startLineWork(){
        //图片滤镜1
        val filter1 = OneTimeWorkRequestBuilder<UploadLogcatWork>()
            .build()
        //图片滤镜2
        val filter2 = OneTimeWorkRequestBuilder<UploadLogcatWork>()
            .build()
        //图片压缩
        val compress = OneTimeWorkRequestBuilder<UploadLogcatWork>()
            .build()
        //图片上传
        val upload = OneTimeWorkRequestBuilder<UploadLogcatWork>()
            .build()
        WorkManager.getInstance(applicationContext)
            .beginWith(listOf(filter1, filter2))
            .then(compress)
            .then(upload)
            .enqueue()
    }


官方文档


Demo代码地址


总结


Jetpack-架构组件讲完了,大家吃🌰应该吃饱了吧哈哈。希望这篇文章能让不怎么熟悉Jetpack的同学熟悉熟悉。


当然,这还远远不够,在我看来,本文更像是一个科普文,只是告诉了大家Jetpack-架构组件有哪些成员,有什么用处。实际项目中,我们还需要建立MVVM的思想,深刻了解每个组件的设计意义,灵活运用组件。(附件有个项目是官方的Jetpack实践项目,可以看看)


最后希望大家都能通过jetpack构建高质量,简易并优质的项目架构,从而解放生产力,成为效率达人


附件


https://github.com/android/sunflowerhttps://github.com/JiMuzz/jimu-Jetpack-Demo


谢谢你的阅读,如果你觉得写的还行,就点个赞支持下吧!感谢!


你的一个👍,就是我分享的动力❤️。

目录
相关文章
|
1月前
|
JSON JavaScript 前端开发
Vue3源码架构简析及Monorepo流程构建
【10月更文挑战第12天】Vue3源码架构简析及Monorepo流程构建
Vue3源码架构简析及Monorepo流程构建
|
29天前
|
消息中间件 存储 Java
RocketMQ(一):消息中间件缘起,一览整体架构及核心组件
【10月更文挑战第15天】本文介绍了消息中间件的基本概念和特点,重点解析了RocketMQ的整体架构和核心组件。消息中间件如RocketMQ、RabbitMQ、Kafka等,具备异步通信、持久化、削峰填谷、系统解耦等特点,适用于分布式系统。RocketMQ的架构包括NameServer、Broker、Producer、Consumer等组件,通过这些组件实现消息的生产、存储和消费。文章还提供了Spring Boot快速上手RocketMQ的示例代码,帮助读者快速入门。
|
1月前
|
存储 分布式计算 API
大数据-107 Flink 基本概述 适用场景 框架特点 核心组成 生态发展 处理模型 组件架构
大数据-107 Flink 基本概述 适用场景 框架特点 核心组成 生态发展 处理模型 组件架构
84 0
|
12天前
|
SQL 数据采集 分布式计算
【赵渝强老师】基于大数据组件的平台架构
本文介绍了大数据平台的总体架构及各层的功能。大数据平台架构分为五层:数据源层、数据采集层、大数据平台层、数据仓库层和应用层。其中,大数据平台层为核心,负责数据的存储和计算,支持离线和实时数据处理。数据仓库层则基于大数据平台构建数据模型,应用层则利用这些模型实现具体的应用场景。文中还提供了Lambda和Kappa架构的视频讲解。
【赵渝强老师】基于大数据组件的平台架构
|
1月前
|
SQL 存储 分布式计算
大数据-157 Apache Kylin 背景 历程 特点 场景 架构 组件 详解
大数据-157 Apache Kylin 背景 历程 特点 场景 架构 组件 详解
29 9
|
1月前
|
消息中间件 监控 Java
大数据-109 Flink 体系结构 运行架构 ResourceManager JobManager 组件关系与原理剖析
大数据-109 Flink 体系结构 运行架构 ResourceManager JobManager 组件关系与原理剖析
66 1
|
30天前
|
消息中间件 运维 NoSQL
基础架构组件选型及服务化
【10月更文挑战第15天】本文概述了分布式系统中常见的基础架构组件及其选型与服务化的重要性。
|
1月前
|
消息中间件 运维 NoSQL
基础架构组件选型及服务化
【10月更文挑战第2天】本文介绍了常见的分布式基础架构组件,包括分布式服务化框架(如Dubbo、Spring Cloud)、分布式缓存(如Redis、Memcached)、数据库及分布式数据库框架(如MySQL、TiDB)、消息中间件(如Kafka、RabbitMQ)和前端接入层(如LVS、Nginx)。文中探讨了组件选型问题,强调统一标准的重要性,避免重复劳动与维护难题。最后,提出基础架构服务化的必要性,通过标准化和平台化提升运维效率
|
6月前
|
存储 安全 Android开发
构建高效的Android应用:Kotlin与Jetpack的结合
【5月更文挑战第31天】 在移动开发的世界中,Android 平台因其开放性和广泛的用户基础而备受开发者青睐。随着技术的进步和用户需求的不断升级,开发一个高效、流畅且易于维护的 Android 应用变得愈发重要。本文将探讨如何通过结合现代编程语言 Kotlin 和 Android Jetpack 组件来提升 Android 应用的性能和可维护性。我们将深入分析 Kotlin 语言的优势,探索 Jetpack 组件的核心功能,并通过实例演示如何在实际项目中应用这些技术。
|
5月前
|
数据管理 API 数据库
探索Android Jetpack:现代安卓开发的利器
Android Jetpack是谷歌为简化和优化安卓应用开发而推出的一套高级组件库。本文深入探讨了Jetpack的主要构成及其在应用开发中的实际运用,展示了如何通过使用这些工具来提升开发效率和应用性能。
下一篇
无影云桌面