我在这个 kotlin 中错误地使用了 sqlite class

I am using sqlite incorrectly in this kotlin class

最近开始学习kotlin,想用sqlite实现一个数据库。当我尝试使用这些功能时,应用程序崩溃了。我不知道如何找到错误日志所以我添加了我制作的功能以及功能实现的地方。谢谢你的帮助。

package com.example.mrtayyab.sqlitedbkotlin

import android.content.ClipDescription
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.database.DatabaseUtils
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper

class DatabaseHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, 1) {


    companion object {

        val DATABASE_NAME = "passwords.db"
        val TABLE_NAME = "passwords_table"
        val COL_1 = "ID"
        val COL_2 = "DESCRIPTION"
        val COL_3 = "PASSWORD"
        val COL_4 = "DATE_TIME"
    }


    override fun onCreate(db: SQLiteDatabase) {

        db.execSQL("CREATE TABLE IF NOT EXISTS $TABLE_NAME(ID INTEGER PRIMARY KEY AUTOINCREMENT , DESCRIPTION  TEXT , PASSWORD TEXT , DATE_TIME INTEGER)")

    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {

        db.execSQL("DROP TABLE IF EXISTS $TABLE_NAME")
        onCreate(db)

    }

    fun insertData(description: String, password: String, date_time: String): Boolean? {
        val db = this.writableDatabase
        val cv = ContentValues()
        cv.put(COL_2, description)
        cv.put(COL_3, password)
        cv.put(COL_4, date_time)
        val res = db.insert(TABLE_NAME, null, cv)
        return !res.equals(-1)
    }

    fun getData(id: String, COL_NUM: String): Cursor {
        val column = arrayOf("$COL_NUM")
        val columnValue = arrayOf("$id")
        val db = this.writableDatabase
        return db.query("$TABLE_NAME", column, "$COL_1", columnValue, null, null, null )
    }

    fun getTableCount(): Long {
        val db = this.readableDatabase
        val count = DatabaseUtils.queryNumEntries(db, TABLE_NAME)

        return count
    }
}

这是实现的代码

package com.example.juliaojonah_hci_outcome2_v1

import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.mrtayyab.sqlitedbkotlin.DatabaseHelper
import com.google.gson.Gson
import kotlinx.android.synthetic.main.activity_main.*
import java.text.DateFormat
import java.util.*
import kotlin.collections.ArrayList

class passwords : AppCompatActivity() {

    lateinit var myDb: DatabaseHelper

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_passwords)



        val dateTime = Calendar.getInstance().time

        val dateFormatted = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(dateTime)

        val dateTextView : TextView = findViewById(R.id.textDate2) as TextView
        dateTextView.setText(dateFormatted)

        val recyclerView = findViewById(R.id.savedPasswords) as RecyclerView

        recyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)

        val counter = myDb.getTableCount()

        val passwords = ArrayList<PasswordCluster>()

        if (counter > 0) {
            for (i in 1..counter) {
                var id = i
                var description = myDb.getData(id.toString(), "COL_2")
                var password = myDb.getData(id.toString(), "COL_3")
                var dateTime = myDb.getData(id.toString(), "COL_4")

                passwords.add(PasswordCluster(description.toString(), password.toString(), dateTime.toString()))
            }
        }

/*
        passwords.add(PasswordCluster("Amazon", "56,78,90,12", "Friday"))
        passwords.add(PasswordCluster("Amazon", "56,78,90,12", "Friday"))
        passwords.add(PasswordCluster("Amazon", "56,78,90,12", "Friday"))
        passwords.add(PasswordCluster("Amazon", "56,78,90,12", "Friday"))
        passwords.add(PasswordCluster("Amazon", "56,78,90,12", "Friday"))
*/
        val adapter = CustomAdapter(passwords)

        recyclerView.adapter = adapter
    }

    fun firstActivity(view: View){
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)
    }
}

我在最后一段代码中使用了保存数据功能,它没有崩溃,但我不知道它是否正常工作

package com.example.juliaojonah_hci_outcome2_v1

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.mrtayyab.sqlitedbkotlin.DatabaseHelper
import com.google.gson.Gson
import kotlinx.android.synthetic.main.activity_main.*
import java.security.spec.PSSParameterSpec
import java.text.DateFormat
import java.util.*
import kotlin.random.Random


class MainActivity : AppCompatActivity() {

    lateinit var myDb: DatabaseHelper

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val dateTime = Calendar.getInstance().time

        val dateFormatted = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(dateTime)

        val dateTextView : TextView = findViewById(R.id.textDate) as TextView
        dateTextView.setText(dateFormatted)

