简体   繁体   中英

Fragment navigation bug Android 13

So basically I have an app where I move across different fragments by using NavController navigation between fragments. This is working on all Android versions < 13.

findNavController().navigate(R.id.step02Fragment)

The issue on Android 13 is that when I move from the Step05Fragment to the Step06Fragment the onPause and onResume methods of the Step05Fragment start to execute forever and ever, causing a StackOverflow error.

I have no idea why this is happening between these two steps and in this specific Android version. All other fragments do the navigation the exact same way also and this issue doesn't happen on them.

Any ideas?

Thanks

PD These are the libraries versions, if this is a known issue on any of them

implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
implementation "androidx.lifecycle:lifecycle-service:2.5.1"

implementation "androidx.navigation:navigation-runtime-ktx:2.5.2"
implementation "androidx.navigation:navigation-fragment-ktx:2.5.2"
implementation "androidx.navigation:navigation-ui-ktx:2.5.2"
implementation "androidx.navigation:navigation-dynamic-features-fragment:2.5.2"

Here is the code of the fragments

@AndroidEntryPoint
class Step05Fragment : Fragment() {

    lateinit var binding: FragmentStep05Binding

    private var initialSetup: Boolean = true
    private val disposable = CompositeDisposable()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentStep05Binding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        arguments?.getBoolean(Constants.ENABLE_PERMISSION)?.let {
            initialSetup = it
        }

        setObservables()
        setView()
    }

    override fun onDestroy() {
        super.onDestroy()
        disposable.dispose()
    }

    private fun setObservables() {
        binding.btnEnable.clicks()
            .map {
                checkPermission()
            }
            .subscribe()
            .disposeBy(disposable)
    }

    private fun setView() {
        if (!initialSetup) {
            binding.currentStep.visibility = View.GONE
        }
    }

    private fun checkPermission() {
        if (!PermissionsUtils.isReadExternalStoragePermissionGranted(requireContext())) {
            requestPermission.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
        } else {
            handleNavigation()
        }
    }

    private val requestPermission =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
            if (isGranted) {
                handleNavigation()
            } else {
                checkPermission()
            }
        }

    private fun handleNavigation() {
        if (initialSetup) {
            findNavController().navigate(R.id.step06Fragment)
        } else {
            activity?.finishAffinity()
        }
    }

}

@AndroidEntryPoint
class Step06Fragment : Fragment() {

lateinit var binding: FragmentStep06Binding

private var initialSetup: Boolean = true
private val disposable = CompositeDisposable()

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    binding = FragmentStep06Binding.inflate(inflater, container, false)
    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    arguments?.getBoolean(Constants.ENABLE_PERMISSION)?.let {
        initialSetup = it
    }

    setObservables()
    setView()
}

override fun onDestroy() {
    super.onDestroy()
    disposable.dispose()
}

private fun setObservables() {
    binding.btnEnable.clicks()
        .map {
            checkPermission()
        }
        .subscribe()
        .disposeBy(disposable)
}

private fun setView() {
    if (!initialSetup) {
        binding.currentStep.visibility = View.GONE
    }
}

private fun checkPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        if (!PermissionsUtils.isMediaLocationPermissionGranted(requireContext())) {
            requestPermission.launch(Manifest.permission.ACCESS_MEDIA_LOCATION)
        } else {
            handleNavigation()
        }
    } else {
        handleNavigation()
    }
}

private val requestPermission =
    registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
        if (isGranted) {
            handleNavigation()
        } else {
            checkPermission()
        }
    }

private fun handleNavigation() {
    if (initialSetup) {
        findNavController().navigate(R.id.step07Fragment)
    } else {
        activity?.finishAffinity()
    }
}

After a while, found the answer

https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions

Seems like this weird behavior is because of this, added the following validation after updating my project dependencies to aim Android 13 and it worked just fine

private fun checkPermission() {
        if (!PermissionsUtils.isReadExternalStoragePermissionGranted(requireContext())) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                requestPermission.launch(Manifest.permission.READ_MEDIA_IMAGES)
            } else {
                requestPermission.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
            }
        } else {
            handleNavigation()
        }
    }

this can be related to binding. I had some binding issues before? Can you try changing it to the old findViewById or another way?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM