更新 - Kotlin - 将数据从 SQLite 提取到 .txt 文件,该文件已创建但为空

问题描述 投票:0回答:1

我是 Kotlin 新手,我正在制作一个小应用程序,用户可以在其中逐一插入他的联系人以及每个联系人的更多详细信息。 插入全部或大部分联系人后,他将能够将数据导出到 .txt 文件中,txt 文件的每一行将是每个联系人的详细信息(以非常特定的格式,每个字段将用“;”)。

退出应用程序后,用户将能够插入相同的文件,并且他的所有联系人将被重新插入到数据库(SQLite)中,以便他可以从中断的地方继续。

目前我已经实现了应用程序的大部分实用程序(将联系人插入 SQLite、获取联系人并使用 recyclerView 显示它们等),剩下的就是通过 txt 文件导出和重新插入数据。

我已经做了一个意图,以便我可以打开android的本机目录选择器,在用户想要的位置创建一个文件,然后写入该文件,但是执行后,该文件是空的。 我想将文件保存在外部(下载文件夹),以便用户能够自己打开它(通过文本文件阅读器或通过我已经制作的其他应用程序之一 - 主要是桌面应用程序)

我已经测试过字符串生成器是否有效,并且他输出的几乎正是我想要的(理想情况下,我希望从最终结果的开头和结尾删除方括号,并将每行联系人放在新行中,并且用“/n”分隔每一行,而不是用“,”)

这是我的数据类对象:

package com.example.contactsform.model

data class ContactModelClass(
    val id: Int,
    val name: String,
    val surname: String,
    val dateOfBirth: String,
    val phoneNumber: String,
    val email: String,
    val address: String,
    val city: String,
    val notes: String)

{
    override fun toString() : String {
        val fields = listOf(
            this.id.toString(),
            this.name,
            this.surname,
            this.dateOfBirth,
            this.phoneNumber,
            this.email,
            this.address,
            this.city,
            this.notes)

            return fields.reduce{ field , element -> "$field;$element" }
    }
}

这是我的活动之一(放置提取数据按钮的位置):

package com.example.contactsform.activityViews

import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.activity.result.contract.ActivityResultContracts
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.contactsform.model.ContactModelClass
import com.example.contactsform.adapter.ItemAdapter
import com.example.contactsform.R
import com.example.contactsform.databaseHelper.SQLiteHelper
import java.io.File
import java.io.FileOutputStream
import java.io.IOException


class ContactListActivity : AppCompatActivity() {

    private  lateinit var contactsRecyclerView: RecyclerView
    private lateinit var addContactButton : Button
    private lateinit var exportDataButton: Button

    private val filePicker = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){result->
        val data : Intent? = result.data
        if (result.resultCode == Activity.RESULT_OK && data!=null){
            val selectedFileUri = data.data
               if (selectedFileUri != null) {
                   val selectedFilePath = selectedFileUri.toString()
                    writeTextToFile(this, selectedFilePath, dataToWriteInFile())
               }
            }

    }

    private val FILENAME = "ExportedContacts.txt"


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

        contactsRecyclerView = findViewById(R.id.contactsRecyclerView)

        setupListOfDataIntoRecyclerView()


        addContactButton= findViewById(R.id.addContactButton)
        addContactButton.setOnClickListener {
            val changeFormIntent = Intent(this, AddContactActivity::class.java)
            startActivity(changeFormIntent)
        }

        exportDataButton = findViewById(R.id.exportDataButton)
        exportDataButton.setOnClickListener {
            createContactFile()

         }

    }


    private fun createContactFile() {
        val openDirectoryPickerIntent = Intent(Intent.ACTION_CREATE_DOCUMENT)
            openDirectoryPickerIntent.addCategory(Intent.CATEGORY_OPENABLE)
            openDirectoryPickerIntent.type = "text/plain"
            openDirectoryPickerIntent.putExtra(Intent.EXTRA_TITLE, FILENAME)
        filePicker.launch(openDirectoryPickerIntent)
    }


    private fun dataToWriteInFile(): String {

        val stringBuilder = StringBuilder()
        return stringBuilder.appendLine(getContactsList().toString()).toString()

    }


    private fun writeTextToFile(context: Context, filePath: String ,dataToFile: String) {
        val file = File(context.getExternalFilesDir(null), filePath)

        try {
            val fileOutputStream = FileOutputStream(file)
            fileOutputStream.use{stream->
                stream.write(dataToFile.toByteArray())
            }

         }catch (e: IOException){
            e.printStackTrace()
        }

    }

    private fun setupListOfDataIntoRecyclerView() {
        if (getContactsList().size > 0){
            contactsRecyclerView.visibility = View.VISIBLE

            contactsRecyclerView.layoutManager = LinearLayoutManager(this)

            val itemAdapter = ItemAdapter(this, getContactsList())
            contactsRecyclerView.adapter = itemAdapter


        } else {
            contactsRecyclerView.visibility = View.GONE


        }
    }

    private fun getContactsList(): ArrayList<ContactModelClass> {
        val sqliteHelper = SQLiteHelper(this)
        return  sqliteHelper.loadContacts()

    }

}

