【问题标题】:How do I delay Android Room loading until after activity and prompts complete?如何将 Android Room 加载延迟到活动和提示完成后?
【发布时间】:2021-12-03 16:49:58
【问题描述】:

问题:我收到null 数据库名称错误。我的MainActiity.class 中的 Android Room 数据库调用继续执行,尽管提示让用户选择数据库名称。

我正在尝试做的事情:我还在学习Android,但我正在尝试在一个单独的应用程序中锻炼,使用大师Room数据库来管理使用应用程序使用的多个Room 数据库(这是一个沙盒类型的应用程序来发挥这个想法)。这个数据库管理功能运行良好,但事情是硬编码的。因此,我正处于希望用户能够通过使用sharedPreferences 和自定义alert 提示的步骤中,可以选择在安装第一个房间数据库时创建命名并在其后添加其他人。稍后添加它们的选项不是问题,因为将加载某些内容。但是,在初始应用程序启动时,我希望用户可以选择创建和命名第一个数据库,而不是创建默认数据库——这并不是什么大不了的事,但为什么用户有一个潜在的默认数据库从不使用。我什至可以开发一种重命名方法,我会这样做,但允许用户这样做似乎是有意义的。

我尝试过的:我尝试创建一些方法来封装和分离 DB 调用与提示,但代码仍然吹到 DB 代码。我对延迟Room 进行了一些搜索,但找不到任何特定的内容。我愿意接受他人的智慧。

