MVVM-Best-Practice读源码笔记

393 阅读4分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

本文是对下面链接中项目的注释,

正在学习项目源码 并且加入了自己的理解

请克隆项目,了解项目背景与软件功能后.结合源码查看本笔记

github.com/KunMinX/Jet…

目录结构

app.src.main

 │  AndroidManifest.xml // 软件应用清单文件包含了应用的配置信息,组件,需要的权限
 │
 ├─assets //存储静态文件的文件夹
 │      bensound-sunny.mp3
 │      summary.html
 │
 ├─java
 │  └─com.kunminx.puremusic
 │     │  App.java // Application对象 用于存放初始化view初始化前要执行的代码
 │     │  MainActivity.java // 主要Activity对象 在这里初始化view
 │     │
 │     ├─data
 │     │  ├─api // 存放http请求函数
 │     │  │      AccountService.java // 定义了请求函数接口
 │     │  │      APIs.java // 存放接口地址
 │     │  │
 │     │  ├─bean // 存放定义的bean对象
 │     │  │      DownloadFile.java
 │     │  │      LibraryInfo.java
 │     │  │      TestAlbum.java
 │     │  │      User.java
 │     │  │
 │     │  ├─config // 设置文件的路径
 │     │  │      Configs.java
 │     │  │
 │     │  └─repository
 │     │          DataRepository.java // 数据仓库,包含网络请求,上传下载,认证相关的函数, 任务随页面声明周期叫停, 下载进度使用使用 DataResult通知页面而非 LiveData
 │     │          ILocalSource.java // 地址保存
 │     │          IRemoteSource.java // 地址保存
 │     │
 │     ├─domain
 │     │  ├─message
 │     │  │      DrawerCoordinateManager.java // 抽屉侧滑控制器 
 │     │  │      SharedViewModel.java // UnPeekLiveData 配合 SharedViewModel 发送跨页面通知样例
 │     │  │
 │     │  ├─request
 │     │  │      AccountRequest.java
 │     │  │      DownloadRequest.java
 │     │  │      InfoRequest.java
 │     │  │      MusicRequest.java
 │     │  │
 │     │  └─usecase
 │     │          CanBeStoppedUseCase.java // UseCase 例,实现LifeCycle接口,用于可叫停业务
 │     │          DownloadUseCase.java
 │     │
 │     ├─player
 │     │  │  PlayerManager.java // 播放管理器
 │     │  │
 │     │  ├─helper
 │     │  │      PlayerCallHelper.java // 在来电时自动协调和暂停音乐播放
 │     │  │      PlayerFileNameGenerator.java
 │     │  │
 │     │  └─notification
 │     │          PlayerReceiver.java
 │     │          PlayerService.java
 │     │
 │     └─ui
 │         ├─base
 │         │  └─binding_adapter // BindingAdapter实现了数据绑定,某一个绑定值(被观察者)改变后,绑定一个方法,然后执行相应逻辑
 │         │          CommonBindingAdapter.java
 │         │          DrawerBindingAdapter.java
 │         │          IconBindingAdapter.java
 │         │          TabPageBindingAdapter.java
 │         │          WebViewBindingAdapter.java
 │         │
 │         ├─page // 每个页面都要单独配备一个 state-ViewModel,职责仅限于 "状态托管和恢复",
 │         │  │  DrawerFragment.java
 │         │  │  LoginFragment.java
 │         │  │  MainFragment.java
 │         │  │  PlayerFragment.java
 │         │  │  SearchFragment.java
 │         │  │
 │         │  ├─adapter
 │         │  │      DiffUtils.java
 │         │  │      DrawerAdapter.java
 │         │  │      PlaylistAdapter.java
 │         │  │
 │         │  └─helper
 │         │          DefaultInterface.java
 │         │
 │         ├─state // 存放各个页面的ViewModel
 │         │      DrawerViewModel.java
 │         │      LoginViewModel.java
 │         │      MainActivityViewModel.java
 │         │      MainViewModel.java
 │         │      PlayerViewModel.java
 │         │      SearchViewModel.java
 │         │
 │         └─view
 │                 PlayerSlideListener.java
 │                 PlayPauseDrawable.java
 │                 PlayPauseView.java
 │
 └─res
     ├─anim  // 定义应用中的动画表现
     │      内容略
     │
     ├─drawable // 定义应用从存放的图标
     │      内容略
     │      有多个文件夹适配不同清晰度资源文件夹略
     │      
     ├─layout // 应用界面定义
     │      activity_main.xml
     │      adapter_library.xml
     │      adapter_play_item.xml
     │      fragment_drawer.xml
     │      fragment_login.xml
     │      fragment_main.xml
     │      fragment_player.xml
     │      fragment_search.xml
     │      notify_player_big.xml
     │      notify_player_small.xml
     │
     ├─layout-land // 横屏的排列结构
     │      activity_main.xml
     │      fragment_main.xml
     │      fragment_player.xml
     │
     ├─navigation // 界面跳转定义
     │      nav_drawer.xml
     │      nav_main.xml
     │      nav_slide.xml
     │
     ├─values // 值
     │      attrs.xml
     │      colors.xml
     │      dimen.xml
     │      strings.xml
     │      styles.xml
     │
     └─xml
             network_security_config.xml

