Android Architecture Components(AAC)首次發佈與2017 GoogleI/O大會,通過近一年的維護,如今Google團隊已經發布了穩定版(v1.1.1)。可以更好的幫助咱們來構建本身的App應用,若是你尚未了解ACC如今時間剛恰好,來不及解釋,趕忙上車吧。java
ACC是一個架構組件,它能幫忙咱們更好的來管理咱們的App,方便咱們的開發。它能幫助咱們的App更好的存儲數據、管理生命週期、進行模塊化、避免常見的錯誤、減小樣板文件的編寫。android
ACC主要由4個單一組件組成,分別爲:Room、LiveData、Lifecycle與ViewModel。它們每個都是獨立存在的組件,咱們能夠單獨使用其中幾個,又或者能夠將它們所有整合到一塊兒。因此對於ACC它提供了更好的使用靈活性,方便咱們集成到咱們的App中。git
今天主要是對ACC其中的Room組件進行分析。Room是一個穩健的SQL對象映射庫,用來幫助咱們快速的實現數據本地存儲。至於爲什麼要使用本地數據庫,天然是當用戶無網絡或者網絡差的時候,可以更好的提升用戶對咱們App的體驗。github
在使用Room以前,咱們仍是要在項目中對其進行依賴添加。
首先在你的項目的根目錄下的build.gradle中添加google()庫,代碼以下:sql
allprojects { repositories { jcenter() google() } }
以後打開你的App或者module中的build.gradle文件,在dependencies中添加以下代碼:數據庫
dependencies { def room_version = "1.1.0" // or, for latest rc, use "1.1.1-rc1" implementation "android.arch.persistence.room:runtime:$room_version" annotationProcessor "android.arch.persistence.room:compiler:$room_version" // optional - RxJava support for Room implementation "android.arch.persistence.room:rxjava2:$room_version" // optional - Guava support for Room, including Optional and ListenableFuture implementation "android.arch.persistence.room:guava:$room_version" // Test helpers testImplementation "android.arch.persistence.room:testing:$room_version" }
上面的依賴添加完成後,接下來咱們能夠正式使用Room。在Android App中進行本地數據的存儲都是使用SQLite,當咱們使用原生的SQLite進行本地數據庫的編寫時,咱們不只要定義數據庫結構,還要建立SQLiteHelper,編寫一連串的SQL語句。這樣代碼量與複雜度不斷上升,這不是咱們想要的。而Room正好能夠幫助咱們減小代碼、簡化複雜度。segmentfault
對於Room的使用主要由三部分構成:api
以上三部分在代碼中都是經過註釋來實現,從而達到代碼的精簡。數組
Entity做用在model上,即咱們與數據表中的字段所匹配的model類。如今咱們來創建一個聯繫人相關的model,對於正常的model創建以下:網絡
data class ContactsModel(val id: Int, val name: String, val phone: String)
如今咱們要把ContactsModel映射到數據庫中的一種表,只需進行以下操做:
@Entity(tableName = "contacts") data class ContactsModel( @PrimaryKey @ColumnInfo(name = "contacts_id") val id: Int, @ColumnInfo(name = "name") val name: String, @ColumnInfo(name = "phone") val phone: String )
首先咱們在ContactsModel中添加@Entity註釋,代表它將映射成一種表。在Entity中能夠經過使用tableName來爲該表命名,這裏將其命名未contacts。
除此以外,使用@ColumnInfo來標明表中的字段,@PrimaryKey來標明表的主鍵。其中@ColumnInfo也能夠經過(name = "name")來命名字段名。固然還有別的註釋例如外鍵的標明:@ForeignKey
數據庫表建好了,如今是提供操做數據表中的數據的方法。
@Dao interface ContactsDao { @Query("SELECT * FROM contacts") fun getAllContacts(): List<ContactsModel> @Query("SELECT * FROM contacts WHERE contacts_id = :id") fun getContactsById(id: Int): LiveData<ContactsModel> @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertContacts(contactsModel: ContactsModel) @Query("UPDATE contacts SET name = :name AND phone = :phone WHERE contacts_id = :id") fun updateContacts(id: Int, name: String, phone: String) @Query("DELETE FROM contacts WHERE contacts_id = :id") fun deleteContacts(id: Int)
這裏咱們只需建立一個接口,經過@Dao來標明它是提供操做數據表的方法集。要注意它必須爲interface,在接口中咱們只需定義接口方法便可。與日常的接口方法定義不一樣的是,咱們必須在每個接口方法上經過註釋來標明該方法的做用。
例如getAllContacts()方法,咱們爲了讓它實現獲取contacts表中的全部數據,咱們須要在其方法中添加@Query註釋,因爲是查詢方法,天然是使用Query,若是是插入方法就是Insert(第三個方法)。其次()中的內容就是正常的查詢語句。這裏是獲取全部的Contacts,因此咱們使用
@Query("SELECT * FROM contacts")
對於有參數的sql語句編寫,能夠查看第二個方法,參數值只需在對應的方法參數名前加入:前綴,這就是傳參的格式。
@Query("SELECT * FROM contacts WHERE contacts_id = :id") fun getContactsById(id: Int): LiveData<ContactsModel>
Room就是這麼簡單,經過定義接口與接口方法的形式,再結合註釋來簡化代碼量與複雜度。固然最終Room會根據註釋,編譯器會幫咱們實現這些接口方法。咱們能夠build項目,而後咱們就能夠搜索到ContactsDao_Impl類,這個讀者能夠自行嘗試。本質是ContactsDao_Impl實現了ContactsDao接口。
Room的強大之一是:它能夠在編譯時檢測你的SQL語句是否編寫正確,若是編寫錯誤將致使編譯失敗。這樣就能夠避免App在運行時致使崩潰。這個讀者能夠自行測試一下。
如今數據表有了,對錶的操做方法也有了,最後就差數據庫來保存各個數據表了。Talk is cheap. Show me the code。
@Database(entities = arrayOf(ContactsModel::class), version = 1) abstract class ContactsDataBase : RoomDatabase() { abstract fun contactsDao(): ContactsDao companion object { private var instance: ContactsDataBase? = null fun getInstance(context: Context): ContactsDataBase { if (instance == null) { instance = Room.databaseBuilder(context.applicationContext, ContactsDataBase::class.java, "contacts.db").build() } return instance as ContactsDataBase } } }
沒錯,仍是使用註釋,這裏咱們定義ContactsDataBase抽象類,讓它繼承RoomDatabase抽象類。固然也是同@Database來標明它是一個數據庫。它接收兩個參數,分別爲entities與version,前者接收的類型是Class[]數組,內容爲對於表的Class;後者是int的數據庫版本號。
在ContactsDataBase中還需定義一個抽象方法,讓它返回由@Dao註釋的ContactsDao,即提供獲取數據表的方法。本質的爲數據庫暴露操做數據表的入口。至於它的具體方法實現也能夠經過build來查看對應的自動生成文件ContactsDataBase_Impl類。
由於contactsDao是數據庫的惟一入口,避免每次對數據庫進行操做時都建立ContactsDataBase實例,如上代碼咱們可使用單例模式來減小實例頻繁建立的開銷。
通過上面的Entity、DAO與Database的建立,如今咱們已經有了完整的本地數據庫結構。接下來咱們來看史上最簡數據庫使用的調用代碼:
private val mContactsDao by lazy { ContactsDataBase.getInstance(application).contactsDao() } fun getContactsById(id: Int): LiveData<ContactsModel> = mContactsDao.getContactsById(id)
你沒看錯只需兩行代碼,咱們就能獲取數據庫中Contacts表中的所用數據。
第一行代碼咱們獲取了ContactsDao實例,該實例包含操做數據表的因此方法。而第二行代碼就是調用ContactsDao中的操做方法。返回咱們所需的數據。
在第二行代碼,細心的大家可能會發現它返回了LiveData<ContactsModel>類型數據。它是ACC的另外一強大組件,這也是Room的另外一強大之處,它能夠直接返回LiveData數據類型,完美與LiveData結合。至於LiveData的做用,敬請關注下一篇文章Android Architecture Components Part2:LiveData
若是你的App使用了Room,那麼你的App本地數據獲取架構將會是這樣的
最後文章中的代碼均可以在Github中獲取到。使用時請將分支切換到feat_architecture_components
Android Architecture Components Part2:LiveData
Android Architecture Components Part3:Lifecycle
Android Architecture Components Part4:ViewModel