Android片段onCreate被调用两次 [英] Android fragment onCreate called twice
问题描述
在我的应用中,我有两项活动.主要活动仅在Appbar
中具有搜索按钮,而第二个活动是可搜索的活动.第二个活动包含一个片段,该片段获取在其onCreate
调用中搜索的数据.我的问题是该片段两次提取数据.检查活动的生命周期后,我得出结论,可搜索活动在某个时刻被暂停,这显然确定了要重新创建的片段.但是我不知道是什么原因导致活动被暂停.
In my app I have two activities. The main activity that only has a search button in the Appbar
and a second, searchable, activity. The second activity hold a fragment that fetches the data searched in it's onCreate
call. My problem is that the fragment fetches the data twice. Inspecting the lifecycle of my activities, I concluded that the searchable activity gets paused at some point, which obviously determines the fragment to be recreated. But I have no idea what causes the activity to be paused.
这是我的活动
MainActivity.kt
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val root = binding.root
setContentView(root)
//Setup the app bar
setSupportActionBar(binding.toolbar);
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
return initOptionMenu(menu, this)
}
}
fun initOptionMenu(menu: Menu?, context: AppCompatActivity): Boolean {
val inflater = context.menuInflater;
inflater.inflate(R.menu.app_bar_menu, menu)
// Get the SearchView and set the searchable configuration
val searchManager = context.getSystemService(Context.SEARCH_SERVICE) as SearchManager
(menu?.findItem(R.id.app_bar_search)?.actionView as SearchView).apply {
// Assumes current activity is the searchable activity
setSearchableInfo(searchManager.getSearchableInfo(context.componentName))
setIconifiedByDefault(false) // Do not iconify the widget; expand it by default
}
return true;
}
SearchActivity.kt
SearchActivity.kt
class SearchActivity : AppCompatActivity() {
private lateinit var viewBinding: SearchActivityBinding
private var query: String? = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = SearchActivityBinding.inflate(layoutInflater)
val root = viewBinding.root
setContentView(root)
// Setup app bar
supportActionBar?.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM
supportActionBar?.setCustomView(R.layout.search_app_bar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
//Get the query string
if (Intent.ACTION_SEARCH == intent.action) {
intent.getStringExtra(SearchManager.QUERY).also {
//Add the query to the appbar
query = it
updateAppBarQuery(it)
}
}
//Instantiate the fragment
if (savedInstanceState == null) {
val fragment = SearchFragment.newInstance();
val bundle = Bundle();
bundle.putString(Intent.ACTION_SEARCH, query)
fragment.arguments = bundle;
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commitNow()
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
return initOptionMenu(menu, this)
}
private fun updateAppBarQuery(q: String?) {
supportActionBar?.customView?.findViewById<TextView>(R.id.query)?.apply {
text = q
}
}
}
如您所见,我正在使用内置的SearchManger
来处理我的搜索操作并在活动之间进行切换.我没有在文档中发现在搜索过程中我的可搜索活动可能会暂停或诸如此类的任何地方.有谁知道为什么会这样吗?预先感谢!
As you can see, I am using the built in SearchManger
to handle my search action and switching between activities. I haven't seen anywhere in the docs that during search, my searchable activity might get paused or anything like that. Does anyone have any idea why this happens? Thanks in advance!
这是我对SearchFragment
的onCreate
方法:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val query = arguments?.getString(Intent.ACTION_SEARCH);
//Create observers
val searchResultObserver = Observer<Array<GoodreadsBook>> {
searchResultListViewAdapter.setData(it)
}
viewModel.getSearchResults().observe(this, searchResultObserver)
GlobalScope.launch { //Perform the search
viewModel.search(query)
}
lifecycle.addObserver(SearchFragmentLifecycleObserver())
}
在这里,searchResultListViewAdapter
是RecyclerView
的适配器,而searchResult
是保存搜索结果的视图模型中的livedata
Here, searchResultListViewAdapter
is the adapter for a RecyclerView
and searchResult
is a livedata in the view-model holding the search result
这是SearchFragment
上第一次调用onCreate()
的堆栈跟踪:
Here is the stack trace for the first call of onCreate()
on SearchFragment
:
这是第二个电话:
这是SearchFragment
的ViewModel
:
class SearchViewModel() : ViewModel() {
private val searchResults: MutableLiveData<Array<GoodreadsBook>> by lazy {
MutableLiveData<Array<GoodreadsBook>>();
}
fun getSearchResults(): LiveData<Array<GoodreadsBook>> {
return searchResults;
}
// TODO: Add pagination
suspend fun search(query: String?) = withContext(Dispatchers.Default) {
val callback: Callback = object : Callback {
override fun onFailure(call: Call, e: IOException) {
// TODO: Display error message
}
override fun onResponse(call: Call, response: Response) {
// TODO: Check res status
val gson = Gson();
val parsedRes = gson.fromJson(
response.body?.charStream(),
Array<GoodreadsBook>::class.java
);
// Create the bitmap from the imageUrl
searchResults.postValue(parsedRes)
}
}
launch { searchBook(query, callback) }
}
}
自从发布此消息以来,我对应用程序进行了一些更改,现在由于某种原因,主分支中的搜索无法正常工作. ViewModel
是从我发布此文章时更近的一个分支开始的.这是当前的ViewModel
,尽管此变体中也存在问题:
I made some changes to the app since posted this and right now the search doesn't work for some reason in the main branch. This ViewModel
it's from a branch closer to the time I posted this. Here is the current ViewModel
, although the problem is present in this variant as well:
class SearchViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
// private val searchResults: MutableLiveData<Array<GoodreadsBook>> by lazy {
//// MutableLiveData<Array<GoodreadsBook>>();
//// }
companion object {
private const val SEARCH_RESULTS = "searchResults"
}
fun getSearchResults(): LiveData<Array<GoodreadsBook>> =
savedStateHandle.getLiveData<Array<GoodreadsBook>>(SEARCH_RESULTS)
// TODO: Add pagination
fun search(query: String?) {
val searchResults = savedStateHandle.getLiveData<Array<GoodreadsBook>>(SEARCH_RESULTS)
if (searchResults.value == null)
viewModelScope.launch {
withContext(Dispatchers.Default) {
//Handle the API response
val callback: Callback = object : Callback {
override fun onFailure(call: Call, e: IOException) {
// TODO: Display error message
}
override fun onResponse(call: Call, response: Response) {
// TODO: Check res status
val gson = Gson();
val parsedRes = gson.fromJson(
response.body?.charStream(),
Array<GoodreadsBook>::class.java
);
searchResults.postValue(parsedRes)
}
}
launch { searchBook(query, callback) }
}
}
}
}
searchBook
函数仅执行对API的HTTP请求,所有数据操作均在viewModel中处理
The searchBook
function just performs the HTTP request to the API, all the data manipulation is handled in the viewModel
推荐答案
我认为负责文档的Android团队应该做得更好.我继续前进,只是从SearchView
中删除了SearchManager
并直接使用了onQueryTextListener
,只是看到通过这种方法,我的监听器也被调用了两次.但是感谢这篇帖子,我看到了显然这是模拟器的错误(或SearchView
处理Submit事件的方式).因此,如果我按下OSK输入按钮,一切都会按预期进行.
I think the Android team in charge of the documentation should really do a better job. I went ahead and just removed the SearchManager
from the SearchView
and use the onQueryTextListener
directly, only to see that with this approach I also get my listener called twice. But thanks to this post, I saw that apparently it's a bug with the emulator (or with the way SearchView
handles the submit event). So if I press the OSK enter button everything works as expected.
感谢大家的帮助!
这篇关于Android片段onCreate被调用两次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!