【问题标题】:SavedStateHandle returning null valuesSavedStateHandle 返回空值
【发布时间】:2020-05-20 20:00:48
【问题描述】:

我有一个ActionBar 菜单图标,可以打开一个CategoryFragment。该片段接受从另一个片段传递的category 对象SafeArgs 参数。在CategoryFragment 中,我将category 的名称和ID 存储到片段的共享ViewModel 中作为SavedStateHandle 值。我已经对其进行了设置,以便片段在需要时使用存储的 SavedStateHandle 值作为类别名称和 id。例如,CategoryFragment 第一次使用从发送片段传递的类别对象,但随后创建CategoryFrgament 将使用SavedStateHandle 值。

问题是,如果先打开CategoriesFragment,然后通过按手机的物理后退按钮或从导航栏中手机的最近按钮终止应用程序退出应用程序,现在通过按直接打开CategoryFragment ActionBar 菜单图标显示一个空白屏幕。这是因为从SavedStateHandle 返回的值是空的。我该如何解决这个问题?

类别片段

class CategoryFragment : Fragment(), SearchView.OnQueryTextListener {

    lateinit var navController: NavController
    private var adapter: TasksRecyclerAdapter? = null
    private val viewModel: CategoryTasksViewModel by activityViewModels()
    private var fromCategoriesFragment: Boolean = false

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_category, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        navController = Navigation.findNavController(view)

        observerSetup()
        recyclerSetup()

        var searchView = category_tasks_searchview
        searchView.setOnQueryTextListener(this)

        fab_new_task.setOnClickListener {
            navController.navigate(R.id.action_categoryFragment_to_newTaskDialogFragment)
        }

        showTasks()
    }

private fun showTasks() {

        if(fromCategoriesFragment){

            PomoPlayObservablesSingleton.fromCategoriesFragment.onNext(false)

            if (!arguments?.isEmpty!!) {

                var args = CategoryFragmentArgs.fromBundle(arguments!!)
                category_title.text = args.category?.name

                var category = args.category
                viewModel.setPomoCategoryName(category.name)
                viewModel.setCategoryId(category.id)
                viewModel.searchTasksByCategoryId(category.id)

            }

        }
        else{
            category_title.text = viewModel.getPomoCategoryName()
            viewModel.searchTasksByCategoryId(viewModel.getCategoryId())

            Log.i("CategoryFrag-CatName", viewModel.getPomoCategoryName().toString())
            Log.i("CategoryFrag-CatId", viewModel.getCategoryId().toString())
        }
    }

    private fun observerSetup() {

        viewModel.getSearchTasksByCategoryIdResults().observe(this,androidx.lifecycle.Observer { tasks ->

            if(tasks.isNotEmpty()){

                adapter?.setTasksList(tasks.sortedBy { task -> task.name?.toLowerCase() })
                task_not_found_bubble.visibility = View.GONE
                task_not_found_text.visibility = View.GONE
            }
            else{
                task_not_found_bubble.visibility = View.VISIBLE
                task_not_found_text.visibility = View.VISIBLE
            }
        })

        PomoPlayObservablesSingleton.fromCategoriesFragment.subscribe {value -> fromCategoriesFragment = value}

    }

    private fun recyclerSetup() {
        adapter = context?.let { TasksRecyclerAdapter(it) }
        tasks_list?.layoutManager = LinearLayoutManager(context)
        tasks_list?.adapter = adapter
    }

    override fun onQueryTextSubmit(query: String?): Boolean {

        Log.i("Lifecycle-CatFragment", "onQueryTextSubmit() called")

        var q = query?.toLowerCase()?.trim()?.replace("\\s+".toRegex(), " ")
        setLastSearchQuery(q.toString())

        viewModel.searchTasksByName(viewModel.getLastSearchQuery().toString())

        return false
    }

    private fun setLastSearchQuery(lastSearchQuery: String) {
        viewModel.setLastSearchQuery(lastSearchQuery)
    }
}

CategoryTasksViewModel

class CategoryTasksViewModel(application: Application, state: SavedStateHandle) : AndroidViewModel(application) {

