Android Room-避免将上下文传递给Singleton

时间:2018-12-06 14:53:53

标签: android singleton android-room android-jetpack

我正在尝试将项目迁移到Android Room。阅读Android Room文档后,我发现Singleton适合访问我的数据库。

Android Developer的报价:

  

注意:如果您的应用程序在单个进程中运行,则在实例化AppDatabase对象时应遵循单例设计模式。每个RoomDatabase实例都相当昂贵,您几乎不需要在单个进程中访问多个实例。

我编写了以下代码:

@Database(entities = {Category.class, News.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {

    private static final String DB_NAME = "database.db";
    private static AppDatabase instance;

    public abstract CategoryDao categoryDao();
    public abstract NewsDao newsDao();

    private AppDatabase () {}

    public static AppDatabase getInstance(Context context) {
        if (instance == null) {
            synchronized (AppDatabase.class) {
                if (instance == null) {
                    instance = Room.databaseBuilder(context.getApplicationContext(),
                            AppDatabase.class, DB_NAME).build();
                }
            }
        }
        return instance;
    }
}

一个简单的双重检查锁定Singleton。

我已经阅读了一些指南/教程,几乎每个人都采用了类似的方法,但是我看到这种方法存在一些问题:

  • 甚至每次只需要一次通过一次Context即可初始化Singleton。
  • 如果我需要在没有Context的情况下访问数据库怎么办?
  • 甚至可以将参数发送给Singleton吗?

有什么想法可以实现解决这些问题的Room Database Singleton?

如果可能,我想避免使用Dagger2之类的DI库。

5 个答案:

答案 0 :(得分:1)

您可以初始化数据库并将其实例保存到Application类中。

public class MyApplication extends Application {

    public AppDatabase database;

    @Override
    public void onCreate() {
        super.onCreate();

        database = AppDatabase.getInstance(this)
    }
}

您可以像

一样访问您的参考资料
((MyApplication)activity).dabase

希望这会有所帮助。

答案 1 :(得分:0)

@Database(entities = {Category.class, News.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {

   private static final String DB_NAME = "database.db";
   private static AppDatabase instance;
   public abstract CategoryDao categoryDao();
   public abstract NewsDao newsDao();

   private AppDatabase () {}

   // Use this to call on any place
   public static AppDatabase getInstance(Context context) {
      return instance;
   }
   // Use once to Create and setup the object
   public static AppDatabase setInstance(Context context) {
      if (instance == null) {
         synchronized (AppDatabase.class) {
            if (instance == null) {
                instance = Room.databaseBuilder(context.getApplicationContext(),
                        AppDatabase.class, DB_NAME).build();
            }
        }
     }
     return instance;
   }
}

// Second you need to set instance on Application Class which create and make your DB 
  //Ready for Use Before anything perform
public class MyApplication extends Application {
   @Override
   public void onCreate() {
      super.onCreate();
      AppDatabase.setInstance(this)
   }
}

要使用

//Need to call
AppDatabase.getInstance().categoryDao();

答案 2 :(得分:0)

您可以实例化数据库,然后锁定实例。

此外,调用context.applicationContext返回当前进程的单个全局Application 对象的上下文。此Context的生命周期与当前context是分开的,这与进程的生命周期(而不是当前组件)息息相关。

/**
 * The Room Database that contains the item table.
 */
@Database(entities = arrayOf(Item::class), version = 1, exportSchema = false)
abstract class ItemDb : RoomDatabase() {
    abstract fun itemDao(): ItemDao

    companion object {
        private var INSTANCE: ItemDb? = null

        private val lock = Any()

        @JvmStatic
        fun getInstance(context: Context): ItemDb {

            // When calling this instance concurrently from multiple threads we're in serious trouble:
            // So we use this method, 'synchronized' to lock the instance
            synchronized(lock) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.applicationContext, ItemDb::class.java, "items.db").build()
                }
                return INSTANCE!!
            }
        }
    }
}

答案 3 :(得分:0)

这里有很多事情要考虑。这完全取决于您的用例-因为您可能需要让您的应用仅在单线程上触摸数据库,所以您根本不需要在那里进行任何同步。

但是,如果您需要一些补充解决方案(在上述情况下也可能称其为“过大杀伤力”),请选中此处粘贴的https://github.com/android/architecture-components-samples仅供参考(有关此方法的讨论是here

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.observability.persistence

import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import android.content.Context

/**
 * The Room database that contains the Users table
 */
@Database(entities = [User::class], version = 1)
abstract class UsersDatabase : RoomDatabase() {

    abstract fun userDao(): UserDao

    companion object {

        @Volatile private var INSTANCE: UsersDatabase? = null

        fun getInstance(context: Context): UsersDatabase =
                INSTANCE ?: synchronized(this) {
                    INSTANCE ?: buildDatabase(context).also { INSTANCE = it }
                }

        private fun buildDatabase(context: Context) =
                Room.databaseBuilder(context.applicationContext,
                        UsersDatabase::class.java, "Sample.db")
                        .build()
    }
}

通常-您可能想看看Kotlin double checking singleton和/或Java Double-Checked Locking with Singleton

答案 4 :(得分:0)

你可以使用这个功能

public class DatabaseClient {

private Context mCtx;
private static DatabaseClient mInstance;

//our app database object
private final AppDatabase appDatabase;
private DatabaseClient(Context mCtx) {
    this.mCtx = mCtx;

    //creating the app database with Room database builder
    //alldata is the name of the database
    appDatabase = Room.databaseBuilder(mCtx, AppDatabase.class, "alldata").build();
}

public static synchronized DatabaseClient getInstance(Context mCtx) {
    if (mInstance == null) {
        mInstance = new DatabaseClient(mCtx);
    }
    return mInstance;
}

public AppDatabase getAppDatabase() { return appDatabase; }

}

并使用它来调用 getInstance()

DatabaseClient.getInstance(MainActivity.this).getAppDatabase().memberDao();

AppDatabase 类如下所示:

@Database(entities = {Member.class} ,  version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract MemberDao memberDao();
}