背景

之前无意中关注了无码科技的公众号,由此知道了他们推出的第一个产品Readhub,地址为readhub.me/,主要提供互联网最新发生的新鲜事,关注了一段时间感觉内容质量还不错,能够帮我们筛选掉一定的垃圾信息。但是它目前只能在浏览器和微信公众号里面查看,又加上自己一直想体验一下谷歌推出的架构组件,所以在简单分析了一下Readhub Web端的接口之后开发了一个Android版本的客户端。GitHub地址user-gold-cdn.xitu.io/2018/1/10/1…

效果图

具体实现

App架构比较简单:一个主Activity+三个Fragment。目前Readhub的信息只有三个分类,分别为热门话题、科技动态和开发者资讯。其中科技动态和开发者资讯数据模型相同,只是调用的就接口不同,可以在很大程度上进行复用。

项目目录划分如下,

和Android官方文档建议的架构基本是一致。

目前Repository中只是单纯的从网络请求数据,没有做本地缓存,代码如下

class DataRepository private constructor(context: Context) {private val SERVER_ADDRESS = "https://api.readhub.me/"private val httpService: Apiinit {val builder = Retrofit.Builder()builder.baseUrl(SERVER_ADDRESS)builder.client(DefaultOkHttpClient.getOkHttpClient(context))builder.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))builder.addCallAdapterFactory(RxJava2CallAdapterFactory.create())val retrofit = builder.build()httpService = retrofit.create(Api::class.java)}/*** 热门话题*/fun getTopics(lastCursor: Long?, pageSize: Int): Observable<PageResult<Topic>> {return httpService.getTopics(lastCursor, pageSize)}/*** 科技动态*/fun getTechNews(lastCursor: Long?, pageSize: Int): Observable<PageResult<News>> {return httpService.getTechNews(lastCursor, pageSize)}/*** 开发者资讯*/fun getDevNews(lastCursor: Long?, pageSize: Int): Observable<PageResult<News>> {return httpService.getDevNews(lastCursor, pageSize)}companion object {private var instance: DataRepository? = nullfun getInstance(context: Context): DataRepository {if (instance == null) {synchronized(DataRepository::class.java) {if (instance == null) {instance = DataRepository(context)}}}return instance!!}}
}复制代码

ViewModel目前有两个:TopicViewModelNewsViewModelNewsViewModel用于为科技动态和开发者资讯提供数据,以NewsViewModel为例,

class NewsViewModel(private val newsType: NewsType, private val pageSize:Int) : ViewModel() {private val liveData: MutableLiveData<List<News>> = MutableLiveData()private var isFirstPage = trueprivate var lastCursor: Long = 0Lprivate val newsList = ArrayList<News>()fun getLiveData(): LiveData<List<News>> {lastCursor = System.currentTimeMillis()fetchData()return liveData}fun refresh() {isFirstPage = truelastCursor = System.currentTimeMillis()fetchData()}fun loadMore() {isFirstPage = falsefetchData()}private fun fetchData() {val observable = if (newsType == NewsType.TechNews) {DataRepository.getInstance(MyApplication.instance).getTechNews(lastCursor, pageSize)} else {DataRepository.getInstance(MyApplication.instance).getDevNews(lastCursor, pageSize)}observable.compose(SchedulerTransformer()).subscribe({ data ->if (isFirstPage) {newsList.clear()}newsList.addAll(newsList.size, data.data?.toList()!!)liveData.value = newsListlastCursor = data.data?.last()?.publishDate!!.toDate("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")?.time!!}, {liveData.value = null})}
}复制代码

NewsViewModel有两个构造参数newsType为一个枚举类型,用于区分是科技动态还是开发者资讯,另一个参数pageSize用于设置分页大小。由于NewsViewModel含有构造参数,所以我们需要自定义它的创建方式,方式为实现ViewProvider.Factory接口

class NewsViewModelFactory(private val newsType: NewsType, private val pageSize: Int) : ViewModelProvider.Factory {override fun <T : ViewModel?> create(modelClass: Class<T>): T {if (modelClass.isAssignableFrom(NewsViewModel::class.java)) {return NewsViewModel(newsType, pageSize) as T}throw IllegalArgumentException("Unknown ViewModel class")}
}复制代码

