Introduced a mutex to manage concurrency in the search functionality. This enables proper clearing of the first running job before executing a new one. By implementing this approach, we ensure that access to the libzim search results occurs one at a time, resolving the crashing issue caused by multiple attempts to access libzim resources.

* Replaced the `cancel` function for the Job with `cancelAndJoin`, ensuring that it thoroughly cancels the first job before initiating the new task.
This commit is contained in:
MohitMaliFtechiz 2023-12-12 15:31:53 +05:30
parent 6d5e50d0ef
commit a07bb680c3

View File

@ -43,7 +43,10 @@ import androidx.recyclerview.widget.RecyclerView
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.BaseActivity
@ -86,6 +89,7 @@ class SearchFragment : BaseFragment() {
private var searchAdapter: SearchAdapter? = null
private var isDataLoading = false
private var renderingJob: Job? = null
private val searchMutex = Mutex()
override fun inject(baseActivity: BaseActivity) {
baseActivity.cachedComponent.inject(this)
@ -252,22 +256,25 @@ class SearchFragment : BaseFragment() {
)
}
private fun render(state: SearchState) {
renderingJob?.cancel()
isDataLoading = false
searchInTextMenuItem?.isVisible = state.searchOrigin == FromWebView
searchInTextMenuItem?.isEnabled = state.searchTerm.isNotBlank()
fragmentSearchBinding?.searchLoadingIndicator?.isShowing(true)
renderingJob = searchViewModel.viewModelScope.launch(Dispatchers.Main) {
val searchResult = withContext(Dispatchers.IO) {
state.getVisibleResults(0, renderingJob)
}
private suspend fun render(state: SearchState) {
searchMutex.withLock {
// `cancelAndJoin` cancels the previous running job and waits for it to completely cancel.
renderingJob?.cancelAndJoin()
isDataLoading = false
searchInTextMenuItem?.isVisible = state.searchOrigin == FromWebView
searchInTextMenuItem?.isEnabled = state.searchTerm.isNotBlank()
fragmentSearchBinding?.searchLoadingIndicator?.isShowing(true)
renderingJob = searchViewModel.viewModelScope.launch(Dispatchers.Main) {
val searchResult = withContext(Dispatchers.IO) {
state.getVisibleResults(0, renderingJob)
}
fragmentSearchBinding?.searchLoadingIndicator?.isShowing(false)
fragmentSearchBinding?.searchLoadingIndicator?.isShowing(false)
searchResult?.let {
fragmentSearchBinding?.searchNoResults?.isVisible = it.isEmpty()
searchAdapter?.items = it
searchResult?.let {
fragmentSearchBinding?.searchNoResults?.isVisible = it.isEmpty()
searchAdapter?.items = it
}
}
}
}