公众号回复 Compose 获取安装包

项目地址: Wanandroid-Compose

经过前段时间的 Android Dev Summit ,相信你已经大概了解了 Jetpack Compose 。如果你还没有听说过,可以阅读这篇文章 Jetpack Compose 最新进展 。总而言之,Compose 是一个 颠覆性声明式 UI 框架 ,它的口号就是 消灭 xml 文件 !

尽管 Jetpack Compose 还只是预览版,API 可能发生变化,缺乏足够的控件支持,甚至不是那么稳定,但这阻止不了我这颗好奇的心。我在第一时间就上手撸了一款 Compose 版本 Wanandroid 应用,功能也比较简单,仅仅包括首页,广告和最新项目,类似于 Android 原生页面的 Viewpager + TabLayout 。下面的 gif 展示了应用的基本页面:

可以看出来页面并不是那么流畅,View 的复用应该是个问题,甚至我也没发现应该怎么做下拉刷新。那么,Compose 给我们带来了什么呢?在解答这个问题之前,我想先来说说 Android 应用架构问题。

荒芜年代 —— MVC

在我刚入行的时候,可以说是 Android 开发的黄金时代,也可以说是开发者的荒芜时代。一方面,毫不夸张的说,基本会写 xml 都能谋得一份工作。另一方面,对于开发者来说,远远没有现在的规范的开发架构。没记错的话,我当年的主力开发框架是 xUtils 2 ,一个类库大包干,从 布局和 ID 绑定,网络请求,到图片展示,ORM 操作,一应俱全。当时的 布局和 ID 绑定还是运行时反射,而不是编译期注解。很长一段时间以来,Android 连一个官方的网络库都没有。

在架构方面,很多人都是一个 Activity 撸到死,我真的见过上千行zouguolai的 MainActivity 。并且觉得这就是 MVC 架构,实体类 Entity 就是 Model 层,Activity/Fragment 就是 Controller 层,布局文件就是 View 层。

但这当真是 MVC 吗?其实并不是。不管是 MVCMVP,还是 MVVM,都应该遵循一个最起码的原则,表现层和业务层分离 ,也就是 Android 官网给出的 应用架构指南 中强调的 分离关注点Activity/Fragment 既要承担视图层的任务,展示和更新 UI,又要处理业务层逻辑,获取数据等。这并不符合架构设计的基本原则。

正确的 MVC 模式中,Model 层不仅包含实体类 Entity,更重要的作用是处理业务逻辑。View 层负责处理视图逻辑。而 Controller 就是 Model 和 View 之间的桥梁。桥怎么建,其实并没有标准,根据你自己的需求就可以了。

引用一张阮一峰老师的图,大致是这么个意思,但是也不一定就完全都是单向依赖。

从荒芜时代走过来,MVC 总算有点分层的味道在里面了,分离了视图层和业务层。但是 View 层和 Model 层的依赖关系,造成代码耦合,终将导致 Activity 日益臃肿。那么有没有办法将 View 层和 Model 层彻底分离,做到视图层和模型层完全分离呢? MVP 就应运而生了。

青铜年代 —— MVP

依旧是阮一峰老师的图片:

相较于 MVC ,MVP 用 Presenter 层 代替了 Controller 层 ,且 View 层Model 层 完全分离,依靠 Presenter 进行通信 。

想象一个获取用户信息的场景。IView 接口中定义了一系列视图层接口 ,View 层(Activity)实现 IView 接口中相应视图逻辑。 View 层通过持有的 Presenter 处理业务逻辑,即请求用户信息。一般情况下,Presenter 也不直接处理业务逻辑,而是通过 Model 层,例如数据仓库 Repository, 来获取数据,避免 Presenter 重蹈覆辙,日渐臃肿。同时,Presenter 层也是持有 VIew 的,获取用户信息之后再转发给 View 。

总结一下,MVP 中 View 和 Model 完全解耦,通过 Presenter 通信。View 和 Presenter 共同处理视图层逻辑,Model 层负责业务逻辑。

在 Github 上 Android 官方的架构示例 architecture-samples 中 MVP 作为主分支坚挺了很久。我最初也是根据这个官方示例改造了自己的 MVP 架构,并且使用了很长时间。但是 MVP 作为一款面向接口编程的架构,随着业务的复杂程度不断加大,有种遍地都是接口的既视感,实在显得有点繁琐。

