RSS Reading MVVM based improvement

Prologue

success_anil
5 min readFeb 8, 2020

--

For me, MVVM is one of the best design patterns available today for improving RSS Reading in kotlin via MVVM based code.

In our last blog, under the EASY ANDROID Programming Category, you helped yourself by developing a basic RSS Reader Application in Kotlin.

To start with, I would rephrase or rather a popular phrase,

Practise makes a man perfect.

So we practice a lot, improve our existing code. Great.

But why?

There is one another saying popular among army people,

soldiers who shed a lot of sweat by hard work in practice, will shed less blood in battles.

Enough of motivation :). Let’s get down to work

Introduction to Jetpack

If you pay attention to the below image, you will see Google developers have introduced Jetpack in Android to supercharge your app development.

In the context of the topic for this blog, we are going to interact with the Architecture component of Android Jetpack. We will be improving existing code for RSS Reading Application made using Kotlin language. In addition to this, I would like to suggest the reader may have knowledge of git.

File Organization

Below is a snapshot of files in our code at Github. Please checkout the master branch.

Conceptually, the Architecture component is implemented as MVVM. See below image

The data flow between the user interface layer, view model layer and singleton repository layer. Knowingly I am writing a singleton repository so that it is easier to remember that there should be only a single instance of the repository within the application.

Let’s add two folders named repository and viewmodels.

if you are new to java or kotlin, I would like to tell you about singleton classes. This class has only a single instance throughout the application. Singleton class is very important and follows an important design pattern called Singleton.

To create a singleton class, we will make it’s constructor private so its objects can’t be created using new. But in our application, we need at least one instance of the singleton class, so there must be some way of creating an instance of the singleton class. Here comes the initializer method which when called from multiple places in code will return a new instance of a singleton class for the very first time but for another time it will return the same instance. Below code in kotlin language is written like that. Please add a file called RssRepository.kt inside the repository folder.

package com.relsellglobal.kotlinrssreading.repositoryclass RssRepository private constructor(){    private object HOLDER {
val INSTANCE = RssRepository()
}
companion object {
val instance: RssRepository by lazy { HOLDER.INSTANCE }
}
}

Connecting ViewModel class with Repository

Here is our updated RssFragmentViewModel class or ViewModel layer class

/*
* Copyright (c) 2020. Relsell Global
*/
package com.relsellglobal.kotlinrssreading.viewmodelsimport androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.relsellglobal.kotlinrssreading.RssItem
import com.relsellglobal.kotlinrssreading.repository.RssRepository
class RssFragmentViewModel : ViewModel() {
private lateinit var rssItems : MutableLiveData<List<RssItem>>
fun fetchRssItems():LiveData<List<RssItem>> {
if(!::rssItems.isInitialized) {
rssItems = MutableLiveData()
rssItems = loadRssItems()
}
return rssItems
}
private fun loadRssItems() : MutableLiveData<List<RssItem>>{
rssItems = RssRepository.instance.fetchRSSData()
return rssItems
}
}

Updated RssRepository class or repository layer class

/*
* Copyright (c) 2020. Relsell Global
*/
package com.relsellglobal.kotlinrssreading.repositoryimport android.os.AsyncTask
import androidx.lifecycle.MutableLiveData
import com.relsellglobal.kotlinrssreading.RssItem
import com.relsellglobal.kotlinrssreading.RssParser
import java.io.IOException
import java.io.InputStream
import java.net.URL
import javax.net.ssl.HttpsURLConnection
class RssRepository private constructor(){ private val RSS_FEED_LINK = "https://www.relsellglobal.in/feed/"; private object HOLDER {
val INSTANCE = RssRepository()
}
companion object {
val instance: RssRepository by lazy { HOLDER.INSTANCE }
}
fun fetchRSSData():MutableLiveData<List<RssItem>> {
val url = URL(RSS_FEED_LINK)
val data = MutableLiveData<List<RssItem>>()
RssFeedFetcher(data).execute(url)
return data
}
class RssFeedFetcher(data: MutableLiveData<List<RssItem>>) : AsyncTask<URL, Void, List<RssItem>>() {
private var stream: InputStream? = null;
val dataObj:MutableLiveData<List<RssItem>> = data
override fun doInBackground(vararg params: URL?): List<RssItem>? {
val connect = params[0]?.openConnection() as HttpsURLConnection
connect.readTimeout = 8000
connect.connectTimeout = 8000
connect.requestMethod = "GET"
connect.connect();
val responseCode: Int = connect.responseCode;
var rssItems: List<RssItem>? = null
if (responseCode == 200) {
stream = connect.inputStream;
try {
val parser = RssParser()
rssItems = parser.parse(stream!!)
} catch (e: IOException) {
e.printStackTrace()
}
} return rssItems } override fun onPostExecute(result: List<RssItem>?) {
super.onPostExecute(result)
if (result != null && !result.isEmpty()) {
dataObj.value = result
}
} }
}

Now after hooking up with UI Layer, UI Layer class or RssFragment will look like

/*
* Copyright (c) 2020. Relsell Global
*/
package com.relsellglobal.kotlinrssreadingimport android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.relsellglobal.kotlinrssreading.viewmodels.RssFragmentViewModel
/**
* A fragment representing a list of Items.
* Activities containing this fragment MUST implement the
* [RSSFragment.OnListFragmentInteractionListener] interface.
*/
class RSSFragment : Fragment() {
// TODO: Customize parameters
private var columnCount = 1
private var listener: OnListFragmentInteractionListener? = null val RSS_FEED_LINK = "https://www.relsellglobal.in/feed/"; var adapter: MyItemRecyclerViewAdapter? = null
var rssItems = ArrayList<RssItem>()
var listV : RecyclerView ?= null override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_item_list, container, false)
listV = view.findViewById(R.id.listV)
return view
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
adapter = MyItemRecyclerViewAdapter(rssItems, listener,activity)
listV?.layoutManager = LinearLayoutManager(activity,LinearLayoutManager.VERTICAL,false)
listV?.adapter = adapter

val model = ViewModelProviders.of(this).get(RssFragmentViewModel::class.java)
model.fetchRssItems().observe(this, Observer {
if (it != null && !it.isEmpty()) {
rssItems.addAll(it)
adapter?.notifyDataSetChanged()
}
})
}
interface OnListFragmentInteractionListener {
// TODO: Update argument type and name
fun onListFragmentInteraction(item: RssItem?)
}
}

Hence the output

This brings us to the end of the blog. We can improve it more by introducing retrofit (a networking lib) for fetching data for RSS items. But that we will be covering in another blog.

Thanks for your time and for reading this.

If you like it share it, if not feel free to let me know what I can improve, please post comments.

Last but not the least code is available at Link under mvvmimprovement branch

Happy coding

--

--

success_anil

Learning via repetition. Long but sturdy, reliable way to learn.