编写代码,这只是一项活动,因为我在玩这个想法......并同时学习。

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "DB_INFO";
    SharedPreferences sharedPreferences;
    SharedPreferences.Editor settings;
    String databaseName;
    String prevDB;

    Button button;

    MasterDatabase masterDB;
    List<MasterDatabaseList> mdbList;
    ArrayList<BaseDatabase> bdbList = new ArrayList<>();

    // Current Database
    int currentBaseDBIndex = -1;
    BaseDatabase currentDB = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = findViewById(R.id.button);
        sharedPreferences = getSharedPreferences("AppSettings", MODE_PRIVATE);
        settings = sharedPreferences.edit();
        setupOnClickActions();
        startChecks();
        Toast.makeText(this, "database: " + databaseName + "\r\n" + "prevDB: " + prevDB, Toast.LENGTH_LONG).show();
    }

    private void startChecks(){
        if(isFirstTime()) {
            databaseName = PopupDialog.AlertInputBox(this, "Enter Research Project Name",
                    "Without spaces or special characters, enter a name for your research project.");
            settings.putString("database", databaseName);
            settings.commit();
            settings.apply();
            startDBs();
        }else{
            databaseName = PopupDialog.AlertInputBox(this, "Enter Research Project Name",
                    "Without spaces or special characters, enter a new or existing name for your research project.");
            settings.putString("prevDB", sharedPreferences.getString("database", ""));
            settings.putString("database", databaseName);
            settings.commit();
            settings.apply();
            prevDB = sharedPreferences.getString("prevDB", "");
            startDBs();
        }
    }

    private void startDBs(){
        masterDB = MasterDatabase.getInstance(this);
        mdbList = masterDB.getMasterDao().getAllDatabases();

        // Add a DB if none exists
        if(mdbList.size()<1){
            addBaseDB("sample.db");
        }
        setCurrentIndexDBandDao(databaseName); /* Add some data to db1 IF it exists (it should) --------------------- */

        if (currentBaseDBIndex > -1 && currentDB.getBaseDao().count() < 1) {
            currentDB.getBaseDao().insert(new BaseTable("Added " + databaseName + " ... etc."));

        }

        /* Extract and Log Data for ALL the BaseDatabase databases i.e. db1 and db2 */
        for(MasterDatabaseList masterdb: masterDB.getMasterDao().getAllDatabases()) {
            Log.d(TAG,"Database is " + masterdb.getDatabaseName());
            setCurrentIndexDBandDao(masterdb.getDatabaseName());
            if (currentBaseDBIndex > -1) {
                for(BaseTable bt: currentDB.getBaseDao().getAllBaseTables()) {
                    Log.d(TAG,"Extracted Base Table  row where MyData is" + bt.getMydata());
                }
            }
        }
    }


    // METHODS =========================================================================================================

    // Attempt to clear and reset SharedPreferences to a user first execution
    private void setupOnClickActions(){
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                settings.putBoolean("firstTime", false);
                settings.clear();
                settings.commit();
                settings.apply();
            }
        });
    }

    /* Add a new Database
         Note that it assumes that it will now be the current
         so the current values are set */
    private void addBaseDB(String baseDBName) {
        masterDB.getMasterDao().insert(new MasterDatabaseList(baseDBName));
    }

    /* Build/ReBuild the 3 Lists according to the master database*/
    private void buildBaseLists() {
        bdbList.clear();
        mdbList = masterDB.getMasterDao().getAllDatabases();
        // Loop through the databases defined in the master database adding the database and dao to the respective lists
        for (MasterDatabaseList masterDB: masterDB.getMasterDao().getAllDatabases()) {
            BaseDatabase baseDB = BaseDatabase.getInstance(this, masterDB.getDatabaseName());
            bdbList.add(baseDB);
        }
    }

    /* Set the currentDB according to the database name*/
    private void setCurrentIndexDBandDao(String baseDBName) {
        currentBaseDBIndex = getListIndexByBaseDBName(baseDBName);
        if(currentBaseDBIndex == -1) {
            addBaseDB(baseDBName);
            buildBaseLists();
            currentBaseDBIndex = getListIndexByBaseDBName(baseDBName);
        }
        if (currentBaseDBIndex > -1) {
            buildBaseLists();
        }
        currentDB = bdbList.get(currentBaseDBIndex);
    }

    /* Get the index according to the database name passed*/
    private int getListIndexByBaseDBName(String baseDBName) {
        if(mdbList==null)
            mdbList = masterDB.getMasterDao().getAllDatabases();
        int rv = -1; // default to not found
        for(int i=0; i < mdbList.size();i++) {
            if (mdbList.get(i).getDatabaseName().equals(baseDBName)) {
                rv = i;
                break;
            }
        }
        return rv;
    }

    /* Output all rows from the BaseTable for data extracted by the BaseDaos getAllBaseTables */
    private void logBaseData(List<BaseTable> baseTableList) {
        Log.d(TAG,"Current Database Index is " + currentBaseDBIndex + " DB name is " + mdbList.get(currentBaseDBIndex).getDatabaseName());
        for(BaseTable bt: baseTableList) {
            Log.d(TAG,"\tMyData value is " + bt.getMydata());
        }
    }

    private boolean isFirstTime(){
        if (sharedPreferences.getBoolean("firstTime", true)) {
            settings.putBoolean("firstTime", false);
            settings.commit();
            settings.apply();
            return true;
        } else {
            return false;
        }
    }

}

BaseDatabase - databaseName 是由于空变量而发生错误的位置

@Database(
        entities = {BaseTable.class},
        version = 1
)
public abstract class BaseDatabase extends RoomDatabase {
    public abstract BaseDao getBaseDao();

    private static final int NUMBER_OF_THREADS = 4;
    public static final ExecutorService databaseWriteExecutor =
            Executors.newFixedThreadPool(NUMBER_OF_THREADS);

    public static BaseDatabase getInstance(Context context, String databaseName) {
        BaseDatabase instance = null;
        if (databaseName != null) {
            return Room.databaseBuilder(context, BaseDatabase.class, databaseName)
                    .allowMainThreadQueries()
                    .build();
        }
        return instance;
    }
}

