使用CameraX拍摄并保存照片后跳转至另一个Fragment的实现方案
Hey Maria, this is a super common thread-related issue with CameraX—let’s break down how to fix it! The core problem here is that CameraX’s onImageSaved callback runs on your background cameraExecutor thread, and you can’t perform Fragment transactions (or any UI operations) off the main thread. Here’s the straightforward fix:
Core Fix: Switch to Main Thread for Navigation
You need to wrap your Fragment navigation code in a call that ensures it runs on the main thread. Android provides requireActivity().runOnUiThread { ... } for exactly this scenario—it posts your UI operation to the main thread’s message queue, even if you’re calling it from a background thread.
Modified Code Example
Let’s update your onImageSaved callback to include this main thread switch. Also, since you’re saving to Firestore, you’ll want to trigger navigation after that upload completes (to make sure the photo is fully saved before switching screens). Here’s the adjusted code:
override fun onImageSaved(output: ImageCapture.OutputFileResults) { val savedUri = output.savedUri ?: Uri.fromFile(photoFile) Log.d(TAG, "Photo capture succeeded: $savedUri") // Keep your media scanner code as-is val mimeType = MimeTypeMap.getSingleton() .getMimeTypeFromExtension(savedUri.toFile().extension) MediaScannerConnection.scanFile( context, arrayOf(savedUri.toFile().absolutePath), arrayOf(mimeType) ) { _, uri -> Log.d(TAG, "Image capture scanned into media store: $uri") } // Update saveToFireStoreStorage to use a completion callback saveToFireStoreStorage(photoFile, fileName) { uploadSuccess -> if (uploadSuccess) { photoFile.delete() // Clean up local file after successful upload // Navigate to Fragment2 on the MAIN thread requireActivity().runOnUiThread { // Replace with your actual navigation logic val navController = findNavController() navController.navigate(R.id.action_fragment1_to_fragment2) // If you're using FragmentManager instead of Navigation Component: // requireActivity().supportFragmentManager.beginTransaction() // .replace(R.id.fragment_container, Fragment2()) // .addToBackStack(null) // .commit() } } else { // Handle upload failure if needed Log.e(TAG, "Firestore upload failed") } } }
Key Details to Note:
- Why
runOnUiThread? All UI operations (including Fragment transactions) are restricted to the main thread. CameraX uses yourcameraExecutor(a background thread) for callbacks, so we explicitly switch back to main here to avoid crashes or freezes. - Wait for Firestore Upload: I added a lambda callback to
saveToFireStoreStorage—you’ll need to update that function to invoke this callback once the Firestore upload finishes. This ensures you only navigate after the photo is safely stored in the cloud. - Alternative: Use
view.postIf you have a reference to your Fragment’s view (likebinding.root), you could also usebinding.root.post { ... }instead ofrunOnUiThread—it achieves the same result, aspostsends the operation to the main thread.
If saveToFireStoreStorage is Synchronous:
If your Firestore save is synchronous (not recommended for network operations), you can simplify the code to:
saveToFireStoreStorage(photoFile, fileName) photoFile.delete() // Navigate on main thread requireActivity().runOnUiThread { // Your navigation code here }
This should resolve the thread issue and let you safely jump to Fragment2 right after the photo is processed and saved.
内容的提问来源于stack exchange,提问作者Maria