这是我的数据库助手:

package com.example.contactsform.databaseHelper

import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteException
import android.database.sqlite.SQLiteOpenHelper
import com.example.contactsform.model.ContactModelClass
import java.util.ArrayList

class SQLiteHelper (context: Context) : SQLiteOpenHelper(context , DATABASE_NAME, null, DATABASE_VERSION) {

    companion object {
        private const val DATABASE_VERSION = 1
        private const val DATABASE_NAME = "contacts_form"
        private const val TABLE_CONTACTS = "contacts"

        private const val ID = "ID"
        private const val NAME = "Name"
        private const val SURNAME = "Surname"
        private const val DATE_OF_BIRTH = "Date_Of_Birth"
        private const val PHONE_NUMBER = "Phone_Number"
        private const val EMAIL = "Email"
        private const val ADDRESS = "Address"
        private const val CITY = "City"
        private const val NOTES = "Notes"
    }

    override fun onCreate(db: SQLiteDatabase?) {
        val CREATE_CONTACTS_TABLE = ("CREATE TABLE " + TABLE_CONTACTS + "("
                + ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
                + NAME + " TEXT,"
                + SURNAME + " TEXT,"
                + DATE_OF_BIRTH + " TEXT,"
                + PHONE_NUMBER + " TEXT,"
                + EMAIL + " TEXT,"
                + ADDRESS + " TEXT,"
                + CITY + " TEXT,"
                + NOTES + " TEXT)")
        db?.execSQL(CREATE_CONTACTS_TABLE)
    }

    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
        db!!.execSQL("DROP TABLE IF EXISTS $TABLE_CONTACTS")
        onCreate(db)
    }

    fun insertContact (contact : ContactModelClass) : Long {
        val db = this.writableDatabase
        var contentValues = ContentValues()
        contentValues.put(NAME, contact.name)
        contentValues.put(SURNAME, contact.surname)
        contentValues.put(DATE_OF_BIRTH, contact.dateOfBirth)
        contentValues.put(PHONE_NUMBER, contact.phoneNumber)
        contentValues.put(EMAIL, contact.email)
        contentValues.put(ADDRESS, contact.address)
        contentValues.put(CITY, contact.city)
        contentValues.put(NOTES, contact.notes)

       val success = db.insertWithOnConflict(TABLE_CONTACTS, null, contentValues, SQLiteDatabase.CONFLICT_REPLACE)

       db.close()

        return success
    }

    fun loadContacts() : ArrayList<ContactModelClass> {

        val contactList : ArrayList<ContactModelClass> = ArrayList<ContactModelClass>()

        val selectQuery = "SELECT * FROM $TABLE_CONTACTS"

        val db = this.readableDatabase
        var cursor: Cursor? = null

        try {
            cursor = db.rawQuery(selectQuery, null)

        } catch (e: SQLiteException){
            db.execSQL(selectQuery)
            return ArrayList()
        }

        var id: Int
        var name: String
        var surname : String
        var dateOfBirth: String
        var phoneNumber: String
        var email: String
        var address: String
        var city: String
        var notes: String

        if (cursor.moveToFirst()){
            do {
                id = cursor.getInt(cursor.getColumnIndexOrThrow(ID))
                name = cursor.getString(cursor.getColumnIndexOrThrow(NAME))
                surname = cursor.getString(cursor.getColumnIndexOrThrow(SURNAME))
                dateOfBirth = cursor.getString(cursor.getColumnIndexOrThrow(DATE_OF_BIRTH))
                phoneNumber = cursor.getString(cursor.getColumnIndexOrThrow(PHONE_NUMBER))
                email = cursor.getString(cursor.getColumnIndexOrThrow(EMAIL))
                address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))
                city = cursor.getString(cursor.getColumnIndexOrThrow(CITY))
                notes = cursor.getString(cursor.getColumnIndexOrThrow(NOTES))

                val contact = ContactModelClass(
                    id = id,
                    name = name,
                    surname = surname,
                    dateOfBirth = dateOfBirth,
                    phoneNumber = phoneNumber,
                    email = email, address = address,
                    city = city,
                    notes = notes)

                contactList.add(contact)

            } while (cursor.moveToNext())
        }

        return contactList

    }

    fun deleteContactsTable() {
        val db = this.writableDatabase
        db?.execSQL("DROP TABLE $TABLE_CONTACTS")
        onCreate(db)
    }

}

