La aplicación se bloquea al girar la pantalla

Estoy usando Mobile Vision API para detectar caras en la camera frontal. Utilicé la demostración FaceTracker de Google, pero necesito usarla en Fragment. Está funcionando, pero cuando trato de rotar la pantalla, la aplicación se bloquea con la exception RuntimeException: Fail to connect to camera service .

Código de fragment:

 class EyeTrackerFragment : Fragment(), AnkoLogger { private var cameraSource: CameraSource? = null private lateinit var cameraSourcePreview: CameraSourcePreview private lateinit var graphicOverlay: GraphicOverlay private var callback: OnEyeContextUpdatedListener? = null companion object { // google play services error code private val RC_HANDLE_GMS = 9001 // permission request codes need to be < 256 private val RC_HANDLE_CAMERA_PERM = 2 } override fun onAttach(context: Context?) { super.onAttach(context) try { callback = activity as OnEyeContextUpdatedListener } catch (e: ClassCastException) { throw ClassCastException("${activity} must implement OnEyeContextUpdatedListener") } } /** * Restarts the camera. */ override fun onResume() { super.onResume() startCameraSource() } /** * Stops the camera. */ override fun onPause() { super.onPause() cameraSourcePreview.stop() } /** * Releases the resources associated with the camera source, the associated detector, and the * rest of the processing pipeline. */ override fun onDestroy() { super.onDestroy() cameraSource?.release() } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater!!.inflate(R.layout.fragment_face_tracker, container, false) override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) cameraSourcePreview = getView()!!.findViewById<View>(R.id.preview) as CameraSourcePreview graphicOverlay = getView()!!.findViewById<View>(R.id.faceOverlay) as GraphicOverlay if (ActivityCompat.checkSelfPermission(context.applicationContext, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { createCameraSource() } else { requestCameraPermission() } } /** * Handles the requesting of the camera permission. This includes * showing a "Toast" message of why the permission is needed then * sending the request. */ private fun requestCameraPermission() { warn("Camera permission is not granted. Requesting permission") val permissions = arrayOf(Manifest.permission.CAMERA) if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA)) { ActivityCompat.requestPermissions(activity, permissions, RC_HANDLE_CAMERA_PERM) return } Toast.makeText(context.applicationContext, "Give me permissions", Toast.LENGTH_LONG).show() } private fun createCameraSource() { val detector = FaceDetector.Builder(context.applicationContext) .setClassificationType(FaceDetector.ALL_CLASSIFICATIONS) .setProminentFaceOnly(true) .setTrackingEnabled(true) .setMode(FaceDetector.ACCURATE_MODE) .build() val faceTracker = GraphicFaceTracker(graphicOverlay, callback, context) val faceProcessor = LargestFaceFocusingProcessor(detector, faceTracker) detector.setProcessor(faceProcessor) if (!detector.isOperational) { longToast("Face detector dependencies are not yet available.") } cameraSource = CameraSource.Builder(context, detector) .setRequestedPreviewSize(640, 480) .setFacing(CameraSource.CAMERA_FACING_FRONT) .setRequestedFps(30.0f) .build() } private fun startCameraSource() { // check that the device has play services available. val code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable( context.applicationContext) if (code != ConnectionResult.SUCCESS) { val dlg = GoogleApiAvailability.getInstance().getErrorDialog(activity, code, RC_HANDLE_GMS) dlg.show() } if (cameraSource != null) { try { cameraSourcePreview.start(cameraSource!!, graphicOverlay) } catch (e: IOException) { error("Unable to start camera source.", e) cameraSource!!.release() cameraSource = null } } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { if (requestCode != RC_HANDLE_CAMERA_PERM) { debug("Got unexpected permission result: $requestCode") super.onRequestPermissionsResult(requestCode, permissions, grantResults) return } if (grantResults.size != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { debug("Camera permission granted - initialize the camera source") createCameraSource() return } error("Permission not granted: results len = ${grantResults.size}. Result code = ${if (grantResults.isNotEmpty()) grantResults[0] else "(empty)"}") val builder = AlertDialog.Builder(context) builder.setTitle("Face Tracker sample") .setMessage("Have no camera permission") .setPositiveButton("Ok", { _, _ -> activity.finish() }) .show() } 

}

De acuerdo con el código de actividad. Solo adjunta fragment anterior en el método onCreate y luego no hace nada.

Supongo que la instancia de la camera no se lanzó en alguna situación, pero se hace de la misma manera que en Activity. ¿Hay algún comportamiento especial en Fragment que requiera acciones adicionales en comparación con las actividades?

Registro completo de excepciones:

 E/AndroidRuntime: FATAL EXCEPTION: main Process: com.eyetracker.android.demo, PID: 22631 java.lang.RuntimeException: Fail to connect to camera service at android.hardware.Camera.<init>(Camera.java:520) at android.hardware.Camera.open(Camera.java:361) at com.google.android.gms.vision.CameraSource.zzchq(Unknown Source) at com.google.android.gms.vision.CameraSource.start(Unknown Source) at com.eyetracker.android.camera.CameraSourcePreview.startIfReady(CameraSourcePreview.kt:82) at com.eyetracker.android.camera.CameraSourcePreview.access$startIfReady(CameraSourcePreview.kt:18) at com.eyetracker.android.camera.CameraSourcePreview$SurfaceCallback.surfaceCreated(CameraSourcePreview.kt:104) at android.view.SurfaceView.updateWindow(SurfaceView.java:580) at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:176) at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:948) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1974) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1065) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5901) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767) at android.view.Choreographer.doCallbacks(Choreographer.java:580) at android.view.Choreographer.doFrame(Choreographer.java:550) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:211) at android.app.ActivityThread.main(ActivityThread.java:5389) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1020) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:815) 

Cada vez que Android destruye y recrea su actividad para el cambio de orientación, llama a SaveInstanceState ().

¿Así que guarde los datos necesarios en un package en onSaveInstanceState () y restaure el mismo en onCreate () marcando si el campo savedInstanceState es nulo?