如何在 Activity 中使用 Room 和 LiveData 删除 LiveData<Object>?

Posted

技术标签:

【中文标题】如何在 Activity 中使用 Room 和 LiveData 删除 LiveData<Object>?【英文标题】:How can I delete a LiveData<Object> using Room and LiveData inside an Activity? 【发布时间】:2019-06-06 00:25:44 【问题描述】:

我正在使用 Kotlin 和 android 架构组件(LiveData 和 Room)制作一个 Android 应用。

我有一个显示用户的活动(存储在数据库中并使用带有 ViewModel 的查询恢复)。

我必须拥有编辑或删除当前用户的选项。问题是,当我尝试删除用户时,应用程序崩溃了,但是当我再次打开它时,用户就消失了。

这是我的完整课程代码:

class DetallesPerfilActivity : AppCompatActivity() 

private var user_id : Int = -1
lateinit var usuarioViewModel: UsuarioViewModel
private lateinit var usuarioActual : Usuario

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

    toolbar.title = getString(R.string.detalle_usuario)
    setSupportActionBar(toolbar)
    val ab = supportActionBar
    ab!!.setDisplayHomeAsUpEnabled(true)

    user_id = intent.getIntExtra("USER_ID", -1)

    usuarioViewModel = ViewModelProviders.of(this).get(UsuarioViewModel::class.java)

    usuarioViewModel.getUsuario(user_id).observe(this, Observer 
        usuarioActual = it!!
        populateUserFieldsFromDB()
    )



override fun onCreateOptionsMenu(menu: Menu): Boolean 
    menuInflater.inflate(R.menu.menu_edit,menu)
    return true


override fun onOptionsItemSelected(item: MenuItem?): Boolean 
    when(item?.itemId)
        android.R.id.home ->
            onBackPressed()
            return true
        
        R.id.edit_item ->
            val builder = AlertDialog.Builder(this@DetallesPerfilActivity)
            builder.setItems(R.array.dialogo_editar_eliminar)
                dialog, which ->
                when(which)
                    0-> 
                        val nav = Intent(this@DetallesPerfilActivity, RegistrarUsuarioActivity::class.java)
                        nav.putExtra("USER_ID", user_id)
                        startActivityForResult(nav,349)
                    
                    1 -> 
                        val innerBuilder = AlertDialog.Builder(this@DetallesPerfilActivity)
                        innerBuilder.setTitle(getString(R.string.eliminar_usuario))
                                .setMessage(getString(R.string.esta_seguro_que_desea_eliminar_usuario))
                                .setPositiveButton(getString(R.string.si))
                                    dialog, id ->
                                    deleteUser()
                                
                                .setNegativeButton(getString(R.string.no))
                                    dialog, id ->
                                
                        val innerDialog = innerBuilder.create()
                        innerDialog.show()
                    
                
            
            val dialog = builder.create()
            dialog.show()
            return true
        
    
    return super.onOptionsItemSelected(item)



private fun populateUserFieldsFromDB()

        NombreApellidosUsuarioTV.text = "$usuarioActual.nombre $usuarioActual.apellidos"
        GeneroUsuarioTV.text = usuarioActual.genero
        EdadUsuarioTV.text = usuarioActual.edad.toString()



private fun deleteUser()
        usuarioViewModel.delete(usuarioActual)
        Toast.makeText(this@DetallesPerfilActivity,getString(R.string.usuario_eliminado_correctamente),Toast.LENGTH_SHORT).show()
        finish()

我得到的错误是在 logcat 中:

    2019-01-10 22:29:09.573 2984-3066/? E/WindowManager: win=Window6686c65 u0 com.kps.spart.moskimedicationreminder/com.kps.spart.moskimedicationreminder.MainActivity EXITING destroySurfaces: appStopped=false win.mWindowRemovalAllowed=false win.mRemoveOnExit=false win.mViewVisibility=8, caller=com.android.server.wm.AppWindowToken.destroySurfaces:748 com.android.server.wm.AppWindowToken.destroySurfaces:732 com.android.server.wm.WindowState.onExitAnimationDone:5523 com.android.server.wm.AppWindowAnimator.stepAnimationLocked:517 com.android.server.wm.AppWindowToken.stepAppWindowsAnimation:1745 