【问题讨论】:

  • 哪一行导致错误?
  • 使用像 liveData 等数据结构这样的观察者订阅者,并在获取数据时监听变化。
  • @Code-Apprentice - `BaseDatabase` 在buildBaseLIstfor 循环中初始化。
  • @Anshul - 谢谢,我没有尝试过Observer,我会阅读的。
  • @svstackoverflow 请给出实际的代码行,而不是对其进行口头描述。

标签: java android android-room


【解决方案1】:

这是一个包含 2 个活动的示例。第一个 MainActivity

  • 显示可用数据库的列表(起初没有)
    • 单击数据库可以选择它(它不会被访问或创建(如果是新的))
  • 允许通过在 EditText 中输入数据库名称来将数据库添加到可用数据库(未创建或访问)。
  • 允许向第二个活动传递数据库名称,然后活动可以打开数据库(如果它不存在则创建它)。
    • 请注意,未访问数据库。

首先是 MasterDatabaseList 类(实体):-

@Entity(tableName = MasterDatabaseList.TABLE_NAME,
        indices = { @Index(value = MasterDatabaseList.COL_DATABASE_NAME, unique = true)
        }
)
class MasterDatabaseList {
    public static final String TABLE_NAME = "masterdatabaselist";
    public static final String COl_ID = "id";
    public static final String COL_DATABASE_NAME = "databasename";
    public static final String[] ALL_COLUMNS = new String[]{
            COl_ID, COL_DATABASE_NAME
    };
    @PrimaryKey
    @ColumnInfo(name = COl_ID)
    Long id;
    @ColumnInfo(name = COL_DATABASE_NAME)
    String databaseName;

    public MasterDatabaseList() {}
    @Ignore
    public MasterDatabaseList(String databaseName) {
        this.databaseName = databaseName;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getDatabaseName() {
        return databaseName;
    }
    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }
}

MasterDatabaseDao

@Dao
abstract class MasterDao {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract long insert(MasterDatabaseList masterDatabaseList);
    @Query("SELECT * FROM masterdatabaselist")
    abstract List<MasterDatabaseList> getAllDatabases();

    Cursor getAllDatabasesAsCursor() {
        MatrixCursor matrixCursor = new MatrixCursor(
                new String[]{
                        BaseColumns._ID, /* Cursor Adapter must use _id column for id) */
                        MasterDatabaseList.COL_DATABASE_NAME
                },
                0
        );
        for(MasterDatabaseList m: getAllDatabases()) {
            matrixCursor.addRow(new Object[]{m.id,m.databaseName});
        }
        return matrixCursor;
    }
}
  • 注意获取可用数据库列表作为光标的新方法(用于 ListView)

  • 主数据库

    @数据库( 实体 = {MasterDatabaseList.class}, 版本 = 1 ) 抽象类 MasterDatabase 扩展 RoomDatabase { 抽象 MasterDao getMasterDao();

      static volatile MasterDatabase instance = null;
      public static MasterDatabase getInstance(Context context) {
          if (instance == null) {
              instance = Room.databaseBuilder(context,MasterDatabase.class,"master.db")
                      .allowMainThreadQueries()
                      .build();
          }
          return instance;
      }
    

    }

  • previous answer保持不变

第二个活动UseSelectedDatabase

public class UseSelectedDatabase extends AppCompatActivity {
    public static final String INTENT_EXTRA_DATABASEID = "database_id";
    public static final String INTENT_EXTRA_DATABASENAME = "database_name";

    long mDatabaseId;
    String mDatabaseName;
    TextView mDatabaseBeingUsed;
    Button mDoneButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_use_selected_database);
        mDatabaseBeingUsed = this.findViewById(R.id.database_name);
        mDoneButton = this.findViewById(R.id.done);


        mDatabaseId = this.getIntent().getLongExtra(INTENT_EXTRA_DATABASEID,-1);
        mDatabaseName = this.getIntent().getStringExtra(INTENT_EXTRA_DATABASENAME);
        mDatabaseBeingUsed.setText(mDatabaseName);
        setDoneButton();

        /*
            can now get an instance of the database
         */

    }
    
    private void setDoneButton() {
        mDoneButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
    }
}
  • 这不会访问数据库,而只是接收主数据库中的数据库名称和 ID。即仅表明您可以传递访问数据库所需的所有信息。