另外一点,Presenter 的职责边界不够清晰,它除了承担调用 Model 层获取业务逻辑之外,还要控制 View 层处理 UI。用下面一段代码表示一下:

  1. class LoginPresenter(private val mView: LoginContract.View) : LoginContract.Presenter {
  2. ......
  3. override fun login(userName: String, passWord: String) {
  4. CoroutineScope(Dispatchers.Main).launch {
  5. val result = WanRetrofitClient.service.login(userName, passWord).await()
  6. with(result) {
  7. if (errorCode == -1) mView.loginError(errorMsg) else mView.login(data)
  8. }
  9. }
  10. }
  11. }

一旦 View 层发生任何变化,Presenter 层也要做出相应改动。虽然 View 和 Model 之间解耦了,但是 View 和 Presenter 却耦合了。理想情况下,Presenter 层应该仅负责数据的获取,View 层自动观察数据的变化。于是,MVVM 来了。

黄金时代 —— MVVM

Google 官图镇楼 。

MVP 风光早已不在, Android 官方的架构示例 architecture-samples 的主分支已经切换到 MVVM 。在 Android 的 MVVM 架构中,ViewModel 是重中之重,它一方面通过数据仓库 Repository 获取数据,另一方面根据获取的数据更新 View 层的 Activity/Fragment。等等,这句话怎么听着这么耳熟,Presenter 不也是干了这些事吗?的确,它们干的事情都差不多,但是实现上完全不一样。

以我的开源项目 Wanandroid 中的 LoginViewModel 为例:

  1. class LoginViewModel(val repository: LoginRepository) : BaseViewModel() {
  2. private val _uiState = MutableLiveData<LoginUiModel>()
  3. val uiState: LiveData<LoginUiModel>
  4. get() = _uiState
  5. fun loginDataChanged(userName: String, passWord: String) {
  6. emitUiState(enableLoginButton = isInputValid(userName, passWord))
  7. }
  8. // ViewModel 只处理视图逻辑,数据仓库 Repository 负责业务逻辑
  9. fun login(userName: String, passWord: String) {
  10. viewModelScope.launch(Dispatchers.Default) {
  11. if (userName.isBlank() || passWord.isBlank()) return@launch
  12. withContext(Dispatchers.Main) { showLoading() }
  13. val result = repository.login(userName, passWord)
  14. withContext(Dispatchers.Main) {
  15. if (result is Result.Success) {
  16. emitUiState(showSuccess = result.data,enableLoginButton = true)
  17. } else if (result is Result.Error) {
  18. emitUiState(showError = result.exception.message,enableLoginButton = true)
  19. }
  20. }
  21. }
  22. }
  23. private fun showLoading() {
  24. emitUiState(true)
  25. }
  26. private fun emitUiState(
  27. showProgress: Boolean = false,
  28. showError: String? = null,
  29. showSuccess: User? = null,
  30. enableLoginButton: Boolean = false,
  31. needLogin: Boolean = false
  32. ) {
  33. val uiModel = LoginUiModel(showProgress, showError, showSuccess, enableLoginButton,needLogin)
  34. _uiState.value = uiModel
  35. }
  36. data class LoginUiModel(
  37. val showProgress: Boolean,
  38. val showError: String?,
  39. val showSuccess: User?,
  40. val enableLoginButton: Boolean,
  41. val needLogin:Boolean
  42. )
  43. }

可以看到,ViewModel 中是没有 View 的引用的,View 通过可观察的 LIveData 来观察数据变化,基于观察者模式做到和 ViewModel 完全解耦。

数据驱动视图 ,这是 Jetpack MVVM 推崇的一个重要原则。其基本数据流如下所示 :

  • 数据层 Repository 负责从不同数据源获取和整合数据,基本负责所有的业务逻辑
  • ViewModel 持有 Repository,获取数据并驱动 View 层更新
  • View 持有 ViewModel,观察 LiveData 携带的数据,数据驱动 UI

曾经和一些开发者讨论过这样一个问题,** 不使用 DataBinding 还算是 MVVM 吗 ?** 我认为 MVVM 的核心从来不在于 DataBinding 。DataBinding 只是可以帮助我们将 数据驱动视图 做到极致,顺便还可以双向绑定。

要说到对 Jetpack MVVM 中最不满意的一块,那非 DataBinding 莫属了。在我狭隘的认为 DataBinding 就是一个在 xml 里面写逻辑代码的反人类的库时,我是坚决反对在任何项目中引入它的。固执己见的时候就容易走进误区,在阅读 KunminX 的 重学安卓:从 被反对 到 真香 的 Jetpack DataBinding! 之后,正如这篇文章名字一样,真香。

香的确是香,一切能让我早下班的都是好东西。在我的某次提交日志上,我写下了 消灭 Adapter 几个字,那时我刚用 DataBinding 消灭了大部分 RecyclerView 的 Adapter 。可是在提交之后,我的良心惴惴不安,我追究还是在 xml 文件里写逻辑代码了,难道这真的不反人类吗?

