Skip to content

Commit

Permalink
Merge pull request #4105 from kiwix/Fixes#4104
Browse files Browse the repository at this point in the history
Fixed: Sometimes application crashes due to a native crash when we frequently load the ZIM files in the reader.
  • Loading branch information
kelson42 authored Nov 22, 2024
2 parents 3305a26 + c86c156 commit fff3c3e
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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.
Expand All @@ -143,7 +148,6 @@ class KiwixReaderFragment : CoreReaderFragment() {
return
}
val zimReaderSource = ZimReaderSource(File(filePath))
clearWebViewListIfNotPreviouslyOpenZimFile(zimReaderSource)
openZimFile(zimReaderSource)
}

Expand Down Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)
Expand All @@ -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())
Expand Down Expand Up @@ -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()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -188,7 +187,7 @@ class CustomReaderFragment : CoreReaderFragment() {
private fun openObbOrZim() {
customFileValidator.validate(
onFilesFound = {
lifecycleScope.launch {
coreReaderLifeCycleScope?.launch {
when (it) {
is ValidationState.HasFile -> {
openZimFile(
Expand Down

0 comments on commit fff3c3e

Please sign in to comment.