2019-01-10 22:29:12.443 1269-1269/com.kps.spart.moskimedicationreminder E/ViewRootImpl: sendUserActionEvent() returned.
2019-01-10 22:29:12.465 2984-3066/? E/WindowManager: win=Window667c3c2 u0 com.kps.spart.moskimedicationreminder/com.kps.spart.moskimedicationreminder.DetallesPerfilActivity EXITING destroySurfaces: appStopped=false win.mWindowRemovalAllowed=true win.mRemoveOnExit=true win.mViewVisibility=0, caller=com.android.server.wm.AppWindowToken.destroySurfaces:748 com.android.server.wm.AppWindowToken.destroySurfaces:732 com.android.server.wm.WindowState.onExitAnimationDone:5523 com.android.server.wm.WindowStateAnimator.stepAnimationLocked:553 com.android.server.wm.DisplayContent.lambda$-com_android_server_wm_DisplayContent_21292:465 
2019-01-10 22:29:13.591 1269-1269/com.kps.spart.moskimedicationreminder E/ViewRootImpl: sendUserActionEvent() returned.
2019-01-10 22:29:13.634 1269-1269/com.kps.spart.moskimedicationreminder E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.kps.spart.moskimedicationreminder, PID: 1269
    kotlin.KotlinNullPointerException
        at com.kps.spart.moskimedicationreminder.DetallesPerfilActivity$onCreate$1.onChanged(DetallesPerfilActivity.kt:43)
        at com.kps.spart.moskimedicationreminder.DetallesPerfilActivity$onCreate$1.onChanged(DetallesPerfilActivity.kt:23)
        at android.arch.lifecycle.LiveData.considerNotify(LiveData.java:109)
        at android.arch.lifecycle.LiveData.dispatchingValue(LiveData.java:126)
        at android.arch.lifecycle.LiveData.setValue(LiveData.java:282)
        at android.arch.lifecycle.LiveData$1.run(LiveData.java:87)
        at android.os.Handler.handleCallback(Handler.java:790)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:7000)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:441)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)