        myDb = DatabaseHelper(this)
        }


    fun secondActivity(view: View){
        val intent = Intent(this, passwords::class.java)
        startActivity(intent)
    }

    fun randomise(view: View){


        val passwordSet1 = Random.nextInt(0,99)
        val passwordSet2 = Random.nextInt(0,99)
        val passwordSet3 = Random.nextInt(0,99)
        val passwordSet4 = Random.nextInt(0,99)

        val editText1 = findViewById<EditText>(R.id.password1)
        editText1.setText(passwordSet1.toString())

        val editText2 = findViewById<EditText>(R.id.password2)
        editText2.setText(passwordSet2.toString())

        val editText3 = findViewById<EditText>(R.id.password3)
        editText3.setText(passwordSet3.toString())

        val editText4 = findViewById<EditText>(R.id.password4)
        editText4.setText(passwordSet4.toString())

    }



    fun save(view: View){
        var correct = true

        val description = descriptionName.text.toString().trim()
        val password = password1.text.toString().trim() + "-" + password2.text.toString().trim() + "-" + password3.text.toString().trim() + "-" + password4.text.toString().trim()
        val dateTime = Calendar.getInstance().time
        val dateFormatted = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(dateTime)

        if (TextUtils.isEmpty(description)){
            descriptionName.error = "Enter description"
            correct = false
        }

        if (password == "---"){
            password1.error = "Enter password"
            password2.error = "Enter password"
            password3.error = "Enter password"
            password4.error = "Enter password"
            correct = false
        }


        var isInserted = myDb.insertData(description, password, dateFormatted)


        if (isInserted == true && correct == true){
            Toast.makeText(this, "Data Saved", Toast.LENGTH_LONG).show()
            Toast.makeText(this, isInserted.toString(), Toast.LENGTH_LONG).show()
        } else {
            Toast.makeText(this, "Data Not Saved", Toast.LENGTH_LONG).show()
        }




    }

}

这是我尝试启动密码时第一次出现的错误activity

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.juliaojonah_hci_outcome2_v1, PID: 20054
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.juliaojonah_hci_outcome2_v1/com.example.juliaojonah_hci_outcome2_v1.passwords}: kotlin.UninitializedPropertyAccessException: lateinit property myDb has not been initialized
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6077)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
     Caused by: kotlin.UninitializedPropertyAccessException: lateinit property myDb has not been initialized
        at com.example.juliaojonah_hci_outcome2_v1.passwords.onCreate(passwords.kt:42)
        at android.app.Activity.performCreate(Activity.java:6662)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2599)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6077) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756) 

这是我尝试 运行 .getData 函数

时发生的情况
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.juliaojonah_hci_outcome2_v1, PID: 20221
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.juliaojonah_hci_outcome2_v1/com.example.juliaojonah_hci_outcome2_v1.passwords}: kotlin.UninitializedPropertyAccessException: lateinit property myDb has not been initialized
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6077)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
     Caused by: kotlin.UninitializedPropertyAccessException: lateinit property myDb has not been initialized
        at com.example.juliaojonah_hci_outcome2_v1.passwords.onCreate(passwords.kt:51)
        at android.app.Activity.performCreate(Activity.java:6662)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2599)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6077) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756) 

When I try using the functions the app crashes.

lateinit property myDb has not been initialized 是说你需要初始化 myDB。那就是你需要包括:-

myDB = DatabaseHelper(this)
  • 在 accessing/using myDB 之前,也许直接在 setContentView(R.layout.activity_main)
  • 之后

I am using sqlite incorrectly in this kotlin class You aren't too far away but you appear to misunderstand queries and for Android Cursors. In short you are querying the database in a loop when a single query can return all of that information and you can then loop through the result the Cursor.

这是一些问题。

getData 函数中,您应该使用 "${COL_1}=?" 作为第三个参数来调用 query 作为此参数是 WHERE 子句减去 WHERE 关键字。你实际上是在说 SELECT whatever_column_is_passed FROM passwords_table WHERE x;

  • x 将是传递的值(1 然后 2 然后 3 ....)。由于 where 子句需要 true 或 false,并且 0 为假且其他数字为正,因此将返回所有行。

但是,您在处理(而不是处理它)从 getData 返回的 Cursor 方面遇到了一些问题。

首先,您假设 table val counter = myDb.getTableCount() 中的行数将等于返回的行数,并且 id 的编号为 1,2,3..... .. 最初可能是这种情况,但如果删除一行或插入失败,则序列可能不会单调递增。

在执行循环时,您试图将 Cursor 分配给 String,例如var description = myDb.getData(id.toString(), "COL_2") 将导致 description 成为游标。为了解决尝试构建不喜欢 Cursor 的 PasswordCluster 的问题,您使用了 CursortoString 方法,这将导致默认正在使用对象 toString 方法,其中 returns 有关 Cursos 的详细信息不是预期值(描述、密码或日期时间)。

