MVVM architecture using android architecture components

Sachin Kumar
MindOrks
Published in
5 min readOct 8, 2018

Google has already introduced Android Architecture Components which includes ViewModel rather than Presenter in MVP or Controller in MVC and hence it indicates that Google is supporting MVVM.

So, in this article, we are going to implement MVVM using Android Architecture Components such as :

  1. LiveData: It is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.
  2. ViewModel: ViewModels are simples classes that interact with the logic/model layer and just exposes states/data and actually has no idea by whom or how that data will be consumed. Only View(Activity) holds the reference to ViewModel and not vice versa.
  3. Room: It provides an abstraction layer over SQLite to allow for more robust database access while harnessing the full power of SQLite.

Enough theory, let us create a sample app in which we have a normal Recyclerview and we have to fetch the latest news from firebase and display it in Recyclerview. For this we’re going to create the following packages :

Here,

  • Model.Firebase contains both the Firebase init code to fetch the news as well Room init code to save the bookmarked news. So, no View and viewModel code will be there.
  • View contains our MainActivity where we have our recyclerview to display the fetched news.
  • ViewModel contains simple java code independent of Model and View code. Its job is just to serve the request of the view after demanding the data from the Model.

And add the following dependencies in build.gradle(app) to use the above components:

//architecture components
compile ‘android.arch.lifecycle:extensions:1.0.0’
annotationProcessor ‘android.arch.lifecycle:compiler:1.0.0’
compile ‘android.arch.persistence.room:runtime:1.0.0’
annotationProcessor ‘android.arch.persistence.room:compiler:1.0.0’
kapt ‘android.arch.persistence.room:compiler:1.0.0’

In MVVM View doesn’t have to request the ViewModel for the data every time, View just register itself using LiveData’s observer method and ViewModel has the reference of the LiveData so whenever ViewModel receives any latest data from Model it automatically updates View.

We are going to create the following files in the above listed packages :

Let’s get into the files one by one :

  • FirebaseInit.kt: It contains the code to fetch the news from Firebase using FirebaseRemoteConfig.

class FirebaseInit {

private var remoteConfig: FirebaseRemoteConfig? = null

private var mProgressBar: ProgressDialog?= null

private var mNews=””;

private var mContext:Context?=null

private var mCount:Int=0

fun getNews(context: Context): String {

mContext=context

mProgressBar = ProgressDialog(mContext)

mProgressBar!!.setCancelable(false)

mProgressBar!!.setMessage(“Please wait…”)

mProgressBar!!.setProgressStyle(ProgressDialog.STYLE_SPINNER)

val remoteConfigSettings = FirebaseRemoteConfigSettings.Builder()

.setDeveloperModeEnabled(BuildConfig.DEBUG)

.build()

remoteConfig = FirebaseRemoteConfig.getInstance()

remoteConfig!!.setConfigSettings(remoteConfigSettings)

remoteConfig!!.setDefaults(R.xml.remote_config)

var cacheExpiration: Long = 0

if(remoteConfig!!.getInfo().configSettings.isDeveloperModeEnabled) {

cacheExpiration = 0

}

mProgressBar!!.show()

val handler = Handler()

remoteConfig!!.activateFetched()

mNews = remoteConfig!!.getString(“newstoday”)

val run = Runnable {

remoteConfig!!.fetch(cacheExpiration).addOnSuccessListener {

//Log.e(“Success”, “Fetch Succeeded”+readSharedPref())

if(readSharedPref()==0){

openMainActivity()

writeToSharedPref()

}

mProgressBar!!.dismiss()

}

}

handler.post(run)

mProgressBar!!.dismiss()

//Log.e(“SuccessStr:”,remoteConfig!!.getString(“newstoday”))

return mNews

}

}

  • BookmarkNews.kt: As we are using Room architecture component, for creating a database table in Room we need to define the class like following:

@Entity

class BookmarkNews {

@PrimaryKey

var news:String=””

var date:String=””

}

BookmarkNews is the name of the table we are gonna create, @Entity annotation is must to define the table in Room. Here, news column is our primary key so we have placed @PrimaryKey above news variable.

  • NewsDao.kt: It’s an interface which is used to define the queries to perform the operation on the table.
@Dao
interface NewsDao {

@Query("SELECT * FROM BookmarkNews")
fun getNews() : List<BookmarkNews>

@Query("SELECT * FROM BookmarkNews ORDER BY date DESC")
fun getSortedNews():List<BookmarkNews>

@Insert(onConflict = IGNORE)
fun insertNews(bookmarkNews: BookmarkNews)

@Query("DELETE FROM BookmarkNews WHERE news LIKE :arg0")
fun deleteNews(newsStr: String)
}

Here @Dao stands for Data Access Object which contains the methods for accessing the database.

  • AppDatabase.kt:
