mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 10:46:53 -04:00
Merge pull request #4105 from kiwix/Fixes#4104
Fixed: Sometimes application crashes due to a native crash when we frequently load the ZIM files in the reader.
This commit is contained in:
commit
fff3c3ec22
@ -18,6 +18,7 @@
|
||||
|
||||
package org.kiwix.kiwixmobile.note
|
||||
|
||||
import android.os.Build
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.net.toUri
|
||||
@ -179,7 +180,10 @@ class NoteFragmentTest : BaseActivityTest() {
|
||||
assertNoteSaved()
|
||||
pressBack()
|
||||
}
|
||||
LeakAssertions.assertNoLeaks()
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
|
||||
// temporary disabled on Android 25
|
||||
LeakAssertions.assertNoLeaks()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
package org.kiwix.kiwixmobile.page.history
|
||||
|
||||
import android.os.Build
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.net.toUri
|
||||
@ -151,7 +152,10 @@ class NavigationHistoryTest : BaseActivityTest() {
|
||||
pressBack()
|
||||
pressBack()
|
||||
}
|
||||
LeakAssertions.assertNoLeaks()
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
|
||||
// temporary disabled on Android 25
|
||||
LeakAssertions.assertNoLeaks()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
package org.kiwix.kiwixmobile.reader
|
||||
|
||||
import android.os.Build
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.net.toUri
|
||||
@ -137,7 +138,10 @@ class KiwixReaderFragmentTest : BaseActivityTest() {
|
||||
pressBack()
|
||||
checkZimFileLoadedSuccessful(R.id.readerFragment)
|
||||
}
|
||||
LeakAssertions.assertNoLeaks()
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
|
||||
// temporary disabled on Android 25
|
||||
LeakAssertions.assertNoLeaks()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -17,6 +17,7 @@
|
||||
*/
|
||||
package org.kiwix.kiwixmobile.search
|
||||
|
||||
import android.os.Build
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.net.toUri
|
||||
@ -221,7 +222,10 @@ class SearchFragmentTest : BaseActivityTest() {
|
||||
assertArticleLoaded()
|
||||
}
|
||||
removeTemporaryZimFilesToFreeUpDeviceStorage()
|
||||
LeakAssertions.assertNoLeaks()
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
|
||||
// temporary disabled on Android 25
|
||||
LeakAssertions.assertNoLeaks()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -57,6 +57,7 @@ import org.kiwix.kiwixmobile.zimManager.Fat32Checker
|
||||
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.Companion.FOUR_GIGABYTES_IN_KILOBYTES
|
||||
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.CannotWrite4GbFile
|
||||
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.DetectingFileSystem
|
||||
import org.kiwix.libzim.Archive
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileNotFoundException
|
||||
@ -418,13 +419,16 @@ class CopyMoveFileHandler @Inject constructor(
|
||||
}
|
||||
|
||||
suspend fun isValidZimFile(destinationFile: File): Boolean {
|
||||
var archive: Archive? = null
|
||||
return try {
|
||||
// create archive object, and check if it has the mainEntry or not to validate the ZIM file.
|
||||
val archive = ZimReaderSource(destinationFile).createArchive()
|
||||
archive = ZimReaderSource(destinationFile).createArchive()
|
||||
archive?.hasMainEntry() == true
|
||||
} catch (ignore: Exception) {
|
||||
// if it is a invalid ZIM file
|
||||
false
|
||||
} finally {
|
||||
archive?.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,6 @@ import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kiwix.kiwixmobile.R
|
||||
@ -93,7 +92,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
|
||||
private fun openPageInBookFromNavigationArguments() {
|
||||
showProgressBarWithProgress(30)
|
||||
val args = KiwixReaderFragmentArgs.fromBundle(requireArguments())
|
||||
lifecycleScope.launch {
|
||||
coreReaderLifeCycleScope?.launch {
|
||||
if (args.pageUrl.isNotEmpty()) {
|
||||
if (args.zimFileUri.isNotEmpty()) {
|
||||
tryOpeningZimFile(args.zimFileUri)
|
||||
@ -120,6 +119,12 @@ class KiwixReaderFragment : CoreReaderFragment() {
|
||||
}
|
||||
|
||||
private suspend fun tryOpeningZimFile(zimFileUri: String) {
|
||||
// Stop any ongoing WebView loading and clear the WebView list
|
||||
// before setting a new ZIM file to the reader. This helps prevent native crashes.
|
||||
// The WebView's `shouldInterceptRequest` method continues to be invoked until the WebView is
|
||||
// fully destroyed, which can cause a native crash. This happens because a new ZIM file is set
|
||||
// in the reader while the WebView is still trying to access content from the old archive.
|
||||
stopOngoingLoadingAndClearWebViewList()
|
||||
// Close the previously opened book in the reader before opening a new ZIM file
|
||||
// to avoid native crashes due to "null pointer dereference." These crashes can occur
|
||||
// when setting a new ZIM file in the archive while the previous one is being disposed of.
|
||||
@ -143,7 +148,6 @@ class KiwixReaderFragment : CoreReaderFragment() {
|
||||
return
|
||||
}
|
||||
val zimReaderSource = ZimReaderSource(File(filePath))
|
||||
clearWebViewListIfNotPreviouslyOpenZimFile(zimReaderSource)
|
||||
openZimFile(zimReaderSource)
|
||||
}
|
||||
|
||||
@ -252,7 +256,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
|
||||
) {
|
||||
when (restoreOrigin) {
|
||||
FromExternalLaunch -> {
|
||||
lifecycleScope.launch {
|
||||
coreReaderLifeCycleScope?.launch {
|
||||
val settings =
|
||||
requireActivity().getSharedPreferences(SharedPreferenceUtil.PREF_KIWIX_MOBILE, 0)
|
||||
val zimReaderSource = fromDatabaseValue(settings.getString(TAG_CURRENT_FILE, null))
|
||||
|
@ -94,6 +94,10 @@ import io.reactivex.Flowable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.processors.BehaviorProcessor
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
@ -323,6 +327,15 @@ abstract class CoreReaderFragment :
|
||||
private var navigationHistoryList: MutableList<NavigationHistoryListItem> = ArrayList()
|
||||
private var isReadSelection = false
|
||||
private var isReadAloudServiceRunning = false
|
||||
private var readerLifeCycleScope: CoroutineScope? = null
|
||||
|
||||
val coreReaderLifeCycleScope: CoroutineScope?
|
||||
get() {
|
||||
if (readerLifeCycleScope == null) {
|
||||
readerLifeCycleScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
||||
}
|
||||
return readerLifeCycleScope
|
||||
}
|
||||
|
||||
private var storagePermissionForNotesLauncher: ActivityResultLauncher<String>? =
|
||||
registerForActivityResult(
|
||||
@ -1198,6 +1211,12 @@ abstract class CoreReaderFragment :
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
try {
|
||||
readerLifeCycleScope?.cancel()
|
||||
readerLifeCycleScope = null
|
||||
} catch (ignore: Exception) {
|
||||
ignore.printStackTrace()
|
||||
}
|
||||
if (sharedPreferenceUtil?.showIntro() == true) {
|
||||
val activity = requireActivity() as AppCompatActivity?
|
||||
activity?.setSupportActionBar(null)
|
||||
@ -1208,14 +1227,13 @@ abstract class CoreReaderFragment :
|
||||
tabCallback = null
|
||||
hideBackToTopTimer?.cancel()
|
||||
hideBackToTopTimer = null
|
||||
webViewList.clear()
|
||||
stopOngoingLoadingAndClearWebViewList()
|
||||
actionBar = null
|
||||
mainMenu = null
|
||||
tabRecyclerView?.adapter = null
|
||||
tableDrawerRight?.adapter = null
|
||||
tableDrawerAdapter = null
|
||||
tabsAdapter = null
|
||||
webViewList.clear()
|
||||
tempWebViewListForUndo.clear()
|
||||
// create a base Activity class that class this.
|
||||
deleteCachedFiles(requireActivity())
|
||||
@ -1714,13 +1732,36 @@ abstract class CoreReaderFragment :
|
||||
}
|
||||
}
|
||||
|
||||
fun clearWebViewListIfNotPreviouslyOpenZimFile(zimReaderSource: ZimReaderSource) {
|
||||
private fun clearWebViewListIfNotPreviouslyOpenZimFile(zimReaderSource: ZimReaderSource?) {
|
||||
if (isNotPreviouslyOpenZim(zimReaderSource)) {
|
||||
stopOngoingLoadingAndClearWebViewList()
|
||||
}
|
||||
}
|
||||
|
||||
protected fun stopOngoingLoadingAndClearWebViewList() {
|
||||
try {
|
||||
if (isNotPreviouslyOpenZim(zimReaderSource)) {
|
||||
webViewList.clear()
|
||||
webViewList.apply {
|
||||
forEach { webView ->
|
||||
// Stop any ongoing loading of the WebView
|
||||
webView.stopLoading()
|
||||
// Clear the navigation history of the WebView
|
||||
webView.clearHistory()
|
||||
// Clear cached resources to prevent loading old content
|
||||
webView.clearCache(true)
|
||||
// Pause any ongoing activity in the WebView to prevent resource usage
|
||||
webView.onPause()
|
||||
// Forcefully destroy the WebView before setting the new ZIM file
|
||||
// to ensure that it does not continue attempting to load internal links
|
||||
// from the previous ZIM file, which could cause errors.
|
||||
webView.destroy()
|
||||
}
|
||||
// Clear the WebView list after destroying the WebViews
|
||||
clear()
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
// Clear the WebView list in case of an error
|
||||
webViewList.clear()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,6 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.net.toUri
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kiwix.kiwixmobile.core.R.dimen
|
||||
@ -188,7 +187,7 @@ class CustomReaderFragment : CoreReaderFragment() {
|
||||
private fun openObbOrZim() {
|
||||
customFileValidator.validate(
|
||||
onFilesFound = {
|
||||
lifecycleScope.launch {
|
||||
coreReaderLifeCycleScope?.launch {
|
||||
when (it) {
|
||||
is ValidationState.HasFile -> {
|
||||
openZimFile(
|
||||
|
Loading…
x
Reference in New Issue
Block a user