建议修复

您似乎想从 RecyclerView 获取 PasswordCluster 的列表到 passwords (val passwords = ArrayList<PasswordCluster>())。

我建议您应该在 DatabaseHelper class 中添加一个函数,将所有密码的列表作为 ArrayList 重新运行,例如

fun getListOfAllPasswordClusters(): ArrayList<PasswordCluster> {
    val rv = ArrayList<PasswordCluster>()
    val db = this.writableDatabase
    val csr = db.query(TABLE_NAME,null /* ALL columns */,null,null,null,null,null)
    //return db.query("$TABLE_NAME", column, "$COL_1", columnValue, null, null, null )
    while (csr.moveToNext()) {
        rv.add(
            PasswordCluster(
                csr.getString(csr.getColumnIndex(COL_2)),
                csr.getString(csr.getColumnIndex(COL_3)),
                csr.getString(csr.getColumnIndex(COL_4))
            )
        )
        /* NOTE ideally you should also store the id in PasswordCluster
            So assuming a constructor that takes
                Long /* id */
                ,String /* description */
                ,String /* password */
                ,String /* date_time */

                E.G.
        rv.add(
            PasswordCluster(
                csr.getLong(csr.getColumnIndex(COL_1)),
                csr.getString(csr.getColumnIndex(COL_2)),
                csr.getString(csr.getColumnIndex(COL_3)),
                csr.getString(csr.getColumnIndex(COL_4))
            )
        )
        */
    }
    csr.close()
    return rv
}
  • 请注意,我强烈建议确保 PasswordCluster 对象可以存储 id,因为该值提供了访问特定行的最有效方法(例如 update/delete/extract a单排)。
    • 您经常会看到 Int for id 这在大多数情况下可能会起作用,但 id 实际上是一个 Long(64 位有符号整数)。因此,我建议始终将其视为 Long。
  • 编码AUTOINCREMENT很可能不需要,而且效率低下。也许阅读 SQLite Autoincrement
    • 似乎很多人认为它是获取自动生成的顺序 id 所必需的,它本身并不是 INTEGER PRIMARY KEY那。

那么你可以:-

class passwords : AppCompatActivity() {

    lateinit var myDb: DatabaseHelper

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_passwords)    
        val dateTime = Calendar.getInstance().time   
        val dateFormatted = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(dateTime)   
        val dateTextView : TextView = findViewById(R.id.textDate2) as TextView
        dateTextView.setText(dateFormatted)    
        val recyclerView = findViewById(R.id.savedPasswords) as RecyclerView    
        recyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)    
        val passwords = getListOfAllPasswordClusters()
        val adapter = CustomAdapter(passwords)    
        recyclerView.adapter = adapter
    }

    fun firstActivity(view: View){
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)
    }
}

工作示例

这是一个完全基于添加建议的 getListOfAllPasswordClusters() (注释掉的部分替换 rv.add(.........) 代码的示例,因此 id 检索) 函数并在 activity 中使用以下代码:-

class MainActivity : AppCompatActivity() {

    lateinit var myDB: DatabaseHelper

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        myDB = DatabaseHelper(this)
        addSomeDataIfNone()

        val passwords = myDB.getListOfAllPasswordClusters()
        for (p: PasswordCluster in passwords) {
            Log.d("PASSWORDCLUSTER","ID=${p.id} Description=${p.description} password=${p.password} datetime=${p.date_time}")
        }
    }

    fun addSomeDataIfNone() {
        if (DatabaseUtils.queryNumEntries(myDB.writableDatabase,DatabaseHelper.TABLE_NAME) > 0) return
        myDB.insertData("Test1","password1","2019-12-01")
        myDB.insertData("Test2","password2","2019-12-02")
        myDB.insertData("Test3","password3","2019-12-03")
    }
}
  • 请注意,为了方便起见,activity 是 MainActiviy 而不是 passwords

运行结果:-

2019-12-01 07:59:58.289 D/PASSWORDCLUSTER: ID=1 Description=Test1 password=password1 datetime=2019-12-01
2019-12-01 07:59:58.289 D/PASSWORDCLUSTER: ID=2 Description=Test2 password=password2 datetime=2019-12-02
2019-12-01 07:59:58.289 D/PASSWORDCLUSTER: ID=3 Description=Test3 password=password3 datetime=2019-12-03

简而言之,class passwords 需要在其 onCreate() 方法中初始化 myDB = DatabaseHelper(this)。您不能只在某个地方初始化它,然后假设它会在其他地方初始化。当新的Activity没有初始化句柄时,就没有句柄可以使用了。仔细一看,两者只有一个Activity初始化句柄