簡介: 還在使用原生的sqllite?有這麼清爽且穩如狗的room為啥不用呢? Room是Google官方推薦使用的資料庫,相比較某些優秀資料庫框架來說,不用過於擔心某天庫會停止維護,且訪問資料庫非常流暢,並且提供了與常規的ORM框架一樣,通過添加編譯期註解來進行表和欄位的配置,譬如@Databas ...
簡介:
還在使用原生的sqllite?有這麼清爽且穩如狗的room為啥不用呢?
Room是Google官方推薦使用的資料庫,相比較某些優秀資料庫框架來說,不用過於擔心某天庫會停止維護,且訪問資料庫非常流暢,並且提供了與常規的ORM框架一樣,通過添加編譯期註解來進行表和欄位的配置,譬如@Database、@Dao、@Entity、@Query、@Insert、@Update、@Detele等的註解,可以使用簡單代碼實現相比以前SQLite更複雜的代碼的效果,這點兒有點兒類似於java世界里的mybatis。總而言之, Room功能強大,速度和穩定性不弱,還簡單易用,算得上是一個優秀的資料庫。
PS:
Demo以kotlin代碼為例,kotlin最近剛在學O.o?java應該也是類似的。插入代碼時沒有看到kotlin語言選項,代碼標識可能顯示js,複製代碼註意包名~
Demo展示效果:
Demo案例參考官網教程:
kotlin官網教程鏈接:https://developer.android.com/codelabs/android-room-with-a-view-kotlin?hl=zh-cn#0
這個鏈接可能需要神奇的力量,也可以使用梯子打開新世界的大門~
架構組件:
LiveData、ViewModel 和 Room
- LiveData:一種可監測的數據存儲器類。務必保存/緩存最新版本的數據,併在數據發生變化時通知其監測者。LiveData 具有生命周期感知能力。界面組件只是監測相關數據,不會停止或恢復監測。LiveData 將自動管理所有這些操作,因為它在監測時可以感知相關的生命周期狀態變化。
- ViewModel:充當存儲庫(數據)和界面之間的通信中心。對於界面而言,數據來源不再是一個需要關註的問題。ViewModel 實例在重新創建 activity/fragment 後仍然存在。
- 存儲庫:您創建的類,主要用於管理多個數據源。
- 實體:使用 Room 時用於描述資料庫表的帶註解的類。
- Room 資料庫:可簡化資料庫工作,並充當 SQLite 底層資料庫的接入點(隱藏 SQLiteOpenHelper)。它使用 DAO 向 SQLite 資料庫發出查詢請求。
- SQLite 資料庫:設備上的存儲空間。Room 持久性庫會為您創建和維護此資料庫。
- DAO:數據訪問對象。從 SQL 查詢到函數的映射。在使用 DAO 時,您需要調用相應方法,其餘操作均由 Room 完成。
依賴添加:
在app(應用模塊)下的build.gradle(app)中添加apply plugin: 'kotlin-kapt' dependencies { // ... 省略無關 // room implementation "androidx.room:room-runtime:$rootProject.roomVersion" implementation "androidx.room:room-ktx:$rootProject.roomVersion" kapt "androidx.room:room-compiler:$rootProject.roomVersion" androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion" }在項目下的build.gradle(project)中添加
ext { roomVersion = '2.1.0-alpha06' //... 省略無關 }
項目結構:
使用步驟:
1.創建entity 創建一個Kotlin類,用於表示資料庫中的一個表,表名為"word_table"。import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey @Entity(tableName = "word_table") class Word(@PrimaryKey @ColumnInfo(name = "word") val word: String)
@Entity(tableName = "word_table"): 這是一個註解,用於標識這個Kotlin類是一個Room資料庫實體。tableName參數指定了在資料庫中對應的表名為"word_table"。Room是一個Android持久性庫,用於簡化資料庫訪問,併在編譯時進行SQL查詢驗證。
class Word: 這是一個Kotlin類的聲明,類名為Word,用於表示資料庫中的一個條目。通常,一個Entity類會映射到資料庫中的一張表,每個實例則代表表中的一行數據。
@PrimaryKey @ColumnInfo(name = "word") val word: String: 這是Word類的一個屬性,表示資料庫表中的一個列。這個屬性被@PrimaryKey註解標記,表示它是表中的主鍵。主鍵是唯一標識資料庫表中每一行的欄位。@ColumnInfo(name = "word")指定了這個屬性在資料庫表中對應的列名為"word"。val word: String定義了一個名為word的不可變屬性,類型為String,用於存儲單詞的值。
這段代碼定義了一個名為Word的資料庫實體類,它映射到資料庫中的"word_table"表,包含一個名為"word"的主鍵列,用於存儲單詞的字元串值。
2.創建Dao
創建一個Kotlin介面,用於定義訪問資料庫中word_table表的操作
1 import androidx.room.Dao 2 import androidx.room.Insert 3 import androidx.room.OnConflictStrategy 4 import androidx.room.Query 5 import com.example.roomwordsample.datac.Word 6 import kotlinx.coroutines.flow.Flow 7 8 @Dao 9 interface WordDao { 10 @Query("select * from word_table order by word asc") 11 fun getAlphabetizedWords(): Flow<List<Word>> 12 13 @Insert(onConflict = OnConflictStrategy.IGNORE) 14 fun insert(word:Word) 15 16 @Query("DELETE FROM word_table") 17 fun deleteAll() 18 }
@Dao: 這是一個註解,標識這個Kotlin介面是一個Room資料庫訪問對象(DAO)。DAO(Data Access Object)用於定義資料庫操作的方法。
interface WordDao: 這是一個Kotlin介面的聲明,用於定義與單詞表相關的資料庫操作方法。
@Query("select * from word_table order by word asc"): 這是一個註解,用於標識一個查詢操作。在這裡,它指定了一個SQL查詢,從"word_table"表中選擇所有列,並按照"word"列的升序排列。這個方法返回一個Flow<List<Word>>對象,表示查詢結果以流的形式返回,其中每個元素都是一個Word對象的列表。
fun getAlphabetizedWords(): Flow<List<Word>>: 這是一個抽象方法,用於執行查詢操作,並返回結果。
@Insert(onConflict = OnConflictStrategy.IGNORE): 這是一個註解,用於標識一個插入操作。onConflict = OnConflictStrategy.IGNORE指定了當插入的數據與現有數據發生衝突時,忽略新數據。這個方法用於將一個Word對象插入到資料庫中。
fun insert(word: Word): 這是一個抽象方法,用於執行插入操作。
@Query("DELETE FROM word_table"): 這是一個註解,用於標識一個刪除操作。它指定了一個SQL刪除語句,從"word_table"表中刪除所有數據。
fun deleteAll(): 這是一個抽象方法,用於執行刪除操作。
這個介面定義了三種操作:獲取按字母順序排列的單詞列表、插入單詞到資料庫中以及刪除資料庫中所有的單詞。
3.創建RoomDatabase 定義一個用於創建和管理資料庫的類,主要使用了Room Persistence Library。1 import android.content.Context 2 import androidx.room.Database 3 import androidx.room.Room 4 import androidx.room.RoomDatabase 5 import androidx.sqlite.db.SupportSQLiteDatabase 6 import com.example.roomwordsample.dao.WordDao 7 import com.example.roomwordsample.datac.Word 8 import kotlinx.coroutines.CoroutineScope 9 import kotlinx.coroutines.launch 10 11 @Database(entities = arrayOf(Word::class), version = 1, exportSchema = false) 12 public abstract class WordRoomDatabase :RoomDatabase(){ 13 abstract fun wordDao(): WordDao 14 15 private class WordDatabaseCallback( 16 private val scope: CoroutineScope 17 ) : RoomDatabase.Callback() { 18 19 override fun onCreate(db: SupportSQLiteDatabase) { 20 super.onCreate(db) 21 INSTANCE?.let { database -> 22 scope.launch { 23 populateDatabase(database.wordDao()) 24 } 25 } 26 } 27 28 suspend fun populateDatabase(wordDao: WordDao) { 29 // Delete all content here. 30 wordDao.deleteAll() 31 32 // Add sample words. 33 var word = Word("Hello") 34 wordDao.insert(word) 35 word = Word("World!") 36 wordDao.insert(word) 37 38 // TODO: Add your own words! 39 } 40 } 41 42 companion object { 43 // Singleton prevents multiple instances of database opening at the 44 // same time. 45 @Volatile 46 private var INSTANCE: WordRoomDatabase? = null 47 48 fun getDatabase( 49 context: Context, 50 scope: CoroutineScope 51 ): WordRoomDatabase { 52 return INSTANCE ?: synchronized(this) { 53 val instance = Room.databaseBuilder( 54 context.applicationContext, 55 WordRoomDatabase::class.java, 56 "word_database" 57 ) 58 .addCallback(WordDatabaseCallback(scope)) 59 .allowMainThreadQueries() 60 .build() 61 INSTANCE = instance 62 // return instance 63 instance 64 } 65 } 66 } 67 }
@Database(entities = arrayOf(Word::class), version = 1, exportSchema = false): 這是一個註解,用於標識這個類是一個Room資料庫類。entities參數指定了資料庫中的實體類,這裡只有一個Word類。version參數指定了資料庫的版本號,如果你更新了資料庫結構,需要增加版本號以執行相應的資料庫遷移。exportSchema參數指定是否導出資料庫的schema到文件中,這裡設置為false表示不導出。
public abstract class WordRoomDatabase : RoomDatabase(): 這是一個抽象類,繼承自RoomDatabase類,用於定義資料庫操作。它包含了一個抽象方法wordDao(),用於獲取WordDao介面的實例。
private class WordDatabaseCallback(...) : RoomDatabase.Callback() {...}: 這是一個內部類,繼承自RoomDatabase.Callback類,用於在資料庫被創建時執行一些操作。在這個例子中,它在資料庫創建時調用了populateDatabase()方法,用於初始化資料庫。
companion object {...}: 這是一個伴生對象,包含了一些靜態方法和屬性。其中INSTANCE是一個WordRoomDatabase的單例實例,使用了雙重校驗鎖來確保線程安全。
fun getDatabase(...): 這是一個靜態方法,用於獲取資料庫實例。如果實例為空,則創建一個新的資料庫實例,併在創建時調用WordDatabaseCallback來初始化資料庫。
populateDatabase(wordDao: WordDao): 這是一個掛起函數,用於在資料庫創建時初始化資料庫內容。在這個例子中,它刪除了資料庫中的所有內容,然後插入了兩個示例單詞"Hello"和"World!",你也可以添加自己的單詞。
這個類負責創建和管理資料庫,定義了資料庫的版本、實體、以及初始化操作。
4.創建Repository
創建一個簡單的Repository類,用於連接ViewModel和DAO(Data Access Object)。
1 import androidx.annotation.WorkerThread 2 import com.example.roomwordsample.dao.WordDao 3 import com.example.roomwordsample.datac.Word 4 import kotlinx.coroutines.flow.Flow 5 6 class WordRepository(private val wordDao: WordDao){ 7 8 val allWords: Flow<List<Word>> = wordDao.getAlphabetizedWords() 9 10 @Suppress("RedundantSuspendModifier") 11 @WorkerThread 12 suspend fun insert(word: Word) { 13 wordDao.insert(word) 14 } 15 }
import語句: 這些是導入所需的類和包,包括androidx.annotation.WorkerThread、com.example.roomwordsample.dao.WordDao、com.example.roomwordsample.data.Word和kotlinx.coroutines.flow.Flow。這些類用於定義數據流以及DAO介面。
class WordRepository(private val wordDao: WordDao): 這是一個Kotlin類的聲明,表示WordRepository類。它包含一個私有屬性wordDao,類型為WordDao,用於對資料庫進行操作。
val allWords: Flow<List<Word>> = wordDao.getAlphabetizedWords(): 這是一個屬性,用於獲取從資料庫中獲取的所有單詞。它是一個Flow<List<Word>>類型的屬性,表示這是一個數據流,可以觀察到資料庫中單詞列表的變化。
@Suppress("RedundantSuspendModifier") @WorkerThread suspend fun insert(word: Word): 這是一個插入操作的掛起函數。@WorkerThread註解表示該函數應該在工作線程中執行,通常用於防止在主線程中執行長時間運行的操作。suspend關鍵字表示這是一個掛起函數,可以在協程中調用。
wordDao.insert(word): 這是調用WordDao介面的插入方法,將單詞插入到資料庫中。
這個Repository類負責從DAO獲取數據,並暴露一個數據流以供ViewModel觀察。它還包含一個方法用於在資料庫中插入新的單詞。
5.創建ViewModel
定義了一個ViewModel類(WordViewModel)以及一個用於創建ViewModel實例的工廠類(WordViewModelFactory)。
1 import androidx.lifecycle.LiveData 2 import androidx.lifecycle.ViewModel 3 import androidx.lifecycle.ViewModelProvider 4 import androidx.lifecycle.asLiveData 5 import androidx.lifecycle.viewModelScope 6 import com.example.roomwordsample.datac.Word 7 import com.example.roomwordsample.repository.WordRepository 8 import kotlinx.coroutines.launch 9 10 class WordViewModel(private val repository: WordRepository) : ViewModel() { 11 12 val allWords: LiveData<List<Word>> = repository.allWords.asLiveData() 13 14 /** 15 * Launching a new coroutine to insert the data in a non-blocking way 16 */ 17 fun insert(word: Word) = viewModelScope.launch { 18 repository.insert(word) 19 } 20 } 21 22 class WordViewModelFactory(private val repository: WordRepository) : ViewModelProvider.Factory { 23 override fun <T : ViewModel> create(modelClass: Class<T>): T { 24 if (modelClass.isAssignableFrom(WordViewModel::class.java)) { 25 @Suppress("UNCHECKED_CAST") 26 return WordViewModel(repository) as T 27 } 28 throw IllegalArgumentException("Unknown ViewModel class") 29 } 30 }
class WordViewModel(private val repository: WordRepository) : ViewModel() {...}:
WordViewModel是一個ViewModel類,用於管理與單詞相關的數據和用戶界面狀態。
它有一個名為repository的私有屬性,類型為WordRepository,用於與數據層交互。
allWords是一個LiveData對象,用於保存從數據層獲取的所有單詞列表。通過調用repository.allWords.asLiveData(),將數據流轉換為LiveData對象,以便在UI層觀察數據變化。
insert是一個公共方法,用於向數據層插入新的單詞。它啟動一個新的協程(coroutine),在非阻塞的方式下執行插入操作。
class WordViewModelFactory(private val repository: WordRepository) : ViewModelProvider.Factory {...}:
WordViewModelFactory是一個用於創建WordViewModel實例的工廠類。
它實現了ViewModelProvider.Factory介面,並重寫了create方法。
在create方法中,它首先檢查所請求的ViewModel類是否是WordViewModel,如果是,則返回一個新的WordViewModel實例,否則拋出一個IllegalArgumentException異常。
這些類的組合使得在應用程式中可以方便地管理與單詞相關的UI邏輯和數據。ViewModel類負責從數據層獲取數據,併在需要時將其提供給UI層。ViewModelFactory類負責實例化ViewModel類,並確保每個ViewModel都具有正確的依賴項。
6.創建Adapter
創建一個RecyclerView的適配器類(WordListAdapter),用於將數據綁定到RecyclerView中的列表項。
1 import android.view.LayoutInflater 2 import android.view.View 3 import android.view.ViewGroup 4 import android.widget.TextView 5 import androidx.recyclerview.widget.DiffUtil 6 import androidx.recyclerview.widget.ListAdapter 7 import androidx.recyclerview.widget.RecyclerView 8 import com.example.roomwordsample.R 9 import com.example.roomwordsample.datac.Word 10 11 class WordListAdapter : ListAdapter<Word, WordListAdapter.WordViewHolder>(WordsComparator()) { 12 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WordViewHolder { 13 return WordViewHolder.create(parent) 14 } 15 16 override fun onBindViewHolder(holder: WordViewHolder, position: Int) { 17 val current = getItem(position) 18 holder.bind(current.word) 19 } 20 class WordViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 21 private val wordItemView: TextView = itemView.findViewById(R.id.textView) 22 23 fun bind(text: String?) { 24 wordItemView.text = text 25 } 26 27 companion object { 28 fun create(parent: ViewGroup): WordViewHolder { 29 val view: View = LayoutInflater.from(parent.context) 30 .inflate(R.layout.recyclerview_item, parent, false) 31 return WordViewHolder(view) 32 } 33 } 34 } 35 36 class WordsComparator : DiffUtil.ItemCallback<Word>() { 37 override fun areItemsTheSame(oldItem: Word, newItem: Word): Boolean { 38 return oldItem === newItem 39 } 40 41 override fun areContentsTheSame(oldItem: Word, newItem: Word): Boolean { 42 return oldItem.word == newItem.word 43 } 44 } 45 }
class WordListAdapter : ListAdapter<Word, WordListAdapter.WordViewHolder>(WordsComparator()) {...}:
WordListAdapter繼承自ListAdapter類,並指定了泛型參數,第一個參數是數據項的類型(Word),第二個參數是ViewHolder的類型(WordViewHolder),並傳入了一個用於比較數據項的WordsComparator對象。
ListAdapter是RecyclerView的一個輔助類,用於處理列表數據的更新和差異計算。
onCreateViewHolder:
onCreateViewHolder方法用於創建ViewHolder對象。在這裡,它調用了WordViewHolder的create方法來創建ViewHolder。
onBindViewHolder:
onBindViewHolder方法用於綁定數據到ViewHolder上。在這裡,它獲取當前位置的數據項,然後調用ViewHolder的bind方法將數據綁定到UI上。
class WordViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {...}:
WordViewHolder是一個內部類,繼承自RecyclerView.ViewHolder類,用於管理列表項的視圖。
它包含一個私有屬性wordItemView,類型為TextView,用於顯示單詞。
bind方法:
bind方法用於將數據綁定到ViewHolder上。在這裡,它接收一個字元串參數,並將其設置到wordItemView的文本屬性上。
create方法:
create方法用於創建ViewHolder實例。在這裡,它使用LayoutInflater從指定的佈局文件(recyclerview_item.xml)中創建一個View對象,並將其傳遞給ViewHolder的構造函數。
WordsComparator類:
WordsComparator是一個內部類,繼承自DiffUtil.ItemCallback<Word>類,用於比較兩個數據項。
areItemsTheSame方法用於判斷兩個數據項是否代表同一個對象。
areContentsTheSame方法用於判斷兩個數據項的內容是否相同。
這個適配器類負責管理RecyclerView的列表項,包括創建ViewHolder、綁定數據以及比較數據項的差異。
7.創建MainActivity
1 import android.app.Activity 2 import android.content.Intent 3 import android.os.Bundle 4 import android.widget.Toast 5 import androidx.activity.viewModels 6 import androidx.appcompat.app.AppCompatActivity 7 import androidx.lifecycle.Observer 8 import androidx.recyclerview.widget.LinearLayoutManager 9 import androidx.recyclerview.widget.RecyclerView 10 import com.example.roomwordsample.adapter.WordListAdapter 11 import com.example.roomwordsample.datac.Word 12 import com.example.roomwordsample.vm.WordViewModel 13 import com.example.roomwordsample.vm.WordViewModelFactory 14 import com.google.android.material.floatingactionbutton.FloatingActionButton 15 16 17 class MainActivity : AppCompatActivity() { 18 private val newWordActivityRequestCode = 1 19 20 private val wordViewModel: WordViewModel by viewModels { 21 WordViewModelFactory((application as WordsApplication).repository) 22 } 23 24 override fun onCreate(savedInstanceState: Bundle?) { 25 super.onCreate(savedInstanceState) 26 setContentView(R.layout.activity_main) 27 val recyclerView = findViewById<RecyclerView>(R.id.recyclerview) 28 val adapter = WordListAdapter() 29 recyclerView.adapter = adapter 30 recyclerView.layoutManager = LinearLayoutManager(this) 31 wordViewModel.allWords.observe(this, Observer { words -> 32 words?.let { adapter.submitList(it) } 33 }) 34 val fab = findViewById<FloatingActionButton>(R.id.fab) 35 fab.setOnClickListener { 36 val intent = Intent(this@MainActivity, NewWordActivity::class.java) 37 startActivityForResult(intent, newWordActivityRequestCode) 38 } 39 } 40 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 41 super.onActivityResult(requestCode, resultCode, data) 42 43 if (requestCode == newWordActivityRequestCode && resultCode == Activity.RESULT_OK) { 44 data?.getStringExtra(NewWordActivity.EXTRA_REPLY)?.let { 45 val word = Word(it) 46 wordViewModel.insert(word) 47 } 48 } else { 49 Toast.makeText( 50 applicationContext, 51 R.string.empty_not_saved, 52 Toast.LENGTH_LONG).show() 53 } 54 } 55 }
8.創建NewWordActivity
1 import android.app.Activity 2 import android.content.Intent 3 import android.os.Bundle 4 import android.text.TextUtils 5 import android.widget.Button 6 import android.widget.EditText 7 import androidx.appcompat.app.AppCompatActivity 8 9 class NewWordActivity : AppCompatActivity() { 10 private lateinit var editWordView:EditText 11 12 override fun onCreate(savedInstanceState: Bundle?) { 13 super.onCreate(savedInstanceState) 14 setContentView(R.layout.activity_new_word) 15 editWordView = findViewById(R.id.edit_word) 16 val button = findViewById<Button>(R.id.button_save) 17 button.setOnClickListener { 18 val replyIntent = Intent() 19 if (TextUtils.isEmpty(editWordView.text)) { 20 setResult(Activity.RESULT_CANCELED, replyIntent) 21 } else { 22 val word = editWordView.text.toString() 23 replyIntent.putExtra(EXTRA_REPLY, word) 24 setResult(Activity.RESULT_OK, replyIntent) 25 } 26 finish() 27 } 28 } 29 companion object{ 30 const val EXTRA_REPLY = "com.example.android.wordlistsql.REPLY" 31 } 32 }
9.創建WordsApplication
創建一個名為WordsApplication的自定義Application類,用於初始化應用程式的全局狀態。
1 import android.app.Application 2 import com.example.roomwordsample.repository.WordRepository 3 import com.example.roomwordsample.room.WordRoomDatabase 4 import kotlinx.coroutines.CoroutineScope 5 import kotlinx.coroutines.SupervisorJob 6 7 class WordsApplication:Application() { 8 val applicationScope = CoroutineScope(SupervisorJob()) 9 val database by lazy { WordRoomDatabase.getDatabase(this,applicationScope) } 10 val repository by lazy { WordRepository(database.wordDao()) } 11 }
class WordsApplication : Application() {...}:
WordsApplication類繼承自Android的Application類,表示一個應用程式級別的全局狀態。
val applicationScope = CoroutineScope(SupervisorJob()):
applicationScope是一個CoroutineScope對象,用於管理應用程式範圍內的協程。它使用了SupervisorJob,表示在子協程出現異常時,不會影響其他子協程的執行。
val database by lazy { WordRoomDatabase.getDatabase(this, applicationScope) }:
database是一個延遲初始化的屬性,使用了懶載入機制。它通過調用WordRoomDatabase.getDatabase()方法來獲取應用程式的資料庫實例。這個資料庫實例是單例的,並且會在第一次訪問時被初始化。
val repository by lazy { WordRepository(database.wordDao()) }:
repository也是一個延遲初始化的屬性,使用了懶載入機制。它通過調用WordRepository類的構造函數來創建一個單詞倉庫的實例。這個倉庫實例會將資料庫實例傳遞給其構造函數,以便它可以與資料庫進行交互。
WordsApplication類負責創建應用程式的全局狀態,包括資料庫實例和單詞倉庫實例。這些實例可以在應用程式的任何地方使用,以便進行數據存儲和檢索操作。
查看room資料庫文件
room資料庫本質上就是對SQlite的一個封裝,類似於java中的mybatis框架,對SQLiteOpenHelper類進行了一些封裝,簡化了資料庫的操作。
簡單來說:Room是一個基於SQLite的強大資料庫框架。
在AndroidManifest.xml清單文件中添加許可權
1 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> 2 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
導出並使用SQLiteStudio軟體打開,其他資料庫管理工具也可以
SQLiteStudio下載鏈接:https://sqlitestudio.pl/
在AndroidStudio中打開Device Explorer(設備瀏覽器)
文件尾碼為.db或.sqlite都可以,這裡我直接將三個文件(二進位文件)導出
使用SQLite打開界面
本篇關於Android中使用Room數據的文章到這就結束了~
感謝觀看~~喜歡的小伙伴一鍵三連,後續可能會更新其他文章
參考其他鏈接:
Android的room資料庫使用小結(kotlin):https://cloud.tencent.com/developer/article/1831046