繁体   English   中英

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

[英]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()
        }




    }

}

这是我尝试启动密码活动时首次出现的错误

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) 

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

当我尝试使用这些功能时,应用程序崩溃了。

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

myDB = DatabaseHelper(this)
  • 在访问/使用 myDB 之前,可能直接在setContentView(R.layout.activity_main)

我在这个 kotlin 类中错误地使用了 sqlite 你并不太远,但你似乎误解了查询和 Android 游标。 简而言之,当单个查询可以返回所有这些信息时,您正在循环查询数据库,然后您可以循环遍历 Cursor 的结果。

这里有一些问题。

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

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

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

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

通过循环时,您试图将一个游标分配给一个字符串,例如var description = myDb.getData(id.toString(), "COL_2")将导致description成为一个游标。 为了解决尝试构建不喜欢 Cursor 的 PasswordCluster 的问题,您使用了CursortoString方法,这将导致使用默认对象toString方法,该方法返回有关 Cursos 的详细信息,而不是预期值(描述、密码)或日期时间)。

建议修复

看来您想从 RecyclerView 获取 PasswordCluster 的列表到密码val passwords = ArrayList<PasswordCluster>() )。

我建议你应该在DatabaseHelper类中有一个函数,它将所有密码的列表作为 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,因为该值提供了访问特定行的最有效方法(例如更新/删除/提取单行)。
    • 您经常会看到 Int 表示 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函数并在活动 :-

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")
    }
}
  • 请注意,为方便起见,活动是 MainActiviy 而不是密码

运行和结果: -

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM