SavedStateHandle 返回空值
SavedStateHandle returning null values
我有一个 ActionBar
菜单图标可以打开 CategoryFragment
。此片段接受从另一个片段传递的 category
对象 SafeArgs
参数。在 CategoryFragment
中,我将 category
的名称和 ID 作为 SavedStateHandle
值存储到片段的共享 ViewModel
中。我已对其进行设置,以便该片段在需要时使用存储的 SavedStateHandle
值作为类别名称和 ID。例如,CategoryFragment
第一次使用从发送片段传递的类别对象,但随后创建 CategoryFrgament
将使用 SavedStateHandle
值。
问题是,如果在第一次打开 CategoriesFragment
之后,然后通过按 phone 的物理后退按钮或从 phone 终止应用程序退出应用程序导航栏中最近的按钮,现在通过按 ActionBar 菜单图标直接打开 CategoryFragment
显示空白屏幕。这是因为从 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
}
}
SavedStateHandle
的设计目的不是您希望它做的:它 ...
... is a key-value map that will let you write and retrieve objects
to and from the saved state. These values will persist after the
process is killed by the system and remain available via the same
object.
被系统杀死,如果用户故意关闭应用程序甚至破坏(“永久导航”)作为其范围的Fragment/Activity则不会。请参阅 Saving UI State - User-initiated UI state dismissal:
上的文档
The user's assumption in these complete dismissal cases is that they
have permanently navigated away from the activity, and if they re-open
the activity they expect the activity to start from a clean state. The
underlying system behavior for these dismissal scenarios matches the
user expectation - the activity instance will get destroyed and
removed from memory, along with any state stored in it and any saved
instance state record associated with the activity.
也许可以将您希望在场景中幸存下来的信息保存在 SharedPreferences。
我有一个 ActionBar
菜单图标可以打开 CategoryFragment
。此片段接受从另一个片段传递的 category
对象 SafeArgs
参数。在 CategoryFragment
中,我将 category
的名称和 ID 作为 SavedStateHandle
值存储到片段的共享 ViewModel
中。我已对其进行设置,以便该片段在需要时使用存储的 SavedStateHandle
值作为类别名称和 ID。例如,CategoryFragment
第一次使用从发送片段传递的类别对象,但随后创建 CategoryFrgament
将使用 SavedStateHandle
值。
问题是,如果在第一次打开 CategoriesFragment
之后,然后通过按 phone 的物理后退按钮或从 phone 终止应用程序退出应用程序导航栏中最近的按钮,现在通过按 ActionBar 菜单图标直接打开 CategoryFragment
显示空白屏幕。这是因为从 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
}
}
SavedStateHandle
的设计目的不是您希望它做的:它 ...
... is a key-value map that will let you write and retrieve objects to and from the saved state. These values will persist after the process is killed by the system and remain available via the same object.
被系统杀死,如果用户故意关闭应用程序甚至破坏(“永久导航”)作为其范围的Fragment/Activity则不会。请参阅 Saving UI State - User-initiated UI state dismissal:
上的文档The user's assumption in these complete dismissal cases is that they have permanently navigated away from the activity, and if they re-open the activity they expect the activity to start from a clean state. The underlying system behavior for these dismissal scenarios matches the user expectation - the activity instance will get destroyed and removed from memory, along with any state stored in it and any saved instance state record associated with the activity.
也许可以将您希望在场景中幸存下来的信息保存在 SharedPreferences。