代码结构

重点了解

事件绑定代码位置

layout xml文件 -DataBinding-> Fragment.ClickProxy 通过在Fragment文件的ClickProxy集中编写点击事件 避免在xml文件中大量引入对象,同时避免了重复在Fragment中对所有需要点击事件的元素执行 找到视图对象 绑定对象 绑定点击事件的操作

应用分别由哪几部分组成

应用有一个带有ViewPager与搜索框的主界面

搭配上侧边栏与地下的播放器

点击搜索栏有登录页 ,搜索页

应用有哪几个页面/组件

主要应用:

activity_main

  • 堆砌fragment
  • 使用DataBinding导入自己ViewModel与ListenerHandler
  • 这里用于编写程序的主体框架,用于组合fragment. 不放置业务与复杂的视图代码

fragment_main

  • 设计主页面
  • 使用DataBinding导入自己ViewModel, ClickProxy与Adapter

fragment_player

  • 设计组件界面
  • 使用DataBinding导入自己的ViewModel, ClickProxy与ListenerHandler

navigation的跳转 相关文件与定义

在activity_main.xml中使用@navigation/引入nav_main, nav_slide, nav_drawer三个资源

文件中action(动作) 定义了动画效果

layout xml文件命名方案 与文件组织

放在res.layout文件夹

前缀为文件类型如 activity, fragment adapter notify

adapter: RecyclerView内条目的展示样式

notify: 通知条样式

java 中对应fragment文件组织方式

在ui.page下, 一个类对应一个fragment

java 中对应fragment命名方式

大驼峰命名,以Fragment结尾

java 中对应ViewModel文件组织方式

所有activity与fragment分别对应一个ViewModel

java 中对应ViewModel命名方式

大驼峰

fragment以ViewModel结尾

activity以ActivityViewModel结尾

BindingAdapter是什么

编写带 @BindingAdapter 注解的方法可以允许程序员自行设定xml文件中的属性值, 设置对应属性值的对象会调用方法提供的自定义逻辑.

BindingAdapter的用法

在layout文件中, 如程序段中第二行

 <TabLayout
     android:id="@+id/tab_layout"
     initTabAndPage="@{vm.initTabAndPage}"
     android:layout_width="match_parent"
     android:layout_height="48dp"
     ...
     >
     ...
 </TabLayout>

下面是适配器的编写方法

     @BindingAdapter(value = {"initTabAndPage"}, requireAll = false)
     public static void initTabAndPage(TabLayout tabLayout, boolean initTabAndPage) {
         int count = tabLayout.getTabCount();
         String[] title = new String[count];
         ...
     }

这种写法是单向绑定, 当TabLayout初始化时, 会执行一次initTabAndPage方法. 第一个值是视图对象, 第二个值是layout xml文件中给传入的对象.

当值改变时, 会再执行一次该方法

注解中value后可以填写多个属性, 设置requireAll为假. 对象设置任意一个属性值, 都会调用此方法

Request类一般承担什么职责

Request 通常按业务划分 一个项目中通常存在多个 Request 类, 每个页面配备的 state-ViewModel 实例可根据业务需要持有多个不同的 Request 实例。

request 的职责仅限于 "业务逻辑处理" 和 "Event 分发",不建议在此处理 UI 逻辑, UI 逻辑只适合在 Activity/Fragment 等视图控制器中完成,是 “数据驱动” 的一部分,