首页 > 解决方案 > Android Room getById(id: Int) 查询从第一个片段调用时返回一个有效对象,但从所有其他片段调用时返回 null

问题描述

我正在做我的学校项目,我开始出现这种奇怪的房间行为。我不得不承认,过去一切都可以正常工作,但是在更改了一些东西之后它停止了,现在即使我将几乎所有东西都恢复到原来的位置,它也无法正常工作。

这是我的 UserDao.kt:

@Dao
interface UserDao {

    @Query("SELECT * FROM $USER_TABLE_NAME")
    fun getAll(): LiveData<List<User>>

    @Query("SELECT * FROM $USER_TABLE_NAME WHERE id = :id")
    fun getById(id: Int): LiveData<User>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(user: User)

    @Update
    fun update(user: User)

    @Query("UPDATE $USER_TABLE_NAME SET pictureAddress = :image WHERE id = :id")
    fun updateImageWhereId(id: Int, image: String)

    @Delete
    fun delete(user: User)
}

这是 LoginFragment.kt,第一个片段,在应用程序启动时被加载。在这里,我检查数据库中是否有用户,如果有,我检查密码是否匹配。这是查询返回它应该返回的用户的地方,并且一切正常。

class LoginFragment : Fragment() {

    private lateinit var binding: FragmentLoginBinding
    private lateinit var model: MainViewModel
    private val navigation: INavigationCallback by lazy {
        activity as INavigationCallback
    }

    data class IdPassword(var id: String = "", var password: String = "", var isCorrect: Boolean = true)
    private val idPassword: IdPassword = IdPassword()

    override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View? =
            DataBindingUtil.inflate<FragmentLoginBinding>(inflater,
                    R.layout.fragment_login,
                    container, false).run {
                binding = this
                model = activity!!.let {
                    ViewModelProviders
                            .of(it, MainViewModel.Factory(it.application))
                            .get(MainViewModel::class.java)
                }
                lifecycleOwner = this@LoginFragment
                idPassword = this@LoginFragment.idPassword
                navigation = this@LoginFragment.navigation
                applyLoginButton.setOnClickListener { tryLogin() }

                return root
            }


    private fun tryLogin() {
        //databaseData.getUserById - is basically a wrapper
        model.databaseData.getUserById(idPassword.id.toInt()).observe(activity!!, Observer {
            if(it != null && it.password == idPassword.password){
                model.activeUserId = it.id  //this stores active user's id
                navigation.navigateTo(R.id.action_loginFragment_to_mainMenuFragment)
            } else {
                binding.errorLogin.visibility = View.VISIBLE
            }
        })
    }
}

登录成功后,我们进入主菜单屏幕,在这里我需要再次获取用户,以显示他的姓名和头像。这是MainMenuFragment.kt,与上面相同的查询返回 null 的地方。我也尝试对此进行测试,因此请阅读评论以更好地理解我所做的事情:

class MainMenuFragment : Fragment() {


    private lateinit var binding: FragmentMainMenuBinding

    private val viewEffect: MainMenuViewUtils by lazy {
        MainMenuViewUtils(binding.userNameSmall, binding.mainLinearLayoutTitle)
    }