第二个活动布局activity_use_selected_database.xml:-

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".UseSelectedDatabase">

    <TextView
        android:id="@+id/database_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="No Database Set?"
        >
    </TextView>

    <Button
        android:id="@+id/done"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="DONE">
    </Button>

</LinearLayout>

初始活动MainActivity:-

public class MainActivity extends AppCompatActivity {

    MasterDatabase mMasterDB;
    MasterDao mMasterDBDao;
    EditText mDBToAdd;
    Button mAddDB,mUseSelectedDatabase;
    ListView mDatabaseList;
    SimpleCursorAdapter mSCA;
    Cursor mCsr;
    long mSelectedDatabaseId = 0;
    String mSelectedDatabaseName = "";


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mDBToAdd = this.findViewById(R.id.database_name);
        mAddDB = this.findViewById(R.id.addDatabase);
        mUseSelectedDatabase = this.findViewById(R.id.useSelectedDatabase);
        mDatabaseList = this.findViewById(R.id.database_list);

        mMasterDB = MasterDatabase.getInstance(this);
        mMasterDBDao = mMasterDB.getMasterDao();


        setUpAddDBButton();
        setUpUseSelectedDatabaseButton();
        setOrRefreshDatabaseList();
    }

    private void setUpAddDBButton() {
        mAddDB.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mDBToAdd.getText().toString().length() > 0) {
                    if (mMasterDBDao.insert(new MasterDatabaseList(mDBToAdd.getText().toString())) > 0) {
                        mDBToAdd.setText("");
                        setOrRefreshDatabaseList();
                    }
                }
            }
        });
    }
    private void setUpUseSelectedDatabaseButton() {
        mUseSelectedDatabase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mSelectedDatabaseId > 0) {
                    Intent intent = new Intent(view.getContext(),UseSelectedDatabase.class);
                    intent.putExtra(UseSelectedDatabase.INTENT_EXTRA_DATABASEID, mSelectedDatabaseId);
                    intent.putExtra(UseSelectedDatabase.INTENT_EXTRA_DATABASENAME,mSelectedDatabaseName);
                    startActivity(intent);
                }
            }
        });
    }

    private void setOrRefreshDatabaseList() {
        mCsr = mMasterDBDao.getAllDatabasesAsCursor();
        if (mSCA == null) {
            mSCA = new SimpleCursorAdapter(
                    this.getApplicationContext(),
                    android.R.layout.simple_list_item_1,
                    mCsr,
                    new String[]{MasterDatabaseList.COL_DATABASE_NAME},
                    new int[]{android.R.id.text1},
                    0
            );
            mDatabaseList.setAdapter(mSCA);
            mDatabaseList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                /* Handle Clicking on an Item (i.e. prepare UseSelected Button) */
                @SuppressLint("Range")
                @Override
                public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                    mSelectedDatabaseId = l;
                    if (l > 0) {
                        mSelectedDatabaseName = mCsr.getString(mCsr.getColumnIndex(MasterDatabaseList.COL_DATABASE_NAME));
                        mUseSelectedDatabase.setText(mSelectedDatabaseName);
                        mUseSelectedDatabase.setClickable(true);
                    } else {
                        mUseSelectedDatabase.setText("NO DATEBASE SELECTED");
                        mUseSelectedDatabase.setClickable(false);
                    }
                }
            });
        } else {
            mSCA.swapCursor(mCsr);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        setOrRefreshDatabaseList();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mCsr.close();
    }
}

