Android Architecture Components - Room - Kotlin
Introduction:
Google's Android Architecture components became stable now. It is nothing but a collection of libraries that ease our Android Development.
Android Architecture Components:
- DataBinding
- Navigation
- Paging Library
- WorkManager
- LiveData & ViewModel
- Room Persistence Library
Let's see about Room in this post.
Room is a new way to create a database in your Android Apps.
SQLite vs Room:
The following are some of the advantages of Room over SQLite.
(a) In SQLite, there is no compile-time verification of SQL queries, whereas Room verifies it.
(b) We need to write lots of boilerplate codes to convert SQL queries to Java Objects.
(c) We need to update the affected SQL queries manually, while the schema changes in SQLite. Whereas, Room will take care of it automatically.
Room Architecure and Components:
Three components are there in Room:
(1) Database is a holder class that uses annotation to define the list of entities (i.e., tables) and database version.
(2) Entity - represents a table within the database
(3) DAO (Database Access Objects) - Contains methods used for accessing database.
Code:
build.gradle (module level gradle):
Include the following in your module - level gradle.
apply plugin: 'kotlin-kapt' compile 'com.android.support:recyclerview-v7:27.0.2' compile 'com.android.support:cardview-v7:27.0.2' compile "android.arch.persistence.room:runtime:1.0.0" kapt "android.arch.persistence.room:compiler:1.0.0"
dialog.xml:
Design a layout to show as dialog while adding/inserting data to the created Table in Database.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="10dp"> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.design.widget.TextInputEditText android:id="@+id/name" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Book Name" /> </android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.design.widget.TextInputEditText android:id="@+id/author" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Author" /> </android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1"> <android.support.design.widget.TextInputEditText android:id="@+id/genre" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Genre" /> </android.support.design.widget.TextInputLayout> <TextView android:id="@+id/submit" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="20dp" android:layout_marginTop="20dp" android:background="@color/colorPrimary" android:gravity="center" android:padding="15dp" android:text="Submit" android:textColor="@color/white" /> </LinearLayout>
db_layout.xml:
Let's design a layout to list the stored data in Recyclerview and a FloatingActionButton to add new data to the database.
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"/> <android.support.design.widget.FloatingActionButton android:id="@+id/add" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:src="@drawable/add" app:elevation="2dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" /> </android.support.constraint.ConstraintLayout>
Book.kt (Entity - Class)
Entity is nothing but Table in Database. In Kotlin, we can define the Entity class as same as data class with so much ease. By default, the class name "Book" will be considered as Table name. If you need to assign a different name, you have define that as follows:
@Entity(tableName = "book_table")
data class Book(var bookName : String)
data class Book(@ColumnInfo(name = "book_name")var bookName : String)
Each table should contain one PrimaryKey, it can be added manually or auto generated. In this example, we are adding that autogenerate.
So, Our Book.kt class will be like as follows:
So, Our Book.kt class will be like as follows:
@Entity data class Book(var bookName : String, var author : String, var genre : String){ @PrimaryKey(autoGenerate = true) var id : Long? = null }
BookDao.kt (Dao - Interface)
Dao interface contains all queries that will be used to process Database.
@Dao interface BookDao { @Query("SELECT * FROM Book") fun getBookInfo() : MutableList<Book> @Insert fun addBook(book : Book) @Update(onConflict = REPLACE) fun updateBook( book: Book) @Delete fun deleteBook( book: Book) }
MyDatabase.kt (Database - Class)
Database class should be declared once like Singleton class and can be accessed throughout the app. It should include array of entities and version of Database. In Kotlin, there is no static keyword. But it can be replaced with companion object. Also, do remember that every database operations should be done only in Background thread. Otherwise the app will crash.
@Database(entities = arrayOf(Book::class), version = 1) abstract class MyDatabase : RoomDatabase() { abstract fun bookDao(): BookDao companion object { var INSTANCE: MyDatabase? = null fun getInstance(context: Context): MyDatabase { if (INSTANCE == null) { INSTANCE = Room.databaseBuilder(context.applicationContext, MyDatabase::class.java, "Books.db").build() } return INSTANCE as MyDatabase; } @SuppressLint("StaticFieldLeak") fun insertData(mydata: MyDatabase, book: Book) { object : AsyncTask<Void, Void, Void>() { override fun doInBackground(vararg voids: Void): Void? { mydata.bookDao().addBook(book) return null } }.execute() } @SuppressLint("StaticFieldLeak") fun getData(mydata: MyDatabase): MutableList<Book> { lateinit var lists: MutableList<Book> return object : AsyncTask<Void, Void, MutableList<Book>>() { override fun doInBackground(vararg voids: Void): MutableList<Book>? { lists = mydata.bookDao().getBookInfo() return lists } }.execute().get() } } }
RecyclerAdapter.kt (Adapter - Class)
class RecyclerAdapter(var context: Context, var data : List<Book>) : RecyclerView.Adapter<RecyclerAdapter.Holder>() { override fun onBindViewHolder(holder: Holder?, position: Int) { holder?.bindItems(data.get(position)) } override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): Holder { val v = LayoutInflater.from(context).inflate(R.layout.recycler_item, parent, false) return Holder(v) } override fun getItemCount(): Int = data.size class Holder(itemView: View?) : RecyclerView.ViewHolder(itemView) { fun bindItems(book: Book){ itemView.name.text = book.bookName itemView.author.text = book.author itemView.genre.text = book.genre } } }
DbActivity.kt
Finally, we can fetch data from Database and add it to our list and set it to our recyclerview's adapter. In FloatingActionButton's click event, a dialog will be opened. There the details of the book can be added and stored into the database. Now its done.
class DbActivity : AppCompatActivity() { lateinit var adapter: RecyclerAdapter lateinit var list: MutableList<Book> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.db_layout) list = MyDatabase.getData(MyDatabase.getInstance(this)) recyclerView.layoutManager = LinearLayoutManager(this) adapter = RecyclerAdapter(this, list) recyclerView.adapter = adapter add.setOnClickListener { openDialog() } } private fun openDialog() { val dialog = Dialog(this) dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) dialog.setContentView(R.layout.dialog) val lp: WindowManager.LayoutParams = WindowManager.LayoutParams().apply { copyFrom(dialog.window.attributes) width = WindowManager.LayoutParams.MATCH_PARENT height = WindowManager.LayoutParams.WRAP_CONTENT } val submit = dialog.findViewById<View>(R.id.submit) as TextView val name = dialog.findViewById<View>(R.id.name) as EditText val author = dialog.findViewById<View>(R.id.author) as EditText val genre = dialog.findViewById<View>(R.id.genre) as EditText submit.setOnClickListener { when { name.length() == 0 || author.length() == 0 || genre.length() == 0 -> Toast.makeText(this@DbActivity, "Please fill all the fields" , Toast.LENGTH_SHORT).show() else -> { val book = Book(name.text.toString(), author.text.toString(), genre.text.toString()) MyDatabase.insertData(MyDatabase.getInstance(this), book) dialog.dismiss() Handler().postDelayed({ list.clear() list.addAll(MyDatabase.getData(MyDatabase.getInstance(this))) adapter.notifyDataSetChanged() }, 1000) } } } dialog.show() dialog.getWindow().setAttributes(lp) } }
Comments
Post a Comment