@Database(entities = arrayOf(BookmarkNews::class), version = 3, exportSchema = false)
public abstract class AppDatabase : RoomDatabase() {

public abstract fun NewsModel(): NewsDao

companion object {

private var INSTANCE:AppDatabase?=null

fun getDatabase(context: Context) : AppDatabase{
if (INSTANCE==null){
INSTANCE = Room.databaseBuilder(context, AppDatabase::class.java,"NewsDB")
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.build()

}
return INSTANCE as AppDatabase
}

fun destroyInstance(){
INSTANCE=null
}

}

}

In the above code in annotation @Database, entities are tables present in our database, for now, we have one table so just added BookmarkNews::class. We are extending RoomDatabase() and creating a single instance of our database named “NewsDB”.

  • MyViewModel: It extends AndroidViewModel see how simple java implementation it is, we just initialize MutableLiveData and call setValue() method whenever we are in need to fetch data from Firebase or Room database. Its job is to interact with the Model.

public class MyViewModel extends AndroidViewModel {

private FirebaseInit firebaseInit;
private AppDatabase mappDatabase;
private MutableLiveData<String> mCurrentNews;
private MutableLiveData<List<BookmarkNews>> mMutRef;


public MyViewModel(@NonNull Application application){
super(application);
firebaseInit = new FirebaseInit();
mappDatabase = AppDatabase.Companion.getDatabase(application);
}

public MutableLiveData<String> getNewsfromFirebase(Context context){
if (mCurrentNews==null){
mCurrentNews = new MutableLiveData<>();
}
mCurrentNews.setValue(firebaseInit.getNews(context));
return mCurrentNews;
}

public MutableLiveData<List<BookmarkNews>> getNewsfromDB(){
MutableLiveData<List<BookmarkNews>> refLive = getMutableRef();
refLive.setValue(mappDatabase.NewsModel().getNews());
return refLive;
}

public MutableLiveData<List<BookmarkNews>> getSortedNewsfromDB(){
MutableLiveData<List<BookmarkNews>> refLive = getMutableRef();
refLive.setValue(mappDatabase.NewsModel().getSortedNews());
return refLive;
}

public void storeNews(BookmarkNews bookmarkNews){
mappDatabase.NewsModel().insertNews(bookmarkNews);
}

public void deleteNews(String news){
mappDatabase.NewsModel().deleteNews(news);
}

private MutableLiveData<List<BookmarkNews>> getMutableRef(){
if (mMutRef==null){
mMutRef = new MutableLiveData<>();
}
return mMutRef;
}
}

  • MainActivity: This is our view in which we are going to request MyViewModel class to fetch the data. In this we override the onChanged() method of Observer interface which will be called automatically once MyViewModel will be able to fetch the data.
class MainActivity : AppCompatActivity(){

private var viewModel: ViewModel? = null
private var adapter: MyAdapter? = null
private var context: Context? = null
private var mNewsInStr: String? = ""
private var mlistOfsavedNews: List<BookmarkNews>? =null
private var recyclerView: RecyclerView? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

//ViewModel initialization..
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
recyclerView = findViewById(R.id.recyclerView)
context = this


(viewModel as MyViewModel).getNewsfromFirebase(context).observe(this,android.arch.lifecycle.Observer{
mNewsInStr = it
//Log.e("LiveData:Observer:",mNewsInStr)
doDboperation()
})


}
fun openMainActivity(){
val handler = Handler()
val run = Runnable {
val `in` = Intent(this, MainActivity::class.java)
`in`.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(`in`)
}
handler.post(run)
}

fun doDboperation(){
(viewModel as MyViewModel).getNewsfromDB().observe(this,android.arch.lifecycle.Observer{
mlistOfsavedNews = it
//Log.e("LiveData:Observer:DB",mNewsInStr)
adapter = MyAdapter(context as MainActivity, viewModel as ViewModel)
adapter!!.setNews(mNewsInStr)
adapter!!.setNewsFromDB(mlistOfsavedNews)

var mLayoutManager: RecyclerView.LayoutManager = LinearLayoutManager(context)
recyclerView!!.layoutManager = mLayoutManager
recyclerView!!.itemAnimator
recyclerView!!.isNestedScrollingEnabled = false
recyclerView!!.adapter = adapter
})

}
}

So, let’s conclude the benefit of using MVVM:

  • It has all the benefits of android architecture components when using it for android development.
  • It makes our code more testable as ViewModel code is pure java or kotlin.
  • It makes our code easy to maintain due to the separation of concern, all View, Model, and ViewModel work independently and these can be easily modified on go.

If you have liked this article, please hit the clap button and share your valuable feedback for improvement.

--

--

Sachin Kumar
MindOrks

Senior Java Backend Dev | Expertise in Java Microservices, Spring Boot Framework & Android apps development.