初始Activity的布局activity_main.xml :-

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />
    <EditText
        android:id="@+id/database_name"
        android:layout_width="500dp"
        android:layout_height="wrap_content"
        android:text="">
    </EditText>
    <Button
        android:id="@+id/addDatabase"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Add Database"
        >
    </Button>
    <ListView
        android:id="@+id/database_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    </ListView>
    <Button
        android:id="@+id/useSelectedDatabase"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="NO SELECTED DATABASE"
        android:clickable="false"
        >
    </Button>

</LinearLayout>

演示

第一次运行 MainActivity 时显示:-

即没有可用的数据库(点击按钮什么都不做)。

Database Test001 进入 Edit Text 然后点击 Add Database :-

  • 在此阶段尚未创建 Test001 数据库。但是,MasterDatabase 已经创建并且 Test001 行已经添加(即,如果数据库不存在,有时可以打开并创建它):-

Test001 被点击:-

  • Use Selected 按钮已更改为 Test001(可能应该是 Use Test001)。
  • Test001 数据库仍然没有被访问,因为没有必要。

在编辑文本中输入数据库Test002(添加后清除)并单击添加数据库:-

  • 请注意,如果将现有数据库输入到编辑文本中,则不会添加该数据库,也不会清除文本框。

点击Test002,按钮变为Test002,点击按钮,开始第二个活动:-

  • 数据库未打开或访问(即进一步演示等待和做事)

点击完成按钮返回到第一个活动。仍然没有打开/访问数据库,但其他代码(Base???? clases 等)可能是(可能在 UseSelectedDatabase 活动中)。

Extra 作为概念证明,BaseTable ???添加了上一个问题中的类,以及一些新用户??? BaseDatabase 的类是:-

@Database(
        entities = {BaseTable.class,User.class},
        version = 1
)
abstract class BaseDatabase extends RoomDatabase {
    abstract BaseDao getBaseDao();
    abstract UserDao getUserDao();

    public static BaseDatabase getInstance(Context context, String databaseName) {

        BaseDatabase db = Room.databaseBuilder(context, BaseDatabase.class, databaseName)
                .allowMainThreadQueries()
                .build();
        db.getOpenHelper().getWritableDatabase();
        return db;
    }
}

UseSelectedDatabase 活动中添加以下内容:-

    ....

    /*
        can now get an instance of the database
     */

    db = BaseDatabase.getInstance(this,mDatabaseName);
    baseDao = db.getBaseDao();
    userDao = db.getUserDao();

    baseDao.insert(new BaseTable("X"));
    userDao.insert(new User("Snowman","Fred","Wlibur","Bloggs","password",0));

    List<BaseTable> baseTableList = baseDao.getAllBaseTables();
    List<User> userList = userDao.getAllUsers();

重新启动应用程序选择 Test001,然后选择 Test002 导致数据库显示为:-

即Test001 虽然已经关闭,但显然已经像 Test002 一样创建了。

【讨论】:

  • 哇,谢谢!我会尽量不让您正确使用整个应用程序,哈哈!我将对此进行审查并提供反馈。感谢您对此和之前的所有帮助。我从他们身上学到了很多。
  • 好的,已审核。正如您可能看到的,我修改了您之前提供的一些问题示例(再次感谢)。重申确认,多次单击Add Database 只需将名称添加到MasterDatabase 并将其添加到每次刷新的ListView,但仅此而已。在ListView 上选择一个数据库会更改第二个Button 名称,如果单击该名称,则会启动第二个Activity 创建实际的Base 数据库。但是,其他数据库名称仍保留在ListView 中,无论它们是否已创建——对吗?这很好......只是确认。
  • @svstackoverflow 是的
  • 为了 MikeT 和其他人的利益,我想要跟进我能够成功实施建议的多数据库管理系统以及他对我之前的问题(此问题的基础)的帮助:@ 987654330@
猜你喜欢
  • 1970-01-01
  • 2016-07-08
  • 2018-09-11
  • 1970-01-01
  • 2015-11-08
  • 2017-01-20
  • 1970-01-01
  • 2012-08-01
  • 1970-01-01
相关资源
最近更新 更多