Wednesday 14 November 2018

HB Blog 161: Jetpack Architecture: - Room And Live Data In Kotlin Language.

In my previous, post we saw Listview Using DataBinding In Kotlin Language,

In this post, lets store data using Room And Live data,

Room is an a SQLite object mapping library. Use it to Avoid boilerplate code and easily convert SQLite table data to Java objects. Room provides compile time checks of SQLite statements and can return RxJava, Flowable and LiveData observables.

Manage your app's lifecycle with ease. New lifecycle-aware components help you manage your activity and fragment lifecycles. Survive configuration changes, avoid memory leaks and easily load data into your UI.

Use LiveData to build data objects that notify views when the underlying database changes.

ViewModel Stores UI-related data that isn't destroyed on app rotations.
Refer the below link for complete sample code:-

Download Sample Code

Have a look on few code snippets,
//build.gradle
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:27.0.2'
    implementation 'com.android.support:design:27.0.2'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:+"
    //room
    implementation 'android.arch.persistence.room:runtime:1.0.0'
    kapt"android.arch.persistence.room:compiler:1.0.0"
    //Lifecycle
    implementation 'android.arch.lifecycle:extensions:1.0.0'
    annotationProcessor "android.arch.lifecycle:compiler:1.0.0"
}

//activity_addperson.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="16dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/tv_name"
                android:layout_width="150dp"
                android:layout_height="wrap_content"
                android:gravity=""
                android:text="name :"
                android:textAppearance="@style/Base.TextAppearance.AppCompat.Caption" />

            <EditText
                android:id="@+id/et_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="enter name"
                android:inputType="text"
                android:textAppearance="@style/Base.TextAppearance.AppCompat.Caption" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/tv_age"
                android:layout_width="150dp"
                android:layout_height="wrap_content"
                android:text="Age :"
                android:textAppearance="@style/Base.TextAppearance.AppCompat.Caption" />

            <EditText
                android:id="@+id/et_age"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="enter age"
                android:inputType="number"
                android:textAppearance="@style/Base.TextAppearance.AppCompat.Caption" />
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

//PersonProfile.kt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.harshalbenake.kotlinroomlivedata.data.model

import android.arch.persistence.room.ColumnInfo
import android.arch.persistence.room.Entity
import android.arch.persistence.room.PrimaryKey

@Entity(tableName = "personprofile")
data class PersonProfile(
        @PrimaryKey(autoGenerate = true)
        @ColumnInfo(name = "idPerson")
        var idPerson: Int = 0,

        @ColumnInfo(name = "name")
        var name: String = "",

        @ColumnInfo(name = "age")
        var age: String = ""

)

//PersonProfileDAO.kt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.harshalbenake.kotlinroomlivedata.data

import android.arch.lifecycle.LiveData
import android.arch.persistence.room.*
import com.harshalbenake.kotlinroomlivedata.data.model.PersonProfile

@Dao
interface PersonProfileDAO {
    @Query("select * from personprofile")
    fun getAllPersonProfiles(): LiveData<List<PersonProfile>>

    @Query("select * from personprofile where age>18")
    fun getAllPersonProfilesAbove18(): PersonProfile

    @Query("select * from personprofile where idPerson in (:id)")
    fun getPersonById(id: Int): PersonProfile

    @Query("delete from personprofile")
    fun deleteAllPersonProfiles()

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertPersonProfiles(personProfile: PersonProfile)

    @Update
    fun updatePersonProfiles(personProfile: PersonProfile)

    @Delete
    fun deletePersonProfiles(personProfile: PersonProfile)
}

//PersonProfileDb.kt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.harshalbenake.kotlinroomlivedata.data

import android.arch.persistence.room.Database
import android.arch.persistence.room.Room
import android.arch.persistence.room.RoomDatabase
import android.content.Context
import com.harshalbenake.kotlinroomlivedata.data.model.PersonProfile

