在多个片段中使用 Room 数据库
Using Room database in multiple fragments
我有 HomeActivity
activity,其中有底部导航栏和 5 个片段。我想在所有这些片段中使用 RoomDatabase
。我该如何实施?
首页活动代码:
public class HomeActivity extends AppCompatActivity {
TextView tvDailyGoalValue;
SharedPreferences sharedPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
//layout setting
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
BottomNavigationView navView = findViewById(R.id.nav_view);
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_home, R.id.navigation_profile, R.id.navigation_notifications)
.build();
NavController navController = Navigation.findNavController(this, R.id.fragment3);
NavigationUI.setupWithNavController(navView, navController);
sharedPreferences = getSharedPreferences("MyPrefs", MODE_PRIVATE);
RoomDB roomDB = Room.databaseBuilder(this, RoomDB.class,"dripDB").build();
}
}
您应该创建一个全局可访问的 RoomDB 对象实例。 Room 文档甚至这样说:
If your app runs in a single process, you should follow the singleton design pattern when instantiating an AppDatabase object. Each RoomDatabase instance is fairly expensive, and you rarely need access to multiple instances within a single process.
您可以通过多种方式在多个 Activity 和 Fragment 之间共享单个对象,例如:
- 使用 Dagger 并配置一个
@Singleton
,您可以在所有相关活动和片段中 @Inject
。如果您是第一次设置 Dagger,设置起来会有些复杂,但这是专业应用程序中的通常选择。
- 使用经典的 Java 单例设计模式并将
RoomDB
转换为单例。
- 使用自定义
Application
子类,将 RoomDB
实例放在那里并将其公开给任何 Context
,例如((YourCustomApplication)context.getApplication()).getRoomDB()
我认为您最好的选择是 1) 可扩展性或 2) 简单性。
我并不是说使用 Android Room is or should be complicated but, definitely not as simple as you think it is; looking to your example. Android developers spend a lot of time trying to learn how to use Android Architecture Components and Dagger 2 可以使他们的应用程序尽可能好,顺便说一下,Google 推荐的 API 可以与 [=51] 一起使用=] 房间。不过,我还是要回答你的问题。
1.实体
决定您要在数据库中使用的实体。假设我们这里只有一个实体。类似于:
@Entity
class User constructor(
@ColumnInfo(name = "first_name")
var firstName: String,
@ColumnInfo(name = "last_name")
var lastName: String
@PrimaryKey(autoGenerate = true)
var id: Int = 0,
)
2。 DAO class
像这样创建你的 DAO class:
/**
* Provides the abstraction API for accessing data.
*/
@Dao
interface AppDao {
/**
* Inserts a new [User].
* @param user The user to insert.
*/
@Insert
suspend fun insertUser(user: User)
/**
* Updates the specified [User].
* @param user The user to update.
*/
@Update
suspend fun updateUser(user: User)
/**
* Deletes the specified [User] from the database.
* @param user The user to delete.
*/
@Delete
suspend fun deleteUser(user: User)
}
3。数据库 class
在单独的 class 中创建您的数据库。 (例如 AppDatabase.java)
这是一个好的数据库 class 的样子:
@Database(entities = [User::class], version = AppDatabase.VERSION)
abstract class AppDatabase : RoomDatabase() {
/**
* Returns the DAO for this application.
* @return The DAO for this application.
*/
abstract fun getAppDao(): AppDao
companion object {
private const val TAG = "AppDatabase"
const val VERSION = 1
private const val DATABASE_NAME = "inventory_database.db"
@Volatile
private var instance: AppDatabase? = null
/**
* Gets and returns the database instance if exists; otherwise, builds a new database.
* @param context The context to access the application context.
* @return The database instance.
*/
fun getInstance(context: Context): AppDatabase =
instance ?: synchronized(this) {
instance ?: buildDatabase(context).also { instance = it }
}
/**
* Creates and returns the callback object to execute when the database is first created.
* @return The callback object to execute when the database is first created.
*/
private fun appDatabaseCallback(): Callback = object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
Log.d(TAG, "Database has been created.")
// Throws exception
CoroutineScope(Dispatchers.IO).launch {
instance?.getAppDao()?.let { populateDbAsync(it) }
}
}
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
Log.d(TAG, "Database has been opened.")
}
}
/**
* Builds and returns the database.
* @param appContext The application context to reference.
* @return The built database.
*/
private fun buildDatabase(appContext: Context): AppDatabase {
val filesDir = appContext.getExternalFilesDir(null)
val dataDir = File(filesDir, "data")
if (!dataDir.exists())
dataDir.mkdir()
val builder =
Room.databaseBuilder(
appContext,
AppDatabase::class.java,
File(dataDir, DATABASE_NAME).toString()
).fallbackToDestructiveMigration()
// Execute the callback only in DEBUG mode.
if (BuildConfig.DEBUG) {
builder.addCallback(appDatabaseCallback())
}
return builder.build()
}
/**
* Populates the database when it is first created, as a suspended operation.
* @param appDao The application DAO to execute queries.
*/
private suspend fun populateDbAsync(appDao: AppDao) {
withContext(Dispatchers.IO) {
// Populate your database here...
}
}
}
}
4.活动与片段
- Activity
class MainActivity : AppCompatActivity() {
private lateinit var dao: AppDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Initialize the DAO..
dao = AppDatabase.getInstance(requireContext()).getAppDao()
}
// An example of how to execute a dao method in an activity (Again, not recommended)
private fun insertUser(firstName: String, lastName: String) {
val user = User(firstName, lastName)
lifecycleScope.launch {
dao.insertUser(user)
}
}
// The rest of the code here...
}
您可以对所有活动执行相同的操作。
- 片段
class ExampleFragment : Fragment(R.layout.fragment_example) {
private lateinit var dao: AppDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize the DAO..
dao = AppDatabase.getInstance(requireContext()).getAppDao()
}
// An example of how to execute a dao method in a fragment (Again, not recommended)
private fun insertUser(firstName: String, lastName: String) {
val user = User(firstName, lastName)
lifecycleScope.launch {
dao.insertUser(user)
}
}
// The rest of the code here...
}
您可以对所有片段执行相同的操作。
我有 HomeActivity
activity,其中有底部导航栏和 5 个片段。我想在所有这些片段中使用 RoomDatabase
。我该如何实施?
首页活动代码:
public class HomeActivity extends AppCompatActivity {
TextView tvDailyGoalValue;
SharedPreferences sharedPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
//layout setting
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
BottomNavigationView navView = findViewById(R.id.nav_view);
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_home, R.id.navigation_profile, R.id.navigation_notifications)
.build();
NavController navController = Navigation.findNavController(this, R.id.fragment3);
NavigationUI.setupWithNavController(navView, navController);
sharedPreferences = getSharedPreferences("MyPrefs", MODE_PRIVATE);
RoomDB roomDB = Room.databaseBuilder(this, RoomDB.class,"dripDB").build();
}
}
您应该创建一个全局可访问的 RoomDB 对象实例。 Room 文档甚至这样说:
If your app runs in a single process, you should follow the singleton design pattern when instantiating an AppDatabase object. Each RoomDatabase instance is fairly expensive, and you rarely need access to multiple instances within a single process.
您可以通过多种方式在多个 Activity 和 Fragment 之间共享单个对象,例如:
- 使用 Dagger 并配置一个
@Singleton
,您可以在所有相关活动和片段中@Inject
。如果您是第一次设置 Dagger,设置起来会有些复杂,但这是专业应用程序中的通常选择。 - 使用经典的 Java 单例设计模式并将
RoomDB
转换为单例。 - 使用自定义
Application
子类,将RoomDB
实例放在那里并将其公开给任何Context
,例如((YourCustomApplication)context.getApplication()).getRoomDB()
我认为您最好的选择是 1) 可扩展性或 2) 简单性。
我并不是说使用 Android Room is or should be complicated but, definitely not as simple as you think it is; looking to your example. Android developers spend a lot of time trying to learn how to use Android Architecture Components and Dagger 2 可以使他们的应用程序尽可能好,顺便说一下,Google 推荐的 API 可以与 [=51] 一起使用=] 房间。不过,我还是要回答你的问题。
1.实体
决定您要在数据库中使用的实体。假设我们这里只有一个实体。类似于:
@Entity
class User constructor(
@ColumnInfo(name = "first_name")
var firstName: String,
@ColumnInfo(name = "last_name")
var lastName: String
@PrimaryKey(autoGenerate = true)
var id: Int = 0,
)
2。 DAO class
像这样创建你的 DAO class:
/**
* Provides the abstraction API for accessing data.
*/
@Dao
interface AppDao {
/**
* Inserts a new [User].
* @param user The user to insert.
*/
@Insert
suspend fun insertUser(user: User)
/**
* Updates the specified [User].
* @param user The user to update.
*/
@Update
suspend fun updateUser(user: User)
/**
* Deletes the specified [User] from the database.
* @param user The user to delete.
*/
@Delete
suspend fun deleteUser(user: User)
}
3。数据库 class
在单独的 class 中创建您的数据库。 (例如 AppDatabase.java)
这是一个好的数据库 class 的样子:
@Database(entities = [User::class], version = AppDatabase.VERSION)
abstract class AppDatabase : RoomDatabase() {
/**
* Returns the DAO for this application.
* @return The DAO for this application.
*/
abstract fun getAppDao(): AppDao
companion object {
private const val TAG = "AppDatabase"
const val VERSION = 1
private const val DATABASE_NAME = "inventory_database.db"
@Volatile
private var instance: AppDatabase? = null
/**
* Gets and returns the database instance if exists; otherwise, builds a new database.
* @param context The context to access the application context.
* @return The database instance.
*/
fun getInstance(context: Context): AppDatabase =
instance ?: synchronized(this) {
instance ?: buildDatabase(context).also { instance = it }
}
/**
* Creates and returns the callback object to execute when the database is first created.
* @return The callback object to execute when the database is first created.
*/
private fun appDatabaseCallback(): Callback = object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
Log.d(TAG, "Database has been created.")
// Throws exception
CoroutineScope(Dispatchers.IO).launch {
instance?.getAppDao()?.let { populateDbAsync(it) }
}
}
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
Log.d(TAG, "Database has been opened.")
}
}
/**
* Builds and returns the database.
* @param appContext The application context to reference.
* @return The built database.
*/
private fun buildDatabase(appContext: Context): AppDatabase {
val filesDir = appContext.getExternalFilesDir(null)
val dataDir = File(filesDir, "data")
if (!dataDir.exists())
dataDir.mkdir()
val builder =
Room.databaseBuilder(
appContext,
AppDatabase::class.java,
File(dataDir, DATABASE_NAME).toString()
).fallbackToDestructiveMigration()
// Execute the callback only in DEBUG mode.
if (BuildConfig.DEBUG) {
builder.addCallback(appDatabaseCallback())
}
return builder.build()
}
/**
* Populates the database when it is first created, as a suspended operation.
* @param appDao The application DAO to execute queries.
*/
private suspend fun populateDbAsync(appDao: AppDao) {
withContext(Dispatchers.IO) {
// Populate your database here...
}
}
}
}
4.活动与片段
- Activity
class MainActivity : AppCompatActivity() {
private lateinit var dao: AppDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Initialize the DAO..
dao = AppDatabase.getInstance(requireContext()).getAppDao()
}
// An example of how to execute a dao method in an activity (Again, not recommended)
private fun insertUser(firstName: String, lastName: String) {
val user = User(firstName, lastName)
lifecycleScope.launch {
dao.insertUser(user)
}
}
// The rest of the code here...
}
您可以对所有活动执行相同的操作。
- 片段
class ExampleFragment : Fragment(R.layout.fragment_example) {
private lateinit var dao: AppDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize the DAO..
dao = AppDatabase.getInstance(requireContext()).getAppDao()
}
// An example of how to execute a dao method in a fragment (Again, not recommended)
private fun insertUser(firstName: String, lastName: String) {
val user = User(firstName, lastName)
lifecycleScope.launch {
dao.insertUser(user)
}
}
// The rest of the code here...
}
您可以对所有片段执行相同的操作。