마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

코틀린 sqlite 질문입니다

0 추천

메모장을 만들고 있습니다

그런데 어플을 실행할때마다 자꾸 null값으로된 리사이클러뷰가 생성되는데 어떻게 해결할지 몰라서 질문합니다

class SqliteHelper(context: Context, name: String, version: Int):
                    SQLiteOpenHelper(context, name, null, version) {
    override fun onCreate(db: SQLiteDatabase?) {
        val create = "create table memo (" +
                "no integer primary key," +
                "caption text," +
                "content text," +
                "datetime text" +
                ")"

        db?.execSQL(create)
    }

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

    fun insertMemo(memo: Memo) {
        val values = ContentValues()
        values.put("caption", memo.caption)
        values.put("content", memo.content)
        values.put("datetime", memo.datetime)
        val wd = writableDatabase
        wd.insert("memo", null, values)
        wd.close()
    }

    @SuppressLint("Range")
    fun selectMemo(): MutableList<Memo> {
        val list = mutableListOf<Memo>()

        val select = "select * from memo"
        val rd = readableDatabase
        val cursor = rd.rawQuery(select, null)
        while (cursor.moveToNext()) {
            val no = cursor.getLong(cursor.getColumnIndex("no"))
            val caption = cursor.getString(cursor.getColumnIndex("caption"))
            val content = cursor.getString(cursor.getColumnIndex("content"))
            val datetime = cursor.getString(cursor.getColumnIndex("datetime"))
            list.add(Memo(no, caption, content, datetime))
        }
        cursor.close()
        rd.close()
        return list
    }

    fun updateMemo(memo: Memo) {
        val values = ContentValues()
        values.put("caption", memo.caption)
        values.put("content", memo.content)
        values.put("datetime", memo.datetime)
        val wd = writableDatabase
        wd.update("memo", values, "no = ${memo.no}", null)
    }

    fun deleteMemo(memo: Memo) {
        val delete = "delete from memo where no = ${memo.no}"
        val db = writableDatabase
        db.execSQL(delete)
        db.close()
    }
}


data class Memo(var no: Long?, var caption: String, var content: String, var datetime: String)

첫 코드는 sqlite 클래스입니다..

class MainActivity : AppCompatActivity() {

    private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
    val helper = SqliteHelper(this, "memo", 1)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        val adapter = MemoAdapter()
        adapter.listData.addAll(helper.selectMemo())
        binding.recyclerView.adapter = adapter
        binding.recyclerView.layoutManager = LinearLayoutManager(this)

        binding.btnCreateMemo.setOnClickListener {
            val intent = Intent(this, CreateMeMoActivity::class.java)
            startActivity(intent)
        }

        val title = intent.getStringExtra("title").toString()
        val content = intent.getStringExtra("content").toString()
        val time = intent.getStringExtra("date").toString()

        Log.d(">>", "getString title = ${title}")
        Log.d(">>", "getString content = ${content}")
        Log.d(">>", "getString date = ${time}")

        val memo = Memo(null, title, content, time)
        helper.insertMemo(memo)
        adapter.listData.clear()
        adapter.listData.addAll(helper.selectMemo())
        adapter.notifyDataSetChanged()
    }
}

두번째 코드는 다른 액티비티에서 인텐트로 값을 전달받은 후 toString() 하여 리사이클러뷰를 생성하는 코드입니다..

어떻게 하면 해결할 수 있는지 알려주시면 감사하겠습니다...

안드익명1 (150 포인트) 님이 2021년 12월 27일 질문

1개의 답변

0 추천
 
채택된 답변

intent.getStringExtra의 사용법이 잘못 되었습니다. API에 사용법을 확인해 보면https://developer.android.com/reference/android/content/Intent#getStringExtra(java.lang.String)

public String getStringExtra (String name)

Returns the value of an item previously added with putExtra(), or null if no String value was found.

일단 getStringExtra()가 String? 을 리턴하므로 toString()을 호출할 필요가 없구요. 키값이 존재하지 않으면 null이 리턴된다고 되어 있습니다. 따라서 null에 대한 처리를 하셔야 합니다. 

  아래처럼 null 일 경우 엘비스 오퍼레이터로 기본값은 리턴하게 하면 될 것 같습니다. 

val title = intent.getStringExtra("title") ?: ""
val content = intent.getStringExtra("content") ?: ""
val time = intent.getStringExtra("date").toString() ?: ""

 

만약 널인 경우가 없어야 한다면, 버그가 있는 거겠죠. 이 경우에는  Null일 때 Exception을 발생시켜서 앱이 크래시되도록 만드시면 됩니다. 그래야 개발시에 버그를 금방 발견할 수 있을테니까요.

val title = requireNotNull(intent.getStringExtra("title"))

// 또는 
val title = intent.getStringExtra("title")!!

 

그리고 selectMemo는 MutableList가 아니라 Readonly 인 List를 리턴하게 하세요.

fun selectMemo(): List<Memo>

 

Memo class는 var대신  val을 사용하시구요,

data class Memo(
    val no: Long?,   // 키값 같은데, nullable로 하는게 맞는지 다시 확인해 보세요
    val caption: String, 
    val content: String, 
    val datetime: String
)

 

제가 보기에 null 이 생기는 근본적인 원인은 SQLite에 null 값이 들어갔기 때문인 것 같은데, 해당 필드롤 not null 만드셔서 null이 들어가는 걸 방지하시는 게 좋을 것 같습니다.

 

그리고 가능하다면 SQLite를 직접 핸들링하시 마시고 RoomDB를 사용하세요. 그리고 selectMemo같이 데이터 처리하는 로직은 뷰에서 분리하시는게 아주 일반적인 접근방법입니다.

spark (227,470 포인트) 님이 2021년 12월 27일 답변
안드익명1님이 2021년 12월 28일 채택됨
일단 알려주신대로 엘비스 오퍼레이터로 기본값을 리턴한 후
if (title.isNotEmpty)를 사용하였더니 해결되었습니다.
no는 원래 표 순번을 표시하려고 했는데 사용하지않을거 같아서 그냥 지워버렸습니다.
SQLite를 사용한 이유는 제가 SQLite를 잘 몰라서 한번 짚어 넘어가고 싶어서 사용해봤습니다. 곧 RoomDB로 바꿔 볼 예정입니다!
질문에 답변 해주셔서 감사합니다!
...