0

我最近开始学习 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) 
4

2 回答 2

1

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

于 2019-11-30T21:15:39.440 回答
0

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

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

myDB = DatabaseHelper(this)
  • 在访问/使用 myDB 之前,也许是在之后setContentView(R.layout.activity_main)

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

这里有一些问题。

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")将导致描述成为光标。为了解决尝试构建不喜欢 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 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函数并在活动中使用以下代码:-

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
于 2019-11-30T20:31:17.093 回答