精华内容
下载资源
问答
  • JetPack

    2021-09-29 10:40:13
  • Jetpack

    2021-07-12 20:43:11
    Jetpack 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录...

    Jetpack


    一、Jetpack是什么?

    什么是JetPack?有人回答 Jetpack 就是 LiveData、ViewModel 这些东西,有人回答 Jetpack 是一套 MVVM 框架,当然更多人的回答是,听过、但没用过,所以也说不出它到底是什么
    在 Jetpack 的官方文档中是这样对它定义的:
    Jetpack 是一个丰富的组件库,它的组件库按类别分为 4 类,分别是架构(Architecture)、界面(UI)、行为(behavior)和基础(foundation)。每个组件都可以单独使用,也可以配合在一起使用。每个组件都给用户提供了一个标准,能够帮助开发者遵循最佳做法,减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者能够集中精力编写重要的业务代码。

    二、Jetpack的优势

    1.解决了 Android 架构问题

    解决了 Android 架构问题
    由于之前 Google 并没有推出关于 Android 应用程序架构设计的标准,因此,很多工程师只能自己创造各种解决方案,但这些方案都普遍存在两个问题:

    非 Google 官方解决方案
    一般有经验的工程师,都会从自己做过的项目中,总结出一套自己的架构设计,并且将其应用到生产环境中,不过或多或少的都会存在一定的问题,因此需要有人一直维护和持续优化项目的架构。并且随着项目需求复杂度的增加,也面临重新设计架构的工作。

    无法辨别最佳的解决方案
    由于每个工程师的设计思路都不相同,因此,也无法确认到底谁的架构是最佳方案,最终也导致了开发出来的应用参差不齐。

    Google 也意识到了这些问题,因此推出了 Jetpack,让开发者能够使用标准的架构组件,而不用去纠结架构的方案设计,可以将更多的精力放在自己的业务代码上。

    2.提升了代码质量

    Jetpack 拥有基于生命周期感知的能力,可以减少 NPE 崩溃、内存泄漏。为我们开发出健壮且流畅的程序提供强力保障;

    3.提升了开发效率

    Jetpack 可以减少样板代码,有助于提升 Android 开发的效率。这些组件可以单独使用,也可以组合使用,并且在不同 Android 版本中运行一致。

    Jetpack与Androidx

    Jetpack 是各种组件库的统称,AndroidX 是这些组件的统一包名。

    AndroidX 对原始 Android Support Library 进行了重大改进,后者现在已不再维护。androidx 软件包完全取代了 support 包,不仅提供同等的功能,而且提供了新的库。Jetpack 组件中也是完全使用 androidx 开头的包名。

    与 Support Library 一样,androidx 命名空间中的库与 Android 平台分开提供,并向后兼容各个 Android 版本。

    注意:

    AndroidX 中的所有软件包都使用一致的命名空间,以字符串 androidx 开头。Support Library 软件包已映射到对应的 androidx.* 软件包。

    与 Support Library 不同,androidx 软件包会单独维护和更新。从版本 1.0.0 开始,androidx 软件包使用严格的语义版本控制。可以单独更新项目中的各个 AndroidX 库。

    版本 28.0.0 是 Support Library 的最后一个版本。以后将不再发布 android.support 库版本。所有新功能都将在 androidx 命名空间中开发。

    Jetpack的组件划分

    jetpack按照功能区分 共分四块

    • Foundation (基础组件)
      1.AppCompat:使得支持较低的 Android 版本
      2.Android KTX:Kotlin 的扩展支持库
      3.Multidex:多 dex 文件支持
      4.Test:测试支持库
    • Architecture(架构组件)
      1.Data Binding:MVVM 的一种实践
      2.Lifecycles:管理你的 Activity 和 Fragment 生命周期
      3.LiveData:通过观察者模式感知数据变化,类比 RxJava
      4.Navigation:处理 Fragment 导航相关逻辑
      5.Paging:分页数据加载方案
      6.Room:官方 ORM 库
      7.ViewModel:通过数据驱动 V 视图发生改变
      8.WorkManager:管理后台任务
    • Behavior(行为组件)
      1.DownloadManager:管理下载任务
      2.Media App:多媒体播放和一些向后兼容的API。主要包含 MediaPlayer 和 ExoPlayer
      3.Notifications:提供向后兼容的通知 API,支持 Wear 和 Auto
      4.Permissions:权限管理,这个应该都接触过。用于检查和请求应用权限
      5.Preference 相关 API。基本每个应用都会用到
      6.Settings:Preference 相关 API。基本每个应用都会用到
      7.Share Action:提供分享操作。这块在国内使用的不多,都是自己封装或者采用第三方方案
      8.Slices:可以让应用通过外部(其他 APP)显示 APP 界面(通过设备自带的搜索,语音助手等)
    • UI(界面组件)
      1.Animations and Transitions:动画,界面转场等
      2.Auto:针对车辆的标准化界面和模式
      3.Emoji:表情符号相关
      4.Fragment:基础概念
      5.Layout:基础概念
      6.Palette-Colors:调色板
      7.TV:Android TV 开发相关
      8.Wear:可穿戴设备(目前主要是手表)开发相关

    Jetpack之Navigation组件的体验感受

    简单学习了下Navigation组件,java和kotlin两种库都支持,可以按照需要选择集成即可 选择了那种库就需要apply对应的 gradle 插件。通过res/navigation/nav_config.xml 的配置 fragment or activity 针对fragment or activity 标签 配置对应的action 指定目标类绝对路劲后 , 然后通过 findNavController 对应的方法可以进行跳转或者返回,配置感很强,有点javaweb的配置的意思。总结了下这种方式的好处:

    • 解耦 通过资源文件配置 暴露id的形式 替代了之前java代码的强引用行为,非常有效的解耦
    • 减少了很多模版代码,正如解耦时一样 ,使用更加简单了,代码量更少,代码质量官方提供 有保证(ps:如果使用kotlin语言 代码量会更少)
    • 规定了基本使用姿势,提高了效率
      所以 建议大家快速熟悉并在自己的项目中 使用你认为合适的jetpack系列组件吧
    展开全文
  • Android Jetpack 使用入门

    2021-01-30 12:55:33
    为何使用 Android Jetpack? # 遵循最佳做法:Android Jetpack 组件采用最新的设计方法构建,具有向后兼容性,可以减少崩溃和内存泄露。 #消除样板代码:Android Jetpack 可以管理各种繁琐的 Activity(如后台任务...

    Jetpack 包含一系列 Android 库,它们都采用最佳做法并在 Android 应用中提供向后兼容性。
    本文来自:https://developer.android.com/jetpack/getting-started

    1. 为何使用 Android Jetpack?

     # 遵循最佳做法:Android Jetpack 组件采用最新的设计方法构建,具有向后兼容性,可以减少崩溃和内存泄露。

     # 消除样板代码:Android Jetpack 可以管理各种繁琐的 Activity(如后台任务、导航和生命周期管理),以便您可以专注于打造出色的应用。

     # 减少不一致:这些库可在各种 Android 版本和设备中以一致的方式运作,助您降低复杂性。

    下文介绍了如何开始使用 Jetpack 组件:

    2. 在应用中使用 Jetpack 库

    所有 Jetpack 组件都可在 Google Maven 代码库中找到,下面的第四点也详细的把Jetpack 库列出来,供参考。

    打开项目的 build.gradle 文件并添加 google() 代码库,如下所示:

    allprojects {
        repositories {
            google()
            jcenter()
        }
    }

    然后,您可以添加 Jetpack 组件(例如 LiveData 和 ViewModel 等架构组件),如下所示:

    dependencies {
        def lifecycle_version = "2.2.0"
        implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
        implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
        ...
    }

    许多 Jetpack 库都提供 Android KTX 扩展,如上面的 lifecycle-livedata-ktx 和 lifecycle-viewmodel-ktx 所示。KTX 扩展在基于 Java 的 API 基础上构建,充分利用了 Kotlin 特有的语言功能。

    如需了解新的 Jetpack 库版本,而Jetpack 库是与 Android 操作系统分开提供的,因此可以单独且更频繁地更新。

    3. 了解Jetpack 库版本

    版本字符串(如 1.0.1-beta02)包含三个代表主要、次要和问题修复级别的数字。预发布版本也有一个后缀,用于指定预发布阶段(Alpha 版、Beta 版、候选版本)和版本号(01、02 等)。

    每个版本都要经历三个预发布阶段各预发布阶段的标准如下:

    Alpha 版

    • Alpha 版功能稳定,但功能可能不完整。
    • 在版本处于 Alpha 版状态时,可以添加、移除或更改 API。

    Beta 版

    • Beta 版功能稳定,并且具有功能完整的 API Surface。
    • 它们可以投入实际使用,但可能包含错误。
    • Beta 版无法使用实验性编译器功能(例如 @UseExperimental)。
    • 其他库的依赖项必须为 Beta 版、RC 版或稳定版。不允许使用 Alpha 版依赖项。

    候选版本 (RC)

    • 候选版本是未来的稳定版。
    • 此版本可能包含在最后一刻提供的重要修复。
    • 此版本的 API Surface 无法更改。
    • 其他库的依赖项只能是 RC 版或稳定版。

    一个库可以同时具有多个版本。每个版本都具有不同的发布阶段。例如,虽然 androidx.activity 的稳定版可以是 1.0.0,但也可能还有 1.1.0-beta02 版本以及 2.0.0-alpha01 版本。

    所以我们可以知道:库的每个版本都要经历三个预发布阶段,才能成为稳定版本。

    基于 Kotlin 以及基于 Java 的 API 参考文档页面适用于所有 Jetpack 库。

    4. 利用 Jetpack

    Jetpack 库可以单独使用,也可以组合使用,以满足应用的不同需求。

    • WorkManager - 满足您的后台调度需求。
    • Room - 实现数据存储持久性。
    • Navigation - 管理应用导航流程。
    • CameraX - 满足相机应用需求。
    • 热门和常用的库排在前面:

      activity *访问基于 Activity 构建的可组合 API。
      appcompat *允许在平台旧版 API 上访问新 API(很多使用 Material Design)。
      camera *构建移动相机应用。
      compose *使用描述界面形状和数据依赖项的可组合函数,以编程方式定义界面。
      databinding *使用声明性格式将布局中的界面组件绑定到应用中的数据源。
      fragment *将您的应用细分为在一个 Activity 中托管的多个独立屏幕。
      hilt *扩展了 Dagger Hilt 的功能,以实现 androidx 库中某些类的依赖项注入。
      lifecycle *构建生命周期感知型组件,这些组件可以根据 Activity 或 Fragment 的当前生命周期状态调整行为。
      Material Design 组件*适用于 Android 的模块化、可自定义 Material Design 界面组件。
      navigation *构建和组织应用内界面,处理深层链接以及在屏幕之间导航。
      paging *在页面中加载数据,并在 RecyclerView 中呈现。
      room *创建、存储和管理由 SQLite 数据库支持的持久性数据。
      test *在 Android 中进行测试。
      work *调度和执行可延期且基于约束条件的后台任务。
      ads获取广告 ID(无论是否通过 Play 服务)。
      annotation公开元数据,帮助工具开发者和其他开发者了解您的应用代码。
      arch.core其他架构依赖项的帮助程序,包括可与 LiveData 配合使用的 JUnit 测试规则。
      asynclayoutinflater异步膨胀布局以避免界面出现卡顿。
      autofill扩展提示,从而提高自动填充的准确性。
      benchmark在 Android Studio 中准确评估代码的性能。
      biometric通过生物识别特征或设备凭据进行身份验证,以及执行加密操作。
      browser在用户的默认浏览器中显示网页。
      car为支持 Android 技术的汽车开发驾驶友好型应用。
      cardview用圆角和阴影实现 Material Design 卡片模式。
      collection降低现有和新的小型集合对内存的影响。
      compose.animation在 Jetpack Compose 应用中构建动画,丰富用户的体验。
      compose.compiler借助 Kotlin 编译器插件,转换 @Composable functions(可组合函数)并启用优化功能。
      compose.foundation使用现成可用的构建块编写 Jetpack Compose 应用,还可扩展 Foundation 以构建您自己的设计系统元素。
      compose.material使用现成可用的 Material Design 组件构建 Jetpack Compose UI。这是更高层级的 Compose 入口点,旨在提供与 www.material.io 上描述的组件一致的组件。
      compose.runtimeCompose 的编程模型和状态管理的基本构建块,以及 Compose 编译器插件针对的核心运行时。
      compose.ui与设备互动所需的 Compose UI 的基本组件,包括布局、绘图和输入。
      concurrent使用协程将任务移出主线程,并充分利用 ListenableFuture。
      constraintlayout使用相对定位灵活地确定微件的位置和大小。
      contentpager在后台线程中加载 ContentProvider 数据并进行分页。
      coordinatorlayout定位顶层应用微件,例如 AppBarLayout 和 FloatingActionButton。
      core针对最新的平台功能和 API 调整应用,同时还支持旧设备。
      cursoradapter向 ListView 微件提供光标数据。
      customview实现自定义视图。
      datastore以异步、一致的事务方式存储数据,克服了 SharedPreferences 的一些缺点
      documentfile查看文件文档。
      drawerlayout实现 Material Design 抽屉式导航栏微件。
      dynamicanimation使用基于物理特性的动画 API 制作流畅的动画。
      emoji在当前设备和旧版本设备上显示表情符号。
      enterprise创建企业专用应用。
      exifinterface读取和写入图片文件 EXIF 标记。
      games在您的应用中以原生方式使用 Android 游戏 SDK 来执行复杂的游戏任务,例如帧同步。
      gridlayout实现网格布局。
      heifwriter使用 Android 设备上可用的编解码器,以 HEIF 格式对图像或图像集进行编码。
      interpolator在旧版平台上使用动画插值器。
      jetifier一款独立工具,可将某个库中对已弃用支持库的依赖项迁移到等效的 AndroidX 依赖项。
      leanback使用适合 dpad 的微件和模板 Fragment 为 Android TV 设备编写应用。
      legacy此工件及其类已弃用。从 Android 8 开始,后台检查限制使此类无法再发挥作用。
      loader加载界面数据,这些数据不会在配置更改过程中丢失。
      localbroadcastmanager此工件及其类已弃用。请改用 LiveData 或响应式流。
      media与其他应用共享媒体内容和控件。已被 media2 取代。
      media2与其他应用共享媒体内容和控件。
      mediarouter利用通用界面实现在远程接收端设备上显示和播放媒体内容。
      multidex在搭载 Android 5 之前版本的设备上部署包含多个 dex 文件的应用。
      palette从图片中提取具有代表性的调色板。
      percentlayout此工件及其类已弃用。请改用 ConstraintLayout 和关联布局。
      preference无需与设备存储空间交互,也不需要管理界面,便能构建交互式设置画面。
      print通过您的应用打印照片、文档、其他图形及图片。
      recommendation将内容推送到 Android TV 启动器的主屏幕。
      recyclerview在您的界面中显示大量数据,同时最大限度减少内存用量。
      remotecallback创建一个封装容器,以便开发者更轻松地提供 PendingIntent。
      savedstate编写可插入组件,这些组件会在进程终止时保存界面状态,并在进程重启时恢复界面状态。
      security安全地管理密钥并对文件和 sharedpreferences 进行加密。
      sharetarget提供向后兼容性,可以将快捷方式用作直接共享目标。
      slice在应用外显示模板化界面元素。
      slidingpanelayout实现滑动窗格界面模式。
      startup实现一种在应用启动时初始化组件的简单、高效方法。
      sqlite使用本地 SQLite 数据库。如果可能,请改用 Room。
      swiperefreshlayout实现下拉刷新的界面模式。
      textclassifier识别文本中的对话、链接、选定内容和其他类似构造内容。
      tracing将跟踪事件写入系统跟踪缓冲区。
      transition使用开始和结束布局为界面中的动作添加动画效果。
      tvprovider提供 Android TV 频道。
      vectordrawable渲染矢量图形。
      versionedparcelable提供稳定且紧凑的二进制序列化格式,该格式可跨进程传递或安全保留。
      viewpager以可滑动的格式显示视图或 Fragment。如果可能,请改用 viewpager2。
      viewpager2以可滑动的格式显示视图或 Fragment。
      wear打造适用于 Wear OS by Google 谷歌智能手表的应用。
      webkit在 Android 5 及更高版本上使用新式 WebView API。
      window帮助支持不同的设备类型,例如可折叠设备。

    ⚠️ Jetpack 库在 androidx 命名空间中发布。如果项目目前使用 Android 支持库,需要把 项目迁移至AndroidX。

    如需要,请参考:如何把项目迁移到 AndroidX

    另外,如需详细了解如何使用 Jetpack,请查看以下页面:

    基于自己的理解进行封装架构示例:
    基于Android Jetpack 组件搭建 MVVM 架构 - MVVM-Project-Hilt

    (欢迎讨论~)

    展开全文
  • Jetpack All In Compose ?看各种Jetpack库在Compose中的使用

    万次阅读 多人点赞 2021-06-15 12:43:53
    幸好 Jetpack 中不少组件库已经与 Compose 进行了适配,开发者可以使用这些 Jetpack 库完成UI以外的功能。 Bloom 是一个 Compose 最佳实践的 Demo App,主要用来展示各种植物列表以及详细信息。 接下来以 Bloom 为...

    Jeptack Compose 主要目的是提高 UI 层的开发效率,但一个完整项目还少不了逻辑层、数据层的配合。幸好 Jetpack 中不少组件库已经与 Compose 进行了适配,开发者可以使用这些 Jetpack 库完成UI以外的功能。

    Bloom 是一个 Compose 最佳实践的 Demo App,主要用来展示各种植物列表以及详细信息。

    Screen Shot 2021-05-30 at 12.42.13 AM.png

    接下来以 Bloom 为例,看一下如何在 Compose 中使用 Jetpack 进行开发


    1. 整体架构:App Architecture


    在架构上,Bloom 完全基于 Jetpack + Compose 搭建

    Screen Shot 2021-05-30 at 9.47.36 PM.png

    从下往上依次用到的 Jetpack 组件如下:

    • Room: 作为数据源提供数据持久化能力
    • Paging: 分页加载能力。分页请求 Room 的数据并进行显示
    • Corouinte Flow:响应式能力。UI层通过 Flow 订阅 Paging 的数据变化
    • ViewModel:数据管理能力。ViewModel 管理 Flow 类型的数据供 UI 层订阅
    • Compose:UI 层完全使用 Compose 实现
    • Hilt:依赖注入能力。ViewModel 等依赖 Hilt 来构建

    Jetpack MVVM 指导我们将 UI层、逻辑层、数据层进行了很好地解耦。上图除了 UI 层的 Compose 以外,与一个常规的 Jetpack MVVM 项目并无不同。

    接下来通过代码,看看 Compose 如何配合各 Jetpack 完成 HomeScreenPlantDetailScreen 的实现。


    2. 列表页:HomeScreen


    HomeScreen 在布局上主要由三部分组成,最上面的搜索框,中间的轮播图,以及下边的的列表

    image.png

    ViewModel + Compose

    我们希望 Composable 只负责UI,状态管理放到 ViewModel 中。 HomeScreen 作为入口的 Composable 一般在 Activity 或者 Fragment 中调用。

    viewmodel-compose 可以方便地从当前 ViewModelStore 中获取 ViewModel:
    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha04"

    @Composable
    fun HomeScreen() {
    
        val homeViewModel = viewModel<HomeViewModel>() 
        
        //...
    
    }
    

    Stateless Composable

    持有 ViewModel 的 Composalbe 相当于一个 “Statful Composalbe” ,这样的 ViewModel 很难复用和单测,而且携带 ViewModel 的 Composable 也无法在 IDE 中预览。 因此,我们更欢迎 Composable 是一个 “Stateless Composable”

    创建 StatelessComposable 的常见做法是将 ViewModel 上提,ViewModel 的创建委托给父级,仅作为参数传入,这可以使得 Composalbe 专注 UI

    @Composable
    fun HomeScreen(
        homeViewModel = viewModel<HomeViewModel>() 
    ) {
        
        //...
    
    }
    

    当然,也可以直接将 State 作为参数传入,可以进一步摆脱对 ViewModel 具体类型的依赖。

    接下来看一下 HomeViewModel 的实现,以及其内部 State 的定义


    3. HomeViewModel


    HomeViewModel 是一个标准的 Jetpack ViewModel 子类, 可以在ConfigurationChanged时保持数据。

    @HiltViewModel
    class HomeViewModel @Inject constructor(
        private val plantsRepository: PlantsRepository
    ) : ViewModel() {
    
    
        private val _uiState = MutableStateFlow(HomeUiState(loading = true))
        val uiState: StateFlow<HomeUiState> = _uiState
    
        val pagedPlants: Flow<PagingData<Plant>> = plantsRepository.plants
    
        init {
    
            viewModelScope.launch {
                val collections = plantsRepository.getCollections()
                _uiState.value = HomeUiState(plantCollections = collections)
            }
        }
    }
    
    

    添加了 @AndroidEntryPoint 的 Activity 或者 Fragment ,可以使用 Hilt 为 Composalbe 创建 ViewModel。 Hilt 可以帮助 ViewModel 注入 @Inject 声明的依赖。例如本例中使用的 PlantsRepository

    pagedPlants 通过 Paging 向 Composable 提供分页加载的列表数据,数据源来自 Room 。

    分页列表以外的数据在 HomeUiState 中集中管理,包括轮播图中所需的植物集合以及页面加载状态等信息:

    data class HomeUiState(
        val plantCollections: List<Collection<Plant>> = emptyList(),
        val loading: Boolean = false,
        val refreshError: Boolean = false,
        val carouselState: CollectionsCarouselState
            = CollectionsCarouselState(emptyList()) //轮播图状态,后文介绍
    )
    

    HomeScreen 中通过 collectAsState() 将 Flow 转换为 Composalbe 可订阅的 State:

    @Composable
    fun HomeScreen(
        homeViewModel = viewModel<HomeViewModel>() 
    ) {
        
        val uiState by homeViewModel.uiState.collectAsState()
        
        if (uiState.loading) {
            //...
        } else {
            //...
        }
    
    }
    

    LiveData + Compose

    此处的 Flow 也可以替换成 LiveData

    livedata-compose 将 LiveData 转换为 Composable 可订阅的 state :
    implementation "androidx.compose.runtime:runtime-livedata:$compose_version"

    @Composable
    fun HomeScreen(
        homeViewModel = viewModel<HomeViewModel>() 
    ) {
        
        val uiState by homeViewModel.uiState.observeAsState() //uiState is a LiveData
        
        //...
    
    }
    

    此外,还有 rxjava-compose 可供使用,功能类似。


    4. 分页列表:PlantList


    PlantList 分页加载并显示植物列表。

    @Composable
    fun PlantList(plants: Flow<PagingData<Plant>>) {
        val pagedPlantItems = plants.collectAsLazyPagingItems()
    
        LazyColumn {
            if (pagedPlantItems.loadState.refresh == LoadState.Loading) {
                item { LoadingIndicator() }
            }
    
            itemsIndexed(pagedPlantItems) { index, plant ->
                if (plant != null) {
                    PlantItem(plant)
                } else {
                    PlantPlaceholder()
                }
    
            }
    
            if (pagedPlantItems.loadState.append == LoadState.Loading) {
                item { LoadingIndicator() }
            }
        }
    }
    
    

    Paging + Compose

    paging-compose 提供了 pagging 的分页数据 LazyPagingItems:
    implementation "androidx.paging:paging-compose:1.0.0-alpha09"

    注意此处的 itemsIndexed 来自paging-compoee,如果用错了,可能无法loadMore

    public fun <T : Any> LazyListScope.itemsIndexed(
        lazyPagingItems: LazyPagingItems<T>,
        itemContent: @Composable LazyItemScope.(index: Int, value: T?) -> Unit
    ) {
        items(lazyPagingItems.itemCount) { index ->
            itemContent(index, lazyPagingItems.getAsState(index).value)
        }
    }
    

    itemsIndexed 接受 LazyPagingItems 参数, LazyPagingItems#getAsState 中从 PagingDataDiffer 中获取数据,当 index 处于列表尾部时,触发 loadMore 请求,实现分页加载。


    5. 轮播图:CollectionsCarousel


    CollectionsCarousel 是显示轮播图的 Composable。

    在下面页面中都有对轮播图的使用,因此我们要求 CollectionsCarousel 具有可复用性。

    Screen Shot 2021-05-31 at 12.15.27 AM.png

    Reusable Composable

    对于有复用性要求的 Composable,我们需要特别注意:可复用组件不应该通过 ViewModel 管理 State。 因为 ViewModel 在 Scope 内是共享的,但是在同一 Scope 内复用的 Composable 需要独享其 State 实例。

    因此 CollectionsCarousel 不能使用 ViewModel 管理 State,必须通过参数传入状态以及事件回调。

    @Composable
    fun CollectionsCarousel(
        // State in,
        // Events out
    ) {
        // ...
    }
    

    参数传递的方式使得 CollectionsCarousel 将自己的状态委托给了父级 Composable。

    CollectionsCarouselState

    既然委托到了父级, 为了方便父级的使用,可以对 State 进行一定封装,被封装后的 State 与 Composable 配套使用。这在 Compose 中也是常见的做法,比如 LazyColumnLazyListState ,或者 ScallfoldScaffoldState

    对于 CollectionsCarousel 我们有这样一个需求:点击某一 Item 时,轮播图的布局会展开

    Screen Shot 2021-05-31 at 12.28.45 AM.png

    由于不能使用 ViewModel, 所以使用常规 Class 定义 CollectionsCarouselState 并实现 onCollectionClick 等相关逻辑

    data class PlantCollection(
        val name: String,
        @IdRes val asset: Int,
        val plants: List<Plant>
    )
    
    class CollectionsCarouselState(
        private val collections: List<PlantCollection>
    ) {
        private var selectedIndex: Int? by mutableStateOf(null)
            
        val isExpended: Boolean
            get() = selectedIndex != null
    
        privat var plants by mutableStateOf(emptyList<Plant>())
            
        val selectPlant by mutableStateOf(null)
            private set
    
        //...
    
        fun onCollectionClick(index: Int) {
            if (index >= collections.size || index < 0) return
            if (index == selectedIndex) {
                selectedIndex = null
            } else {
                plants = collections[index].plants
                selectedIndex = index
            }
        }
    }
    

    然后将其定义为 CollectionsCarousel 的参数

    @Composable
    fun CollectionsCarousel(
        carouselState: CollectionsCarouselState,
        onPlantClick: (Plant) -> Unit
    ) {
        // ...
    }
    

    为了进一步方便父级调用,可以提供
    rememberCollectionsCarouselState()方法, 效果相当于
    remember { CollectionsCarouselState() }

    最后,父Composalbe 访问 CollectionsCarouselState 时,可以将它放置父级的 ViewModel 中保存,以支持 ConfigurationChanged 。例如本例中会放到 HomeUiState 中管理。


    6. 详情页:PlantDetailScreen & PlantViewModel


    PlantDetailScreen 中除了复用 CollectionsCarousel 以外,大部分都是常规布局,比较简单。

    重点说明一下 PlantViewModel, 通过 idPlantsRepository 中获取详情信息。

    class PlantViewModel @Inject constructor(
        plantsRepository: PlantsRepository,
        id: String
    ) : ViewModel() {
    
        val plantDetails: Flow<Plant> = plantsRepository.getPlantDetails(id)
        
    }
    

    此处的 id 该如何传入呢?

    一个做法是借助 ViewModelProvider.Factory 构造 ViewModel 并传入 id

    @Composable
    fun PlantDetailScreen(id: String) {
        
        val plantViewModel : PlantViewModel = viewModel(id, remember {
            object : ViewModelProvider.Factory {
                override fun <T : ViewModel> create(modelClass: Class<T>): T {
                    return PlantViewModel(PlantRepository, id)
                }
            }
        })
    }
    
    

    这种构造方式成本较高,而且按照前文介绍的,如果想保证 PlantDetailScreen 的可复用性和可测试性,最好将 ViewModel 的创建委托到父级。

    除了委托到父级创建,我们还可以配合 NavigationHilt 更合理的创建 PlantViewModel,这将在后文中介绍。


    7. 页面跳转:Navigation


    HomeScreen 列表中点击某 Plant 后跳转 PlantDetailScreen

    实现多个页面之间跳转,其中一个常见思路是为 Screen 包装一个 Framgent,然后借助 Navigation 实现对 Fragment 的跳转

    @AndroidEntryPoint
    class HomeFragment : Fragment() {
        override fun onCreateView(inflater: LayoutInflater, 
            container: ViewGroup?,  savedInstanceState: Bundle?
        ) = ComposeView(requireContext()).apply {
            setContent {
                HomeScreen(...)
            }
        }
    }
    

    Navigation 将回退栈中的节点抽象成一个 Destination , 所以这个 Destination 不一定非要用 Fragment 实现, 没有 Fragment 也可以实现 Composable 级别的页面跳转。

    Navigation + Compose

    navigation-compose 可以将 Composalbe 作为 Destination 在 Navigation 中使用
    implementation "androidx.navigation:navigation-compose:$version"

    因此,我们摆脱 Framgent 实现页面跳转:

    @AndroidEntryPoint
    class BloomAcivity : ComponentActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
            setContent {
    
                val navController = rememberNavController()
    
                Scaffold(
                    bottomBar = {/*...*/ }
                ) {
                    NavHost(navController = navController, startDestination = "home") {
                        composable(route = "home") {
                            HomeScreen(...) { plant ->
                                navController.navigate("plant/${plant.id}")
                            }
                        }
                        composable(
                            route = "plant/{id}",
                            arguments = listOf(navArgument("id") { type = NavType.IntType })
                        ) {
                            PlantDetailScreen(...)
                        }
                    }
                }
    
            }
        }
    }
    

    Navigaion 的使用依靠两个东西: NavControllerNavHost

    • NavController 保存了当前 Navigation 的 BackStack 信息,因此是一个携带状态的对象,需要像 CollectionsCarouselState 那样,跨越 NavHost 的 Scope 之外创建。

    • NavHostNavGraph 的容器, 将 NavController 作为参数传入。 NavGraph 中的Destinations(各Composable)将 NavController 作为 SSOT(Single Source Of Truth) 监听其变化。

    NavGraph

    不同于传统的 XML 方式, navigation-compose 则使用 Kotlin DSL 定义 NavGraph:

    comosable(route = “$id”) {
        //...
    }
    

    route 设置 Destination 的索引 id。 HomeScreen 使用 “home” 作为唯一id; 而 PlantDetailScreen 使用 “plant/{id}” 作为id。 其中 {id}中的 id 来自前一页面跳转时携带的 URI 中的参数 key。 本例中就是 plant.id:

    HomeScreen(...) { plant ->
        navController.navigate("plant/${plant.id}")
    }
    
    composable(
        route = "plant/{id}",
        arguments = listOf(navArgument("id") { type = NavType.IntType })
    ) { //it: NavBackStackEntry 
        val id = it.arguments?.getString("id") ?: ""
        ...
    }
    

    navArgument可以将 URI 中的参数转化为 Destination 的 arguments , 并通过 NavBackStackEntry 获取

    如上所述,我们可以利用 Navigation 进行 Screen 之间的跳转并携带一些基本参数。此外, Navigation 帮助我们管理回退栈,大大降低了开发成本。

    Hilt + Compose

    前文中介绍过,为了保证 Screen 的独立复用,我们可以将 ViewModel 创建委托到父级 Composable。 那么在 Navigation 的 NavHost 中我们该如何创建 ViewModel 呢?

    hilt-navigation-compose 允许我们在 Navigation 中使用 Hilt 构建 ViewModel:
    implementation “androidx.hilt:hilt-navigation-compose:$version”

    NavHost(navController = navController, 
            startDestination = "home",
            route = "root" // 此处为 NavGraph 设置 id。
            ) {
          composable(route = "home") {
                val homeViewModel: HomeViewModel = hiltNavGraphViewModel()
                val uiState by homeViewModel.uiState.collectAsState()
                val plantList = homeViewModel.pagedPlants
                
                HomeScreen(uiState = uiState) { plant ->
                       navController.navigate("plant/${plant.id}")
                }
            }
            
            composable(
                route = "plant/{id}",
                arguments = listOf(navArgument("id") { type = NavType.IntType })
            ) {
                val plantViewModel: PlantViewModel = hiltNavGraphViewModel()
                val plant: Plant by plantViewModel.plantDetails.collectAsState(Plant(0))
                
                PlantDetailScreen(plant = plant)
            }
    }
    
    

    Navigation 中,每个 Destination 都是一个 ViewModelStore, 因此 ViewModel 的 Scope 可以限制在 Destination 内部而不用放大到整个 Activity,更加合理。而且,当 Destination 从 BackStack 弹出时, 对应的 Screen 从视图树上卸载,同时 Scope 内的 ViewModel 被清空,避免泄露。

    • hiltNavGraphViewModel() : 可以获取 Destination Scope 的 ViewModel,并使用 Hilt 构建。

    • hiltNavGraphViewModel("root") : 指定 NavHost 的 routeId,则可以在 NavGraph Scope 内共享ViewModel

    Screen 的 ViewModel 被代理到 NavHost 中进行, 不持有 ViewModel 的 Screen 具有良好的可测试性。

    再看一看 PlantViewModel

    @HiltViewModel
    class PlantViewModel @Inject constructor(
        plantsRepository: PlantsRepository,
        savedStateHandle: SavedStateHandle
    ) : ViewModel() {
    
        val plantDetails: Flow<Plant> = plantsRepository.getPlantDetails(
            savedStateHandle.get<Int>("id")!!
        )
    }
    
    

    SavedStateHandle 实际上是一个键值对的 map。 当使用 Hilt 在构建 ViewModel 时,此 map 会被自动填充 NavBackStackEntry 中的 arguments,之后被参数注入 ViewModel。 此后在 ViewModel 内部可以通过 get(xxx) 获取键值。

    至此, PlantViewModel 通过 Hilt 完成了创建,相比与之前的 ViewModelProvider.Factory 简单得多。


    8. Recap:


    一句话总结各 Jetpack 库为 Compose 带来的能力:

    • viewmodel-compose 可以从当前 ViewModelStore 中获取 ViewModel
    • livedate-compose 将 LiveData 转换为 Composable 可订阅的 state 。
    • paging-compose 提供了 pagging 的分页数据 LazyPagingItems
    • navigation-compose 可以将 Composalbe 作为 Destination 在 Navigation 中使用
    • hilt-navigation-compose 允许我们在 Navigation 中使用 Hilt 构建 ViewModel

    此外,还有几点设计规范需要遵守:

    • 将 Composable 的 ViewModel 上提,有利于保持其可复用性和可测试性
    • 当 Composable 在同一 Scope 内复用时,避免使用 ViewModel 管理 State
    展开全文
  • Jetpack Compose 架构比较:MVP & MVVM & MVI

    千次阅读 多人点赞 2021-06-01 00:34:56
    自从 Android 用 ViewModel 命名了某 Jetpack 组件后,在很多人心里,Jetpack 似乎就与 MVVM 画上了等号。这确实客观推动了 MVVM 的普及,但是 Jetpack 的 ViewModel 并非只能用在 MVVM 中(比如如后文介绍的 MVI ...
  • JetPack Compose 正式版已经发布好几个月了,在这段时间里,除了业务相关需求之外,我也开始了 Compose 在实际项目中的落地实验,因为一旦要接入当前项目,那么遇到的问题其实远远大于新创建一个项目所需要的问题。...
  • 一、Jetpack Compose的概念 Jetpack Compose是用于构建原生Android UI的现代工具包。 Jetpack Compose使用更少的代码,强大的工具和直观的Kotlin API,简化并加速了Android上的UI开发。这是Android Developers 官网...
  • 文章目录 Kotlin结合Jetpack构建MVVM Jetpack官方推荐架构MVVMAPI接口工程结构添加依赖搭建项目1. 定义User实体类2. 定义Dao类3. 定义DataBase类4. 定义API接口5. 定义Retrofit访问网络6. 定义Application类7. 定义...
  • Jetpack-Compose

    2021-03-08 18:26:52
    Jetpack Compose去年写了一些列表布局就丢下了,以前一个小兄弟说“什么都不如官网来的直接”这是他的博客打算用Compose写玩安卓大家可以去好好学习一波,接下来我们跟着官网走,原理案例基于官网,写的效果高于官网。...
  • jetpack compose 框架

    2021-09-02 22:55:21
    Compose Rows,Columns,Box Row 和Column是用来设置子项的位置。在水平方向(horizontal)和竖直方向(vertical)设置有两个参数,分别是排列(Arrangement)和布局(Alignment) Arrangement ...
  • Android Jetpack组件(九)DataStore

    千次阅读 2021-11-04 00:23:35
    Jetpack DataStore 是一种数据存储解决方案,允许您使用协议缓冲区存储键值对或类型化对象。DataStore 使用 Kotlin 协程和 Flow 以异步、一致的事务方式存储数据。
  • JetPack-Compose - Flutter 动态UI?

    千次阅读 2021-03-18 14:43:50
    手势滑动,手势缩放等 Android自定义-任意区域可点击的折线图 Android自定义-手势缩放折线图 Android自定义-手势滑动缩放渐变填充曲线折线图表 Android-自定义可伸展的ViewGroup Android自定义-曲线渐变填充 Jetpack-...
  • 文章目录Android开发架构MVCMVPMVVMUI驱动 vs 数据驱动MVVM的具体实现方案Jetpack MVVMMVVM封装总结参考 Android开发架构 如果开发过程中大家各自为战,没有统一规范,久而久之,项目代码会变得混乱且后续难以维护。...
  • Jetpack之Room的使用

    千次阅读 2021-12-16 11:35:01
    } ​ viewmodel里面的代码对于很多不熟悉jetpack同学来说看起来是很蒙圈的,但是无关紧要,我们只要简单分析plants这个方法就可以,知道他是用来做什么的。至于相关的flow或者其他的知识点,可以先忽略。 plants是...
  • Jetpack架构组件库-Jetpack入门介绍

    千次阅读 2021-11-18 01:09:41
    一,初认Jetpack Jetpack 是 Android 软件组件的集合,使您可以更轻松地开发出色的 Android 应用。这些组件可帮助您遵循最佳做法、让您摆脱编写样板代码的工作并简化复杂任务,google官方一种Android架构的一种,...
  • Android Jetpack之简介为何使用 Android JetpackJetpack 是一个由多个库组成的套件 为何使用 Android Jetpack? 遵循最佳做法 Android Jetpack组件采用最新的设计方法构建,具有向后兼容性,可以减少崩溃和内存...
  • Jetpack 系列之AppSearch

    千次阅读 2021-06-18 23:15:14
    前言 在今年的Google I/O 大会上,Jetpack库新增了三个组件(刚发布Alpha版本),分别是MarcrobenChmark、AppSearch和Google Shortc
  • Jetpack常用组件使用

    2020-12-22 00:52:00
    Jetpack是Google官方推出的一套Android库,它帮助开发者更方便、更快速的开发出稳健性极佳的软件,简化开发流程与提高效率。Jetpack库常用的如下几个组件,它们都可以单独使用或者组合使用:组建名称介绍Android ...
  • Jetpack Compose助我快速打造电影App

    千次阅读 热门讨论 2021-03-21 17:41:30
    现如今Jetpack框架愈发火热,便萌生了完全使用Jetpack框架重新开发的想法。加上Compose Beta版的正式公开,这个时机再适合不过了。 整体上采用Compose去实现UI。数据请求则依赖Coroutines调用Retrofit接口,最后...
  • Jetpack:ViewModel使用指南,实现原理详细解析! class SimpleViewModel(private val simpleStudentDao: SimpleStudentDao) : ViewModel() { fun insertStudent(studentEntity: SimpleStudentEntity) { ...
  • 可能是最全的kotlin协程+Jetpack入门教程前言一、协程是什么?二、Jetpack是什么?1.引入库2.读入数据总结二级目录三级目录 文章目录前言一、协程是什么?二、Jetpack是什么?1.引入库2.读入数据总结二级目录三级...
  • Android Jetpack Compose 超快速上手指南

    多人点赞 2021-04-20 17:50:19
    Jetpack Compose对于没有接触过声明式UI的小伙伴可能会学习曲线有点陡峭,对于已经能熟练开发Flutter应用的小伙白来说几乎没有难度。(Compose就是参考flutter的模式开发的,代码中还可以看到Flutter的相关注释) 这...
  • Jetpack Compose初体验

    2021-04-29 16:10:35
    Jetpack Compose 是用来构建Android界面的新款工具包,前段时间beta版本刚发布的时候就已经心痒难耐的想要尝试了,最近轻松点了赶紧尝试一波。 以前我们都是通过xml布局,通过findViewById()找到控件之后手动给控件...
  • 在今年的Google/IO大会上,亮相了一个全新的 Android 原生 UI 开发框架-Jetpack Compose, 与苹果的SwiftIUI一样,Jetpack Compose是一个声明式的UI框架,随着了今年安卓和苹果两大移动平台相继推出自己的UI开发框架...
  • Android Kotlin+Jetpack+MVVM

    2021-11-17 14:50:51
    最近学习了Kotlin,学习了Jetpack,发现是真香,所以就手写了一个MVVM的框架,可以方便开发。Kotlin+Jetpack+MVVM之GitHub地址,帮我点个Star,赠人玫瑰,手留余香,谢谢。 先讲一下思路 ViewModel ViewModel可以...
  • Jetpack学习之DataBinding(一)

    千次阅读 2021-11-13 17:47:14
    文章目录一、 JetPack是什么 一、 JetPack是什么 百度下都是差不多的说明,我这里结合自己的理解记录下,可能理解的也不是很全面正确。 JetPack是一套工具库,包含4类组件、分别是架构、界面、行为、基础。(这里不...
  • 二、Jetpack Compose 介绍 Jetpack Compose 是一个用于构建原生Android UI 的现代化工具包,它基于声明式的编程模型,因此你可以简单地描述UI的外观,而Compose则负责其余的工作-当状态发生改变时,你的UI将自动...
  • Petterp的博客地址: https://juejin.cn/user/3491704662136541/posts / 引言 / JetPack Compose 正式版已经发布好几个月了,在这段时间里,除了业务相关需求之外,我也开始了 Compose 在实际项目中的落地实验,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 27,914
精华内容 11,165
关键字:

jetpack

友情链接: 代码.zip