编辑

我更新了活动,但文件仍然创建但为空(代码如下)

class ContactListActivity : AppCompatActivity() {
private val createFile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ it ->
        val resultData : Intent? = it.data
        val selectedFileUri = resultData?.data
        if (it.resultCode == Activity.RESULT_OK){
            try{
                if (resultData != null) {
                    resultData.data?.let {
                        if (selectedFileUri != null) {
                            contentResolver.openOutputStream(selectedFileUri).use { stream ->
                                stream?.writer()?.write(dataToWriteInFile())
                            }
                        }
                    }
                }
                    } catch (e: IOException){
                    e.printStackTrace()
                }
        }
    }
 private val FILENAME = "ExportedContacts.txt"


------------------------------------------------------------

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

exportDataButton = findViewById(R.id.exportDataButton)
        exportDataButton.setOnClickListener {
        createContactFile()
        }
}


------------------------------------------------------------
private fun createContactFile() {
        val openDirectoryPickerIntent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply{
            addCategory(Intent.CATEGORY_OPENABLE)
            type = "text/plain"
            putExtra(Intent.EXTRA_TITLE, FILENAME)
        }
        createFile.launch(openDirectoryPickerIntent)
}


 private fun dataToWriteInFile(): String {

        val stringBuilder = StringBuilder()
        return stringBuilder.appendLine(getContactsList().toString()).toString()

}


android kotlin android-sqlite
1个回答
0
投票

我认为问题在于使用:-

val file = File(context.getExternalFilesDir(null), filePath)

测试时(下面用于测试的代码)这会导致文件未找到捕获异常(因此代码继续)。使用设备资源管理器,目录(请注意,即使显然不需要,但已添加 mkdirs)存在,但在从 writeTextFile 调用返回后不存在文件。

更改为使用:-

val file = File(/*context.getExternalFilesDir(null),*/ filePath)

高兴地继续创建了文件,该文件随后可见,可以通过设备资源管理器打开并查看数据。

注意 进行的测试是精简版本,纯粹是为了检查 writeTextToFile 方法并确定问题(如果有)。用于测试的代码:-

  • 未改变的 SQLiteHelper 和 ContactModel 类

  • 修改以下活动代码以测试 writeTextToFile 函数

:-

class MainActivity : AppCompatActivity() {

