【问题标题】:SQLiteDatabase error on recreate Activity重新创建活动时出现 SQLiteDatabase 错误
【发布时间】:2021-11-25 23:41:54
【问题描述】:

重新创建意图后,每个数据库查询都会出现异常。我认为这可能是因为重新创建活动的方式,但不知道如何解决这个问题。

我在更改语言和更改暗模式后重新创建它。

重新创建活动时抛出异常:

尝试重新打开一个已经关闭的对象:SQLiteDatabase:

活动

private lateinit var db: SQLiteDatabase
private lateinit var oh: LOTDatabaseHelper
...

onCreate

...
oh = LOTDatabaseHelper(this)
db = oh.readableDatabase
...

onDestroy

...
db.close()
oh.close()
...

以及我如何重新创建意图

val intent = intent
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
startActivity(intent)
finish()

编辑:

我发现 Activity 大约创建了两次,然后留下一个,其余的被销毁。 OnDestroy 似乎适用于所有活动并关闭数据库,这是不可能的。

现在我导航到启动画面,然后导航到使用数据库的活动,一切正常。 那么为什么用自己替换Activity时生命周期不能正常工作呢?

【问题讨论】:

    标签: android sqlite android-activity android-sqlite android-lifecycle


    【解决方案1】:

    打开一个已经关闭的数据库一般是由于打开和关闭不匹配造成的。

    解决方法通常是不打开和关闭数据库(除非特别需要,例如从备份副本恢复数据库,或强制 WAL 检查点备份数据库),而是只打开一次数据库。

    通常不需要继续打开和关闭数据库,因为当应用程序完成时数据库将被关闭(您始终可以在最顶层活动的 onDestroy 上关闭它)。此外,打开数据库需要耗费大量资源,因此多次打开会浪费资源/效率低下。

    我建议删除所有关闭。您可能还希望考虑单例方法,甚至可能在 Helper 中拥有所有 DB 功能。

    作为一个例子也许考虑:-

    助手 DBHelper :-

    class LOTDatabaseHelper(context: Context): SQLiteOpenHelper(
        context,
        DATABASE_NAME,
        null,
        DATABASE_VERSION) {
    
        companion object {
            const val DATABASE_NAME = "my.db"
            const val DATABASE_VERSION = 1
    
            @Volatile
            private var instance: SQLiteDatabase? = null
    
            fun getInstance(context: Context): SQLiteDatabase {
                if (instance == null) {
                    instance = LOTDatabaseHelper(context).writableDatabase
                }
                return instance as SQLiteDatabase
            }
    
        }
    
        override fun onCreate(db: SQLiteDatabase?) {
            db?.execSQL(Table1.CREATE_SQL)
        }
    
        override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
            TODO("Not yet implemented")
        }
    
        override fun onDowngrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
            super.onDowngrade(db, oldVersion, newVersion)
        }
    
        fun insert(name: String): Long {
            val cv = ContentValues()
            cv.put(Table1.COL_NAME,name)
            return instance!!.insert(Table1.TABLE_NAME,null,cv)
        }
    
        fun getAllFromTable1() : Cursor {
            return instance!!.query(Table1.TABLE_NAME,null,null,null,null,null,"${Table1.COL_NAME} ASC")
        }
    
        @SuppressLint("Range")
        fun logAll() {
            val csr = getAllFromTable1()
            var row = 1
            val nameix = csr.getColumnIndex(COL_NAME)
            while (csr.moveToNext()) {
                Log.d("CURSORINFO","Row ${row++} ${COL_NAME} is ${csr.getString(csr.getColumnIndex(
                    COL_NAME))}")
                //val test = csr.getString(csr.getColumnIndex("${COL_NAME}"))
    
            }
            csr.close()
        }
    
        // Table stuff
        class Table1 {
            companion object {
                const val TABLE_NAME = "table1"
                const val COL_ID = BaseColumns._ID
                const val COL_NAME = "${TABLE_NAME}_name"
                const val CREATE_SQL = "CREATE TABLE IF NOT EXISTS $TABLE_NAME (${COL_ID} INTEGER PRIMARY KEY, $COL_NAME TEXT);"
            }
        }
    }
    

    调用第二个Activity的初始ActivityMainActivity:-

    class MainActivity : AppCompatActivity() {
        val TAG = "MAINACTIVITYINFO"
    
        private lateinit var dbother: DBHelper
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            dbother = DBHelper.getInstance(this)!!
            dbother.insert("TESTOTHER") // Use the Insert in the DBHelper
    
            /* of course you can still get an SQLiteDatabase */
            /* There is very little use getting readable as it gets writable */
            /* so get writable is more accurate description */
            var sqliteDatabase = dbother.writableDatabase //<<<<<< can be used
            logCursorInfo(dbother.allAsCursor)
    
            // NOW Start the other Activity
            Log.d(TAG,"starting Activity2")
            intent = Intent(this,Activity2::class.java)
            startActivity(intent)
            Log.d(TAG,"after starting Activity2")
    
        }
    
        override fun onDestroy() {
            super.onDestroy()
            Log.d("ONDESTROY","On Destroy invoked.")
        }
    
        override fun onResume() {
            super.onResume()
            Log.d(TAG,"On Resume invoked")
            dbother = DBHelper.getInstance(this)!!
        }
    
        //
        @SuppressLint("Range")
        fun logCursorInfo(csr: Cursor) {
            val nameix = csr.getColumnIndex(LOTDatabaseHelper.Table1.COL_NAME)
            val idix = csr.getColumnIndex(LOTDatabaseHelper.Table1.COL_ID)
            while (csr.moveToNext()) {
                //Log.d(TAG,"${LOTDatabaseHelper.Table1.COL_ID} = ${csr.getString(idix)} ${LOTDatabaseHelper.Table1.COL_NAME} = ${csr.getString(nameix)}")
                Log.d(TAG,"${csr.getString(csr.getColumnIndex(LOTDatabaseHelper.Table1.COL_NAME))}")
            }
        }
        companion object {
            var counter: Int = 0
        }
    }
    
    • 这使用 DBHelper 但获取一个实例,然后添加几行,提取它们然后启动第二个 Activity。

    Activity2您遇到问题的活动:-

    class Activity2 : AppCompatActivity() {
        val TAG = "ACTIVITY2INFO"
        private lateinit var dbother: DBHelper
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_2)
            Log.d(TAG,"Activity 2 Started (onCreate). $this")
    
            dbother = DBHelper.getInstance(this)!! // Get the Database
    
            dbother.insert("TESTOTHER_ACTIVITY2") // Add a row
            var csr = dbother.allAsCursor // get All the rows
            DatabaseUtils.dumpCursor(csr) // dump the cursor
            csr.close() // close the cursor
    
            /* Start another activity (will loop so stop with counter)*/
            Log.d(TAG,"Restarting Activity. counter is ${counter++} $this")
            if (counter < 3) {
                val intent = intent
                intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
                startActivity(intent) //<<<<<<<<<< RUN 1 Not the Way as starts a new activity
    
                /* Perhaps the following???? */
                // this.intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
                //this.recreate() //<<<<<<<<<< RUN 2 perhaps the way but no intent
            }
            Log.d(TAG,"Finishing Activity 2 $this")
            finish()
        }
    
        companion object {
            var counter: Int = 0
        }
    
        override fun onResume() {
            super.onResume()
            Log.d(TAG,"OnResume Invoked. $this")
        }
    
        override fun onDestroy() {
            super.onDestroy()
            Log.d(TAG,"OnDestory (after super call - before db close and helper close. $this)")
            Log.d(TAG,"OnDestory (after all. $this)")
        }
    }
    
    • 这将获取一个(该)DBHelper 实例,插入一行,提取所有数据,然后启动另一个活动,注意这将无限循环,因此计数器。

    希望 cmets 能提供帮助。

    如您所见,我已经包含了相当多的日志记录。但是,正如建议的那样,数据库永远不会关闭。

    运行应用程序

    这是运行应用程序的结果,即日志(来自全新安装):-

    2021-10-06 19:52:50.715  D/DBHELPERINFO: getInstance returning instance a.a.so69454766kotlinsqlite.DBHelper@59860ab
    2021-10-06 19:52:50.717  D/MAINACTIVITYINFO: TESTOTHER
    2021-10-06 19:52:50.717  D/MAINACTIVITYINFO: starting Activity2
    2021-10-06 19:52:50.724  D/MAINACTIVITYINFO: after starting Activity2
    2021-10-06 19:52:50.734  D/MAINACTIVITYINFO: On Resume invoked
    2021-10-06 19:52:50.734  D/DBHELPERINFO: getInstance returning instance a.a.so69454766kotlinsqlite.DBHelper@59860ab
    2021-10-06 19:52:50.848  D/ACTIVITY2INFO: Activity 2 Started (onCreate). a.a.so69454766kotlinsqlite.Activity2@3269902
    2021-10-06 19:52:50.848  D/DBHELPERINFO: getInstance returning instance a.a.so69454766kotlinsqlite.DBHelper@59860ab
    2021-10-06 19:52:50.850  I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@950aa67
    2021-10-06 19:52:50.851  I/System.out: 0 {
    2021-10-06 19:52:50.852  I/System.out:    _id=1
    2021-10-06 19:52:50.852  I/System.out:    table1_name=TESTOTHER
    2021-10-06 19:52:50.852  I/System.out: }
    2021-10-06 19:52:50.852  I/System.out: 1 {
    2021-10-06 19:52:50.852  I/System.out:    _id=2
    2021-10-06 19:52:50.852  I/System.out:    table1_name=TESTOTHER_ACTIVITY2
    2021-10-06 19:52:50.852  I/System.out: }
    2021-10-06 19:52:50.852  I/System.out: <<<<<
    2021-10-06 19:52:50.856  D/ACTIVITY2INFO: Restarting Activity. counter is 0 a.a.so69454766kotlinsqlite.Activity2@3269902
    2021-10-06 19:52:50.864  D/ACTIVITY2INFO: Finishing Activity 2 a.a.so69454766kotlinsqlite.Activity2@3269902
    2021-10-06 19:52:51.020  D/ACTIVITY2INFO: Activity 2 Started (onCreate). a.a.so69454766kotlinsqlite.Activity2@7db7a75
    2021-10-06 19:52:51.020  D/DBHELPERINFO: getInstance returning instance a.a.so69454766kotlinsqlite.DBHelper@59860ab
    2021-10-06 19:52:51.021  I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@7fc80ae
    2021-10-06 19:52:51.021  I/System.out: 0 {
    2021-10-06 19:52:51.021  I/System.out:    _id=1
    2021-10-06 19:52:51.021  I/System.out:    table1_name=TESTOTHER
    2021-10-06 19:52:51.021  I/System.out: }
    2021-10-06 19:52:51.021  I/System.out: 1 {
    2021-10-06 19:52:51.021  I/System.out:    _id=2
    2021-10-06 19:52:51.022  I/System.out:    table1_name=TESTOTHER_ACTIVITY2
    2021-10-06 19:52:51.022  I/System.out: }
    2021-10-06 19:52:51.022  I/System.out: 2 {
    2021-10-06 19:52:51.022  I/System.out:    _id=3
    2021-10-06 19:52:51.022  I/System.out:    table1_name=TESTOTHER_ACTIVITY2
    2021-10-06 19:52:51.022  I/System.out: }
    2021-10-06 19:52:51.022  I/System.out: <<<<<
    2021-10-06 19:52:51.023  D/ACTIVITY2INFO: Restarting Activity. counter is 1 a.a.so69454766kotlinsqlite.Activity2@7db7a75
    2021-10-06 19:52:51.030  D/ACTIVITY2INFO: Finishing Activity 2 a.a.so69454766kotlinsqlite.Activity2@7db7a75
    2021-10-06 19:52:51.081  D/ACTIVITY2INFO: Activity 2 Started (onCreate). a.a.so69454766kotlinsqlite.Activity2@3433874
    2021-10-06 19:52:51.081  D/DBHELPERINFO: getInstance returning instance a.a.so69454766kotlinsqlite.DBHelper@59860ab
    2021-10-06 19:52:51.082  I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@5c713d1
    2021-10-06 19:52:51.082  I/System.out: 0 {
    2021-10-06 19:52:51.082  I/System.out:    _id=1
    2021-10-06 19:52:51.082  I/System.out:    table1_name=TESTOTHER
    2021-10-06 19:52:51.082  I/System.out: }
    2021-10-06 19:52:51.082  I/System.out: 1 {
    2021-10-06 19:52:51.083  I/System.out:    _id=2
    2021-10-06 19:52:51.083  I/System.out:    table1_name=TESTOTHER_ACTIVITY2
    2021-10-06 19:52:51.083  I/System.out: }
    2021-10-06 19:52:51.083  I/System.out: 2 {
    2021-10-06 19:52:51.083  I/System.out:    _id=3
    2021-10-06 19:52:51.083  I/System.out:    table1_name=TESTOTHER_ACTIVITY2
    2021-10-06 19:52:51.083  I/System.out: }
    2021-10-06 19:52:51.083  I/System.out: 3 {
    2021-10-06 19:52:51.083  I/System.out:    _id=4
    2021-10-06 19:52:51.083  I/System.out:    table1_name=TESTOTHER_ACTIVITY2
    2021-10-06 19:52:51.083  I/System.out: }
    2021-10-06 19:52:51.083  I/System.out: <<<<<
    2021-10-06 19:52:51.084  D/ACTIVITY2INFO: Restarting Activity. counter is 2 a.a.so69454766kotlinsqlite.Activity2@3433874
    2021-10-06 19:52:51.084  D/ACTIVITY2INFO: Finishing Activity 2 a.a.so69454766kotlinsqlite.Activity2@3433874
    2021-10-06 19:52:51.097  D/MAINACTIVITYINFO: On Resume invoked
    2021-10-06 19:52:51.097  D/DBHELPERINFO: getInstance returning instance a.a.so69454766kotlinsqlite.DBHelper@59860ab
    

    需要注意的是,检索到的 DBHelper 实例始终是 DBHelper@59860ab(即单个实例)。

    正如您所注意到的,活动发生的情况是另一个活动被启动。您不妨尝试使用注释掉的:-

            /* Perhaps the following???? */
            // this.intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
            //this.recreate() //<<<<<<<<<< RUN 2 perhaps the way but no intent
    
    • 我不确定这是否会达到您的期望。

    【讨论】:

    • 我已经删除了 LOTDatabaseHelper 的关闭,如你所说,一切正常。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多