以前无心中关注了无码科技的公众号,由此知道了他们推出的第一个产品Readhub,地址为readhub.me/,主要提供互联网最新发生的新鲜事,关注了一段时间感受内容质量还不错,可以帮咱们筛选掉必定的垃圾信息。可是它目前只能在浏览器和微信公众号里面查看,又加上本身一直想体验一下谷歌推出的架构组件,因此在简单分析了一下Readhub Web端的接口以后开发了一个Android版本的客户端。GitHub地址user-gold-cdn.xitu.io/2018/1/10/1…java
App架构比较简单:一个主Activity+三个Fragment。目前Readhub的信息只有三个分类,分别为热门话题、科技动态和开发者资讯。其中科技动态和开发者资讯数据模型相同,只是调用的就接口不一样,能够在很大程度上进行复用。api
项目目录划分以下,浏览器
和Android官方文档建议的架构基本是一致。缓存
目前Repository中只是单纯的从网络请求数据,没有作本地缓存,代码以下微信
class DataRepository private constructor(context: Context) {
private val SERVER_ADDRESS = "https://api.readhub.me/"
private val httpService: Api
init {
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? = null
fun getInstance(context: Context): DataRepository {
if (instance == null) {
synchronized(DataRepository::class.java) {
if (instance == null) {
instance = DataRepository(context)
}
}
}
return instance!!
}
}
}
复制代码
ViewModel目前有两个:TopicViewModel
和NewsViewModel
,NewsViewModel
用于为科技动态和开发者资讯提供数据,以NewsViewModel
为例,网络
class NewsViewModel(private val newsType: NewsType, private val pageSize:Int) : ViewModel() {
private val liveData: MutableLiveData<List<News>> = MutableLiveData()
private var isFirstPage = true
private var lastCursor: Long = 0L
private val newsList = ArrayList<News>()
fun getLiveData(): LiveData<List<News>> {
lastCursor = System.currentTimeMillis()
fetchData()
return liveData
}
fun refresh() {
isFirstPage = true
lastCursor = System.currentTimeMillis()
fetchData()
}
fun loadMore() {
isFirstPage = false
fetchData()
}
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 = newsList
lastCursor = 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作好界面显示逻辑就能够了。ide
NewsFragment
的代码以下fetch
class NewsFragment : Fragment() {
private val PAGE_SIZE = 10
private var dataList: List<News> = ArrayList()
private lateinit var newsViewModel: NewsViewModel
private lateinit var newsLiveData: LiveData<List<News>>
private var adapter: NewsListAdapter? = null
private var newsType: NewsType = NewsType.TechNews
private fun getObserver() = Observer<List<News>> { newsList ->
if (newsList != null) {
dataList = newsList
if (adapter == null) {
adapter = NewsListAdapter(context, dataList)
adapter!!.onItemClickListener = onItemClickListener
recyclerView.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 = bundle
return fragment
}
}
}
复制代码
在onActivityCreated回调中建立ViewModel而且获取LiveData进行监听,在Observer的回调中进行RecycleView的显示逻辑处理。关于下拉刷新和上拉加载这里使用了SmartRefreshLayout,只须要在回调中触发ViewModel中对应的方法,数据获取成功以后一样执行Observer中代码逻辑。其余代码逻辑比较明显就不在介绍了。ui
完整代码能够查看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的做用不只局限本篇这种形式,具体能够参考官方文档。欢迎你们一块儿交流使用心得!