NewsViewModel本身封装了下拉刷新和上拉加载的逻辑,并且提供了相应的方法。在Fragment中只需要在回调里面触发方法即可。这里面的liveData使用是MutableLiveData即可变的LiveData,因为每次请求数据之后我们需要重新设置liveData里面的值。这样的话在对应的Fragment中只需要监听LiveData做好界面显示逻辑就可以了。

NewsFragment的代码如下

class NewsFragment : Fragment() {private val PAGE_SIZE = 10private var dataList: List<News> = ArrayList()private lateinit var newsViewModel: NewsViewModelprivate lateinit var newsLiveData: LiveData<List<News>>private var adapter: NewsListAdapter? = nullprivate var newsType: NewsType = NewsType.TechNewsprivate fun getObserver() = Observer<List<News>> { newsList ->if (newsList != null) {dataList = newsListif (adapter == null) {adapter = NewsListAdapter(context, dataList)adapter!!.onItemClickListener = onItemClickListenerrecyclerView.layoutManager = LinearLayoutManager(context)recyclerView.adapter = adapter} else {adapter?.data = dataList}smartRefreshLayout.finishLoadmore()smartRefreshLayout.finishRefresh()adapter!!.notifyDataSetChanged()recyclerView.scrollToPosition(dataList.size - PAGE_SIZE)}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)newsType = arguments?.getNewsType(KEY_NEWS_TYPE)!!}private val onItemClickListener = object : NewsListAdapter.OnItemClickListener {override fun onItemClick(view: View, position: Int) {val item = dataList[position]val intent = WebViewActivity.makeIntent(context, item.url, item.title, "")startActivity(intent)}}override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View {val view = inflater?.inflate(R.layout.news_fragment, container, false)return view!!}override fun onActivityCreated(savedInstanceState: Bundle?) {super.onActivityCreated(savedInstanceState)newsViewModel = ViewModelProviders.of(this, NewsViewModelFactory(newsType, PAGE_SIZE)).get(NewsViewModel::class.java)newsLiveData = newsViewModel.getLiveData()newsLiveData.observe(this, getObserver())smartRefreshLayout.setOnRefreshListener {newsViewModel.refresh()}smartRefreshLayout.setOnLoadmoreListener {newsViewModel.loadMore()}}companion object {val KEY_NEWS_TYPE = "KEY_NEWS_TYPE"fun newInstance(newsType: NewsType): NewsFragment {val fragment = NewsFragment()val bundle = Bundle()bundle.putNewsType(KEY_NEWS_TYPE, newsType)fragment.arguments = bundlereturn fragment}}
}复制代码

在onActivityCreated回调中创建ViewModel并且获取LiveData进行监听,在Observer的回调中进行RecycleView的显示逻辑处理。关于下拉刷新和上拉加载这里使用了SmartRefreshLayout,只需要在回调中触发ViewModel中对应的方法,数据获取成功之后同样执行Observer中代码逻辑。其他代码逻辑比较明显就不在介绍了。

完整代码可以查看user-gold-cdn.xitu.io/2018/1/10/1…

App目前发布在酷安应用市场www.coolapk.com/apk/name.dm…,欢迎下载试用

总结

按照Android官方建议项目中RxJava和LiveData选择一个即可。我们这里两个都使用了,这里大家可以根据结合自己的情况选择。使用LiveData可以不用关心生命周期的问题,但是LiveData本身提供操作符没有RxJava功能强大;如果选择RxJava可以结合Rxlifecyle使用来弥补关于生命周期的问题。整体来看Android提供这一套架构组件对我们的开发还是非常有指导意义的,尤其是关于ViewModel的作用不仅局限本篇这种形式,具体可以参考官方文档。欢迎大家一起交流使用心得!