@Database(entities = [(PersonProfile::class)], version = 1, exportSchema = false)
abstract class PersonProfileDb : RoomDatabase() {
    companion object {
        private var INSTANCE: PersonProfileDb? = null
        fun getDataBase(context: Context): PersonProfileDb {
            if (INSTANCE == null) {
                INSTANCE = Room.databaseBuilder(context.applicationContext, PersonProfileDb::class.java, "personprofile-db")
                        .allowMainThreadQueries().build()
            }
            return INSTANCE as PersonProfileDb
        }
    }

    abstract fun personProfileDAO(): PersonProfileDAO
}

//PersonProfileViewModel.kt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.harshalbenake.kotlinroomlivedata.viewmodel

import android.app.Application
import android.arch.lifecycle.AndroidViewModel
import android.arch.lifecycle.LiveData
import android.os.AsyncTask
import com.harshalbenake.kotlinroomlivedata.data.model.PersonProfile
import com.harshalbenake.kotlinroomlivedata.data.PersonProfileDb

class PersonProfileViewModel(application: Application) : AndroidViewModel(application) {

    var listPersonProfile: LiveData<List<PersonProfile>>
    private val appDb: PersonProfileDb

    init {
        appDb = PersonProfileDb.getDataBase(this.getApplication())
        listPersonProfile = appDb.personProfileDAO().getAllPersonProfiles()
    }

    fun getListPersonProfiles(): LiveData<List<PersonProfile>> {
        return listPersonProfile
    }

    fun addersonProfile(personProfile: PersonProfile) {
        addAsynTask(appDb).execute(personProfile)
    }


    class addAsynTask(db: PersonProfileDb) : AsyncTask<PersonProfile, Void, Void>() {
        private var personProfileDb = db
        override fun doInBackground(vararg params: PersonProfile): Void? {
            personProfileDb.personProfileDAO().insertPersonProfiles(params[0])
            return null
        }

    }

}

//AddPersonActivity.kt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package com.harshalbenake.kotlinroomlivedata.ui

import android.arch.lifecycle.ViewModelProviders
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.widget.Toast
import com.harshalbenake.kotlinroomlivedata.R
import com.harshalbenake.kotlinroomlivedata.data.model.PersonProfile
import com.harshalbenake.kotlinroomlivedata.data.PersonProfileDAO
import com.harshalbenake.kotlinroomlivedata.data.PersonProfileDb
import com.harshalbenake.kotlinroomlivedata.viewmodel.PersonProfileViewModel
import kotlinx.android.synthetic.main.activity_addperson.*

//import kotlinx.android.synthetic.main.activity_contact_details.*

class AddPersonActivity : AppCompatActivity() {