    private val repository: PomoPlayRepository = PomoPlayRepository(application)
    private val allCategories: LiveData<List<Category>>?
    private val allPomoTasks: LiveData<List<PomoTask>>?
    private val searchCategoriesByNameResults: MutableLiveData<List<Category>>
    private val searchCategoryByIdResults: MutableLiveData<Category>
    private val searchTasksByIdResults: MutableLiveData<PomoTask>
    private val searchTasksByNameResults: MutableLiveData<List<PomoTask>>
    private val searchTasksByCategoryIdResults: MutableLiveData<List<PomoTask>>

    private val savedStateHandle = state

    companion object{

        private const val LAST_SEARCH_QUERY = "lastSearchQuery"

    }

    init {
        allCategories = repository.allCategories
        allPomoTasks = repository.allPomoTasks
        searchTasksByIdResults = repository.searchTasksByIdResults
        searchTasksByNameResults = repository.searchTasksByNameResults
        searchTasksByCategoryIdResults = repository.searchTasksByCategoryIdResults
        searchCategoryByIdResults = repository.searchCategoriesByIdResults
        searchCategoriesByNameResults = repository.searchCategoriesByNameResults
    }

    fun setLastSearchQuery(lastSearchName: String){
        savedStateHandle.set(LAST_SEARCH_QUERY, lastSearchName)
    }

    fun getLastSearchQuery(): String?{
        return savedStateHandle.get<String>(LAST_SEARCH_QUERY)
    }

    fun setPomoCategoryName(name: String?){
        savedStateHandle.set("categoryName", name)
    }

    fun getPomoCategoryName(): String?{
        return savedStateHandle.get<String>("categoryName")
    }

    fun setCategoryId(id: Int){
        savedStateHandle.set("categoryId", id)
    }

    fun getCategoryId(): Int?{
        return savedStateHandle.get<Int>("categoryId")
    }

    fun insertTask(pomoTask: PomoTask?) {
        repository.insertTask(pomoTask)
    }

    fun deleteTask(pomoTask: PomoTask) {
        repository.deleteTask(pomoTask)
    }

    fun updateTask(pomoTask: PomoTask) {
        repository.updateTask(pomoTask)
    }


    fun searchTasksByName(name: String) {
        repository.searchTasksByName(name)
    }


    fun searchTasksById(pomoTaskId: Int){
        repository.searchTasksById(pomoTaskId)
    }

    fun searchTasksByCategoryId(categoryId: Int?){
        repository.searchTasksByCategoryId(categoryId)
    }

    fun getAllPomoTasks() : LiveData<List<PomoTask>>? {
        return allPomoTasks
    }

    fun getSearchTasksbyNameResults() : MutableLiveData<List<PomoTask>> {
        return searchTasksByNameResults
    }

    fun getSearchTasksByIdResults() : MutableLiveData<PomoTask> {
        return searchTasksByIdResults
    }

    fun getSearchTasksByCategoryIdResults() : MutableLiveData<List<PomoTask>> {
        return searchTasksByCategoryIdResults
    }

}

【问题讨论】:

    标签: android android-architecture-components android-savedstate android-safe-args


    【解决方案1】:

    SavedStateHandle 不是设计用来做的,你期望它做的事情:它...

    ... 是一个键值映射,可让您编写和检索对象 往返已保存状态。这些值将在 进程被系统杀死并通过相同的方式保持可用 对象。

    被系统杀死,如果用户故意关闭应用程序甚至销毁(“永久离开”)作为其作用域的 Fragment/Activity,则不会。请参阅Saving UI State - User-initiated UI state dismissal 上的文档:

    在这些完全解雇案例中,用户的假设是他们 已永久离开活动,如果他们重新打开 他们期望活动从干净状态开始的活动。这 这些解雇场景的基础系统行为与 用户期望 - 活动实例将被销毁并 从内存中删除,以及存储在其中的任何状态以及任何已保存的状态 与活动关联的实例状态记录。

    也许将您希望在您的场景中幸存下来的信息保存在SharedPreferences

    【讨论】:

      猜你喜欢
      • 2021-07-07
      • 1970-01-01
      • 2021-12-21
      • 2015-11-07
      • 2020-11-08
      • 2013-10-09
      • 2012-09-25
      • 2012-03-29
      • 2019-11-24
      相关资源
      最近更新 更多