0

我正在使用 Mobile Vision API 来检测前置摄像头上的人脸。我使用了 Google 的 FaceTracker 演示,但我需要在 Fragment 中使用它。它正在工作,但是当我尝试旋转屏幕时,应用程序崩溃并出现异常RuntimeException: Fail to connect to camera service

片段代码:

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()
}

}

根据活动代码。它只在 onCreate 方法中附加上一个片段,然后不做任何事情。

我猜想在某些情况下没有释放相机实例,但它的完成方式与 Activity 中的相同。Fragment 中是否有任何特殊行为需要与活动相比需要额外的操作?

完整的异常日志:

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)
4

1 回答 1

0

每当 Android 销毁并重新创建您的 Activity 以更改方向时,它都会调用 onSaveInstanceState()。

因此,是否在 onSaveInstanceState() 中的 Bundle 中保存必要的数据,并通过检查字段 savedInstanceState 是否为空来在 onCreate() 中恢复相同的数据?

于 2017-11-29T10:15:28.170 回答