    private val globalLayoutListener = object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            binding.root.viewTreeObserver.removeOnGlobalLayoutListener(this)
            viewEffect.adjustCardSize(activity!!, binding.todayStatsCard)
        }
    }

    override fun onCreateView(inflater: LayoutInflater,
                              container: ViewGroup?,
                              savedInstanceState: Bundle?): View? = DataBindingUtil
            .inflate<FragmentMainMenuBinding>(inflater,
                    R.layout.fragment_main_menu,
                    container, false).run {
                binding = this
                lifecycleOwner = this@MainMenuFragment
                navigation = activity as INavigationCallback
                viewModel = activity!!.let {
                    ViewModelProviders
                            .of(it, MainViewModel.Factory(it.application))
                            .get(MainViewModel::class.java)
                }
                viewModel?.let {
                    user = it.activeUser
                    it.setUpMainMenuObservers()
                }
                mainMenuAppbar.addOnOffsetChangedListener(
                        AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
                            viewEffect.onOffsetChanged(appBarLayout, verticalOffset)
                        })
                profilePicture.setOnClickListener {
                    Dialogs.profilePictureDialog(activity!!, viewModel!!).show()
                }
                mainMenuToolbar.setUpMainMenuToolbar(navigation!!)
                root.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
                viewEffect.startAlphaAnimation(userNameSmall, 0, View.INVISIBLE)
                setHasOptionsMenu(true)

                return root
            }

    private fun setUpBluetooth(): Boolean {
        val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
        activity?.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
        return true
    }

    private fun MainViewModel.setUpMainMenuObservers(){


        //this is the original call which doesn't work
        activeUser.observe(activity!!, Observer { 
            log("Active user id = $activeUserId")   //this prints correct id
            if(it == null) log("USER IS NULL")
            else log("USER : ${it.name}")  // and this prints that user is null, so it wasn't found
            tryCatch {
                binding.profilePicture.setImageBitmap(getProfilePicture(it, 256))
            }
        })


        //this was added for testing purposes. It returns a list of all users in the db.
        databaseData.getAllUsers.observe(activity!!, Observer {
            log("ALL USERS:")
            it.forEach{user: User ->
                log("NAME : ${user.name}, ID = ${user.id}") //this prints each user with the correct name and id
            }
            log("Active user id = $activeUserId")  //this prints correct active user id
            val user = it.first { id == activeUserId }    // on this line app crashes, as if 1 != 1.

            log("USER : ${user.name}")
            tryCatch {
                binding.profilePicture.setImageBitmap(getProfilePicture(user, 256))
            }
        })


        bluetoothData.steps.observe(activity!!, Observer {
            binding.stepsTodayCounter.text = it.toString()
        })
        bluetoothData.location.observe(activity!!, Observer {
            val text = "${lastDayData.getLastDayDistanceFormatted()} Km"
            binding.distanceTodayCounter.text = text
        })
        bluetoothData.isBluetoothConnected.observe(activity!!, Observer {
            when (it) {
                true -> binding.changeOnBTConnected("Connected!", R.drawable.bt_connected_icon)
                false -> binding.changeOnBTConnected("Not Connected!", R.drawable.bt_disconnected_icon)
            }
        })
    }

    private fun FragmentMainMenuBinding.changeOnBTConnected(changeText: String, changeDrawable: Int){
        connectionStatus.text = changeText
        mainMenuToolbar.menu?.findItem(R.id.menu_bluetooth)?.icon =
                activity!!.getDrawable(changeDrawable)
        Toast.makeText(activity!!, "You are $changeText!", Toast.LENGTH_LONG).show()
    }

    private fun Toolbar.setUpMainMenuToolbar(navigation: INavigationCallback) {
        inflateMenu(R.menu.main_view_menu)
        menu.findItem(R.id.menu_bluetooth).setOnMenuItemClickListener {
            setUpBluetooth()
        }
        menu.findItem(R.id.menu_settings).setOnMenuItemClickListener {
            navigation.navigateTo(R.id.action_mainMenuFragment_to_settingsFragment)
            true
        }
    }



}

所以从评论中你可以理解它有多奇怪。因此,在测试查询中,我会在控制台中打印如下:

 NAME : Slava Simonov, ID = 1
 NAME : Uzi, ID = 11
 Active user id = 1
 Shutting down VM
 FATAL EXCEPTION: main
 ...

我希望有人能够帮助我,因为我已经没有想法了,为什么会发生这种情况。感觉像是某种错误。如果您需要我的其他代码,我可以附上它,请随时询问。提前感谢大家。

标签: androidkotlinandroid-roomandroid-architecture-componentsandroid-livedata

解决方案


推荐阅读