    private val filePicker =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            val data: Intent? = result.data
            if (result.resultCode == Activity.RESULT_OK && data != null) {
                val selectedFileUri = data.data
                if (selectedFileUri != null) {
                    val selectedFilePath = selectedFileUri.toString()
                    writeTextToFile(this, selectedFilePath, dataToWriteInFile())
                }
            }

        }

    private val FILENAME = "ExportedContacts.txt"

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

        val extFilePath = this.getExternalFilesDir("testing")
        var filePath: File? = null
        val fileName: String = "testit.txt"
        if (extFilePath != null) {
            extFilePath.mkdirs()
            filePath = File("${extFilePath}${File.separator}${fileName}")
        }
        val helper = SQLiteHelper(this)
        helper.insertContact(ContactModelClass(1,"A","Z","2000-01-01","00 0000 0001","a@email","1 Nowhere Street.","ELSEWHERE","not much to say"))
        helper.insertContact(ContactModelClass(2,"B","Z","2000-02-01","00 0000 0002","b@email","1 Nowhere Street.","ELSEWHERE","not much to say"))
        helper.insertContact(ContactModelClass(3,"C","Z","2000-03-01","00 0000 0003","c@email","1 Nowhere Street.","ELSEWHERE","not much to say"))
        helper.insertContact(ContactModelClass(4,"D","Z","2000-04-01","00 0000 0004","d@email","1 Nowhere Street.","ELSEWHERE","not much to say"))
        if (filePath != null) {
            writeTextToFile(this,filePath.path,dataToWriteInFile())
        }
        val breakpoint: String = ""
    }

    private fun createContactFile() {
        val openDirectoryPickerIntent = Intent(Intent.ACTION_CREATE_DOCUMENT)
        openDirectoryPickerIntent.addCategory(Intent.CATEGORY_OPENABLE)
        openDirectoryPickerIntent.type = "text/plain"
        openDirectoryPickerIntent.putExtra(Intent.EXTRA_TITLE, FILENAME)
        filePicker.launch(openDirectoryPickerIntent)
    }


    private fun dataToWriteInFile(): String {
        val stringBuilder = StringBuilder()
        return stringBuilder.appendLine(getContactsList().toString()).toString()
    }

    private fun writeTextToFile(context: Context, filePath: String, dataToFile: String) {
        val file = File(/*context.getExternalFilesDir(null),*/ filePath)
        try {
            val fileOutputStream = FileOutputStream(file)
            val dataAsByteArray = dataToFile.toByteArray()
            val breakpoint: String = ""
            fileOutputStream.use { stream ->
                stream.write(dataToFile.toByteArray())
            }
            fileOutputStream.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    private fun getContactsList(): ArrayList<ContactModelClass> {
        val sqliteHelper = SQLiteHelper(this)
        return sqliteHelper.loadContacts()
    }
}
  • 即UI 方面已从方程式/测试中删除,所有完成的操作都在主线程上完成。

最终结果,一个文件包含:-

[1;A;Z;2000-01-01;00 0000 0001;a@email;1 Nowhere Street.;ELSEWHERE;not much to say, 2;B;Z;2000-02-01;00 0000 0002;b@email;1 Nowhere Street.;ELSEWHERE;not much to say, 3;C;Z;2000-03-01;00 0000 0003;c@email;1 Nowhere Street.;ELSEWHERE;not much to say, 4;D;Z;2000-04-01;00 0000 0004;d@email;1 Nowhere Street.;ELSEWHERE;not much to say]

但是,如果将代码更改为使用file的原始设置,则在

val breakpoint: String = ""
函数中的
onCreate
行上放置断点后:-

然后运行(卸载已安装的App id后)并单击调试(绿色Bug图标),结果如下:-

    1. 看起来不错???
  1. 在日志中

    W/System.err: java.io.FileNotFoundException: /storage/emulated/0/Android/data/a.a.so77247817kotlinsqlitewritetofile/files/storage/emulated/0/Android/data ....

    1. 哎呀
  2. 使用设备资源管理器并遵循以下路径:-

    1. 期望实际文件存在于测试目录中,但事实并非如此。
    2. 如上面工作代码所示,文件存在并且可以打开。

您可能仍然需要注意应用于该问题的注释,尽管建议的关闭已包含在 writeTestToFile 函数中。

© www.soinside.com 2019 - 2024. All rights reserved.