2019-01-10 22:29:13.732 1958-1958/? E/Zygote: isWhitelistProcess - Process is Whitelisted
2019-01-10 22:29:14.519 4660-4799/? E/PBSessionCacheImpl: sessionId[20496443867522706] not persisted.
2019-01-10 22:29:15.547 2984-3636/? E/Watchdog: !@Sync 1101 [2019-01-10 22:29:15.547]
2019-01-10 22:29:34.739 2027-2027/? E/FeatureClassSet: [#CMH#] Rubin package not supported 
2019-01-10 22:29:43.943 6319-6637/? E/BtGatt.GattService: [GSIM LOG]: gsimLogHandler, msg: MESSAGE_SCAN_START, appName: com.google.uid.shared, scannerId: 4, reportDelayMillis=0
2019-01-10 22:29:44.619 2052-2052/? E/Zygote: isWhitelistProcess - Process is Whitelisted
2019-01-10 22:29:45.540 3655-3695/? E/RequestManager_FLP: [LocationManagerService] Location remove 552b5a4 from system
2019-01-10 22:29:45.554 2984-3636/? E/Watchdog: !@Sync 1102 [2019-01-10 22:29:45.554]
2019-01-10 22:29:45.896 6319-6637/? E/BtGatt.GattService: [GSIM LOG]: gsimLogHandler, msg: MESSAGE_SCAN_STOP, appName: com.google.uid.shared, scannerId: 4, reportDelayMillis=0
2019-01-10 22:29:54.628 2081-2081/? E/Zygote: isWhitelistProcess - Process is Whitelisted
2019-01-10 22:29:54.963 2081-2081/? E/zygote: The String#value field is not present on Android versions >= 6.0
2019-01-10 22:29:55.147 2081-2081/? E/TTS: Unparsable line in file with voice data checksums: voices-list.dev/signature.sf 7846532c8eb3d4d374813dae6d74638b
2019-01-10 22:29:55.147 2081-2081/? E/TTS: Unparsable line in file with voice data checksums: voices-list.dev/voices-list-dsig.pb c1024b1416240bb24b316bac696f5cdb
2019-01-10 22:29:55.148 2081-2081/? E/TTS: Unparsable line in file with voice data checksums: voices-list.rel/signature.sf 93ee1641133be6e6d8cb83934833cd8c
2019-01-10 22:29:55.148 2081-2081/? E/TTS: Unparsable line in file with voice data checksums: voices-list.rel/voices-list-rsig.pb 8ad16260ab46941c146c4598d78862ee
2019-01-10 22:29:55.574 2081-2107/? E/native: compressed_store.h:386 Read: Failed to read compressed states.
2019-01-10 22:30:13.464 3531-3531/? E/KeyguardFingerPrint: updateFingerprintListeningState#mFingerprintRunningState=0 shouldListenForFingerprint=true
2019-01-10 22:30:13.464 3531-3531/? E/KeyguardFingerPrint: startListeningForFingerprint()

那么,如何使用 ViewModel 删除 LiveData 对象而不导致应用崩溃?

【问题讨论】:

顺便问一下BD是什么意思? 使用房间制作并使用 usuarioViewModel 查询的 SQLite BD。 这就是我要问的。 BD的完整形式是什么? D 最有可能代表数据库。但是 B 代表什么? 顺便回答了你的问题。请看一下。 对不起,这是一个错字(我感到羞耻),它是 DB(数据库) 【参考方案1】:

所以,我终于找到了解决这个问题的方法,当我用 usuarioViewModel 删除 usuario 时产生了错误,观察者得到一个空引用,为了解决这个问题,我需要删除 LiveData 的观察者并删除用户之后。

首先我保留对当前 LiveData 的引用:

    usuarioViewModel = ViewModelProviders.of(this).get(UsuarioViewModel::class.java)

    usuarioActualLive =  usuarioViewModel.getUsuario(user_id)
    usuarioActualLive.observe(this, Observer 
        populateUserFieldsFromDB(it)

    )

所以,当我想删除当前用户时,我会删除匿名观察者:

private fun deleteUser()
    if(usuarioActualLive.hasObservers())
        usuarioActualLive.removeObservers(this@DetallesPerfilActivity)
        usuarioViewModel.delete(usuarioActualLive.value!!)
        finish()
    

【讨论】:

这很有帮助。谢谢【参考方案2】:

一种简单的方法是对观察值添加一个空检查。

 usuarioViewModel.getUsuario(user_id).observe(this, Observer 
    if(it!=null)
        usuarioActual = it
        populateUserFieldsFromDB()
    
)

【讨论】:

【参考方案3】:

我使用了数据绑定,而 Eduardo Corona 的解决方案对我不起作用。最后,我使用Transformations进行转换,避免直接观察b

  var note:LiveData<Note> = Transformations.map(_note)it?:Note()

【讨论】:

以上是关于如何在 Activity 中使用 Room 和 LiveData 删除 LiveData<Object>?的主要内容,如果未能解决你的问题,请参考以下文章

Android Room - 使用自动生成获取新插入行的 id - MVVM 版本

Google Clas-s-room APIS:阅读课程中的公告、问题和作业

Activity 无法转换为 LifecycleOwner

Android Kotlin Room 与Flow的应用 demo 添加数据并展示

如何使用 Room 和 LiveData 保持 RecyclerView 顺序的更改?

Android Room 通用 DAO