    private var personProfileDAO: PersonProfileDAO? = null
    private var personProfileViewModel: PersonProfileViewModel? = null
    private var currentPersonProfile: Int? = null
    private var personProfile: PersonProfile? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_addperson)
        var personProfileDB: PersonProfileDb = PersonProfileDb.getDataBase(this)
        personProfileDAO = personProfileDB.personProfileDAO()
        personProfileViewModel = ViewModelProviders.of(this).get(PersonProfileViewModel::class.java)
        currentPersonProfile = intent.getIntExtra("idPerson", -1)
        if (currentPersonProfile != -1) {
            setTitle("Edit")
            personProfile = personProfileDAO!!.getPersonById(currentPersonProfile!!)
            et_name.setText(personProfile!!.name)
            et_age.setText(personProfile!!.age)
        } else {
            setTitle("Add")
            invalidateOptionsMenu()
        }
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        var inflater: MenuInflater = menuInflater
        inflater.inflate(R.menu.menu_items, menu)
        return true
    }

     override fun onOptionsItemSelected(item: MenuItem?): Boolean {
        when (item!!.itemId) {
            R.id.done_item -> {
                if (currentPersonProfile == -1) {
                    menuSavePersonProfile()
                    Toast.makeText(this, "Saved Successfully", Toast.LENGTH_SHORT).show()
                } else {
                    menuUpdatePersonProfile()
                    Toast.makeText(this, "Updated Successfully", Toast.LENGTH_SHORT).show()
                }
                finish()
            }
            R.id.delete_item -> {
                menuDeletePersonProfile()
                Toast.makeText(this, "Deleted Successfully", Toast.LENGTH_SHORT).show()
                finish()
            }
        }
        return super.onOptionsItemSelected(item)
    }

    override fun onPrepareOptionsMenu(menu: Menu): Boolean {
        super.onPrepareOptionsMenu(menu)
        if (currentPersonProfile == -1) {
            menu.findItem(R.id.delete_item).isVisible = false
        }
        return true
    }

    private fun menuSavePersonProfile() {
        var nameContact = et_name.text.toString()
        var numberContact = et_age.text.toString()
        var contact = PersonProfile(0, nameContact, numberContact)
        personProfileViewModel!!.addersonProfile(contact)
    }

    private fun menuDeletePersonProfile() {
        personProfileDAO!!.deletePersonProfiles(personProfile!!)
    }

    private fun menuUpdatePersonProfile() {
        var nameContact = et_name.text.toString()
        var numberContact = et_age.text.toString()
        var contact = PersonProfile(personProfile!!.idPerson, nameContact, numberContact)
        personProfileDAO!!.updatePersonProfiles(contact)
    }

}

//MainActivity.kt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.harshalbenake.kotlinroomlivedata.ui

import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders
import android.content.Intent
import android.os.Bundle
import android.support.design.widget.FloatingActionButton
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.Menu
import android.view.MenuItem
import com.harshalbenake.kotlinroomlivedata.R
import com.harshalbenake.kotlinroomlivedata.adapter.PersonProfileAdapter
import com.harshalbenake.kotlinroomlivedata.data.model.PersonProfile
import com.harshalbenake.kotlinroomlivedata.data.PersonProfileDb
import com.harshalbenake.kotlinroomlivedata.viewmodel.PersonProfileViewModel

class MainActivity : AppCompatActivity(), PersonProfileAdapter.OnItemClickListener {

    private var personProfileRecyclerView: RecyclerView? = null
    private var personProfileAdapter: PersonProfileAdapter? = null
    private var personProfileViewModel: PersonProfileViewModel? = null
    private var personProfileDB: PersonProfileDb? = null
    private var fab:FloatingActionButton?= null

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

        initLayout()

        personProfileRecyclerView!!.layoutManager = LinearLayoutManager(this)
        personProfileRecyclerView!!.adapter = personProfileAdapter

        personProfileViewModel!!.getListPersonProfiles().observe(this, Observer { personprofile ->
            personProfileAdapter!!.addContacts(personprofile!!)
        })
        fab!!.setOnClickListener {
            var intent = Intent(applicationContext, AddPersonActivity::class.java)
            startActivity(intent)
        }
    }

    /**
     * init Layout
     */
    fun initLayout(){
        personProfileRecyclerView = findViewById(R.id.recycler_view)
        fab = findViewById(R.id.fab)
        personProfileDB = PersonProfileDb.getDataBase(this)
        personProfileAdapter = PersonProfileAdapter(arrayListOf(), this)
        personProfileViewModel = ViewModelProviders.of(this).get(PersonProfileViewModel::class.java)

    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }

   override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.delete_all_items -> {
                menuDeleteAllPersonProfiles()
            }
        }
        return super.onOptionsItemSelected(item)
    }

    /**
     * menu deletes All Person Profiles
     */
    private fun menuDeleteAllPersonProfiles() {
        personProfileDB!!.personProfileDAO().deleteAllPersonProfiles()
    }
    override fun onItemClick(personProfile: PersonProfile) {
        var intent = Intent(applicationContext, AddPersonActivity::class.java)
        intent.putExtra("idPerson", personProfile.idPerson)
        startActivity(intent)
    }
}