未来可期 —— Jetpack Compose

现在你应该可以理解我对 Jetpack Compose 的执念了。抛去其他特性,在我看来,它完美的解决了 数据驱动视图 的问题,我再也不需要使用 DataBinding 了。

简单代码展示一下 Compose 的用法。下面的代码描绘的是首页 Tab 下的文章列表。

  1. @Composable
  2. fun MainTab(articleUiModel: ArticleViewModel.ArticleUiModel?) {
  3. VerticalScroller {
  4. FlexColumn {
  5. inflexible {
  6. HeightSpacer(height = 16.dp)
  7. }
  8. flexible(1f) {
  9. articleUiModel?.showSuccess?.datas?.forEach {
  10. ArticleItem(article = it)
  11. }
  12. articleUiModel?.showError?.let { toast(App.CONTEXT, it) }
  13. wenjian
  14. articleUiModel?.showLoading?.let { Progress() }
  15. }
  16. }
  17. }
  18. }

这种写法叫做 声明式编程 ,会用 Flutter 的同学应该很熟悉。方法参数 ArticleUiModel 就是数据实体类,直接根据数据 ArticleUiModel 构建 UI 。说的大白话一点,就是给你长方形的长和宽了,让你画个长方形出来。最后加上 @Compose 注解,就是一个可用的 UI 组件了。仔细看代码,里面还用了两个 UI 组件 ,ArticleItemProgress ,代码就不贴出来了。分别是文章列表的 item 项目 和加载进度条。

那么,数据如何更新呢?最简单的方式是使用 @Model 注解。

  1. @Model
  2. data class ArticleUiModel(){
  3. ......
  4. }

对,就是这么简单。@Model 注解会自动把你的数据类变成可观察对象,只要 ArticleUIModel 发生变化,UI 就会自动更新。

但是我在实际开发中结合 LiveData 使用时,好像表现的不是那么正常。后来在 Medium 上无意中看到了解决方案,针对 LiveData 做了特殊处理 :

  1. // general purpose observe effect. this will likely be provided by LiveData. effect API for
  2. // compose will also simplify soon.
  3. fun <T> observe(data: LiveData<T>) = effectOf<T?> {
  4. val result = +state<T?> { data.value }
  5. val observer = +memo { Observer<T> { result.value = it } }
  6. +onCommit(data) {
  7. data.observeForever(observer)
  8. onDispose { data.removeObserver(observer) }
  9. }
  10. result.value
  11. }wenjian

在 Activity/Fragment 中观测 LiveData 即可:

  1. class MainActivity : BaseVMActivity<ArticleViewModel>() {
  2. override fun initView() {
  3. setContent {
  4. +observe(mViewModel.uiState)
  5. WanandroidApp(mViewModel)
  6. }
  7. }
  8. override fun initData() {
  9. mViewModel.getHomeArticleList()
  10. }
  11. }

这样 View 层就可以自动观察 LiveData 所包含的值了。

没有 xml,没有 DataBinding,一切看起来称心如意多了。但就是 UI 体验有那么一点糟心,你可以在公众号后台回复 Compose 安装体验一下。由于还是早期的预览版,这也是可以理解的。我相信,等到发布 Release 版本的时候,一定足以完全代替原声的 View 体系。

本文并没有详细介绍 Jetpack Compose 的详细使用过程和其他特性,更多信息我推荐下面两篇文章:

最后

正如 Android 官网 Jetpack 介绍页所说,Jetpack 可以帮助开发者更轻松的编写优质应用。的确,随着应用架构的规范,我们只需要把精力放在需要的代码上,加速开发,消除样板代码,减少崩溃和内存泄露,构建高质量的强大应用。我想不出来有任何理由不使用 Jetpack 来构建你的应用。而 Compose 必将称为 Jetpack 中极其重要的一块拼图。

Jetpack Compse ,未来可期 !


添加我的微信,加入技术交流群。

公众号后台回复 “compose”, 获取最新安装包。