基于Kotlin、ViewModel、LiveData和LifeCycle开发的Readhub客户端相关推荐

  1. 基于PyQt5的图形化界面开发——模拟医院管理系统

    基于PyQt5的图形化界面开发--模拟医院管理系统 0. 前言 1. 需求分析 2. 挂号界面的思路.UI界面代码及相应触发函数 2.1 思路分析 2.2 ui_guahao.py 2.3 相应的触发 ...

  2. 基于PyQt5的图形化界面开发——Windows内存资源监视助手[附带编译exe教程]

    基于PyQt5的图形化界面开发--Windows内存资源监视助手[附带编译exe教程] 0. 前言 1. 资源信息获取函数--monitor.py 2. UI界面--listen.py 3. main ...

  3. Android开发:基于Kotlin编写一个简易计算器

    目录 前言 Kotlin学习tips 界面绘制及控件绑定 UI界面绘制 控件绑定 Button点击事件 运算逻辑 整体逻辑 边界情况 输入展示 点击数字键 点击运算符键 点击"=" ...

  4. 基于Kotlin(可转Java)开发的网易云音乐爬虫项目

    简介 基于kotlin环境开发,但转Java是可以的,只是多写点代码而已.直奔主题,我是朝着云音乐的评论去的,在接口没有暗改前这个还是可以使用的,具体代码参见:CloudMusic 先说一下使用方法, ...

  5. viewmodel+livedata+binding 实现listview+adapter

    首先listview 布局,这是一个fragment的layout,通过binding 自动注入! fragment_yao_ce.xml <layout xmlns:android=" ...

  6. Android的MvVM模式探讨: Databinding 与 ViewModel+LiveData+Repository对比

    Mvvm模式: Databinding 与 ViewModel+LiveData+Repository 作者:Yagami3zZ,转自:https://www.jianshu.com/p/e7628d ...

  7. Mvvm模式: Databinding 与 ViewModel+LiveData+Repository

    前言: 本文主要是对常见设计模式的一些分析,以及讲述在Android项目中实现Mvvm模式的两种方式.通过Databinding或者ViewModel+LiveData+Repository如何实现M ...

  8. 【JetPack+Retrofit+Rxjava】获取Bing每日一图并显示ViewModel+LiveData+DataBinding+MVVM 补充笔记

    扉: 原文来自:Android官方架构组件ViewModel+LiveData+DataBinding架构属于自己的MVVM 很喜欢作者的思路,但是使用Kotlin需要配置的东西好多并且很多细节要重写 ...

  9. 第13章 Kotlin 集成 SpringBoot 服务端开发(1)

    第13章 Kotlin 集成 SpringBoot 服务端开发 本章介绍Kotlin服务端开发的相关内容.首先,我们简单介绍一下Spring Boot服务端开发框架,快速给出一个 Restful He ...

  10. Andriod --- JetPack (七):Room + ViewModel + LiveData 增删改查实例

    1.Andriod - JetPack (一):初识 JetPack 2.Andriod - JetPack (二):LifeCycle 的诞生 3.Andriod - JetPack (三):Vie ...

最新文章

  1. python opencv模板匹配多目标_基于opencv的多目标模板匹配
  2. Nginx + Lua + redis (一)(转)
  3. Serializable Parcelable
  4. 卷积神经网络 池化层上采样(upsampling、interpolating)、下采样(subsampled、downsampled)是什么?(上采样为放大图像或图像插值、下采样为缩小图像)
  5. 洛谷 CF1043F Make It One 解题报告
  6. 多个goruntine 性能变慢_提高 JavaScript 性能的 12 个技巧
  7. hashmap为什么线程不安全_StringBuilder为什么线程不安全?
  8. ssh远程登录Jupyter notebook(七月GPU服务器)
  9. oracle查询语句转sql,将sql server查询语句转换为oracle查询语句[紧急]
  10. Mysql读写分离的四种方案
  11. 史上最全的测试团队组建方法
  12. PID控制器的输入量和输出量的物理关系解释
  13. Python版按键精灵基础代码
  14. 点击复制按钮复制input中的内容
  15. Intel(Altera)FPGA的SOF转JIC文件和下载详细教程
  16. matlab diff函数用法_Matlab-计算机代数工具箱
  17. labview信号时序分析--谐波失真分析
  18. mysql fulltext match_使用Mysql全文搜索Full-Text(fulltext和match...against)_MySQL
  19. 【雷达通信】基于matlab大规模MIMO三维信道【含Matlab源码 2105期】
  20. 台积电业绩出现下滑,开始进一步向中国大陆芯片企业示好

热门文章

  1. 【Linux系列文章】网络配置
  2. PIXHAWK飞控固件及代码基础介绍
  3. 1150 Travelling Salesman Problem (25 分)(分析题目,细节处理)
  4. Conflux人物志 | Péter明哲 Marcel马驰
  5. 医院管理系统(Java+SSM+MySQL开发的医院科室管理系统)
  6. java modelbus_modelbus tcp java
  7. vsftpd 虚拟用户
  8. no.4京东话费充值系统架构演讲读后感
  9. win7计算机内存占用高,win7降低电脑内存占用过高的方法
  10. 3500元计算机基本硬件配置清单,电脑硬件中配配置清单