Jetpack Compse 实战 —— 全新的开发体验的更多相关文章

  1. NET Core全新的开发体验

    NET Core全新的开发体验 2016年6月27日,这是一个特殊的日子,微软全新的.NET开发平台.NET Core的RTM版本正式发布.我个人将.NET Core的核心特性归结为三点,它们的首字母 ...

  2. 通过几个Hello World感受.NET Core全新的开发体验

    2016年6月27日,这是一个特殊的日子,微软全新的.NET开发平台.NET Core的RTM版本正式发布.我个人将.NET Core的核心特性归结为三点,它们的首字母组成一个非常好记的简称——COM ...

  3. springboot 实战之一站式开发体验

    都说springboot是新形势的主流框架工具,然而我的工作中并没有真正用到springboot: 都说springboot里面并没有什么新技术,不过是组合了现有的组件而已,但是自己却说不出来: 都说 ...

  4. .NET Core多平台开发体验[1]: Windows

    微软在千禧年推出 .NET战略,并在两年后推出第一个版本的.NET Framework和IDE(Visual Studio.NET 2002,后来改名为Visual Studio),如果你是一个资深的 ...

  5. [ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [上篇]

    微软在千禧年推出 .NET战略,并在两年后推出第一个版本的.NET Framework和IDE(Visual Studio.NET 2002,后来改名为Visual Studio),如果你是一个资深的 ...

  6. [转][ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [上篇]

    微软在千禧年推出 .NET战略,并在两年后推出第一个版本的.NET Framework和IDE(Visual Studio.NET 2002,后来改名为Visual Studio),如果你是一个资深的 ...

  7. Jetpack MVVM 实战项目,附带源码+视频,收藏!

    从读者的反馈来看,近期大部分安卓开发已跳出舒适圈,开始尝试认识和应用 Jetpack MVVM 到实际的项目开发中. 只可惜,关于 Jetpack MVVM,网上多是 东拼西凑.人云亦云.通篇贴代码  ...

  8. Microsoft Graph Web应用程序极致开发体验

    作者:陈希章 重写于 2017年5月24日 前言 这篇文章最早写于2017年5月2日,当时的想法是从最简单的方式来写如何在一个ASP.NET MVC应用程序中集成Microsoft Graph,但实际 ...

  9. 【Android开发VR实战】三.开发一个寻宝类VR游戏TreasureHunt

    转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53939303 本文出自[DylanAndroid的博客] [Android开发 ...

随机推荐

  1. Linux防火墙常用操作

    /tcp —— 配置白名单 sudo systemctl start firewalld — 启动防火墙 sudo firewall-cmd --state - 看状态 sudo firewall-c ...

  2. 维护基于ASP.NET的网站的学习-SqlCommand类介绍及存储过程

    笔者目前在维护学校科技处的一个网站,目前学期初,教师申报项目操作多,出现了一些问题.前几天维护了一个验证码图片不显示的bug,今天想记录下这个解决了一整天的bug-老师项目结题需要手动修改数据库老师项 ...

  3. [NOIp2013] luogu P1970 花匠

    scy居然开网了. 题目描述 你有一个序列 aaa,你需要保留尽量多的数,使得剩下的数满足以下条件中的一个: ∀x∈[2,n−1]∩N∗\forall x\in[2,n-1]∩\N^*∀x∈[2,n− ...

  4. The Mininum Function Value (luoguP2085 最小函数值)

    Background\text{Background}Background 1. CSDN's been upgraded today. There's one MORE ad for each pa ...

  5. caffe中batch norm源码阅读

    1. batch norm 输入batch norm层的数据为[N, C, H, W], 该层计算得到均值为C个,方差为C个,输出数据为[N, C, H, W]. <1> 形象点说,均值的 ...

  6. Dell R720 RAID配置

    Dell服务器上一般都带有Raid卡,Raid5配置请看下边,亲们 1. 将服务器接上电源,显示器,键盘,并开机 2. 按 ctrl + R进入Raid设置 3. 将光标放置在Raid卡那,按F2,选 ...

  7. JAVA动态代理 你真的完全了解Java动态代理吗?

    网上讲JAVA动态代理,说的天花乱坠,发现一篇文章写的通俗易懂,特意转载过来 原文地址:https://www.jianshu.com/p/95970b089360 动态代理看起来好像是个什么高大上的 ...

  8. SpringBoot生命周期管理之停掉应用服务几种方法

    前言 在生产环境下管理Spring Boot应用的生命周期非常重要.Spring容器通过ApplicationContext处理应用服务的所有的beans的创建.初始化.销毁. 本文着重于生命周期中的 ...

  9. ESP8266 打造一款物联网产品---搭建环境编译及烧录

    一 前记 作为一个在wifi领域耕耘了多年的人,以前一直在外企和大公司做芯片,没有怎么使用过国内的芯片公司做出来的芯片.最近正好有一个项目需要用到一款低成本的wifi芯片,找来找去,发现乐鑫的最适合. ...

  10. github实用的搜索小技巧

    查资源,学习优秀的框架,搜索是一种能力! 作为程序猿开发中最大的同性交友网站,github当之无愧,里面有很多优秀的开源框架,各种技术大佬混迹其中,有他们总结的学习教程,造好的轮子(开发的各种工具,技 ...