2017-12-13 95 views
7

Tôi chỉ chuyển mã của tôi asynctask-rxjava2 và tôi nhận được một cách ngẫu nhiên ngoại lệ này trên mối quan hệ của tôi:Rxjava 2 ngoại lệ với camera

Máy ảnh đang được sử dụng sau khi Camera.release() được gọi trong thiên hà s6 Cạnh

Sau đây là số- tôi

Lớp Máy ảnh:

public class Cameras { 


    private static final String TAG = Cameras.class.getSimpleName(); 

    private static final String SP_CAMERA_ID = "camera_id"; 

    private static final int NO_NEXT_TASK = 0; 

    private static final int NEXT_TASK_RELEASE_COMPLETE = 1; 

    private static final int NEXT_TASK_SWITCH_COMPLETE = 2; 

    private static final int NEXT_TASK_START_PREVIEW = 3; 


    private Camera camera; 

    private int currentCameraId = -1; 


    private Camera.PreviewCallback previewCallback; 

    private byte[] buffer1, buffer2, buffer3; 

    private SurfaceTexture surfaceTexture; 


    private Listener listener; 


    public interface Listener { 

     void onCameraOpened(Camera.Size size, int angle); 
    } 

    private boolean cameraReleased = false; 


    public Cameras(Camera.PreviewCallback previewCallback, Listener listener) { 
     this.listener = listener; 
     this.previewCallback = previewCallback; 
     this.currentCameraId = Spin.INSTANCE.getSp().getInt(SP_CAMERA_ID, -1); 
     getCameraList(); 
    } 


    private void getCameraList() { 
     int numberOfCameras = Camera.getNumberOfCameras(); 
     Camera.CameraInfo camInfo = new Camera.CameraInfo(); 

     for (int i = 0; i < numberOfCameras; i++) { 
      Camera.getCameraInfo(i, camInfo); 
      cams.add(camInfo.facing); 
     } 

     if (Camera.CameraInfo.CAMERA_FACING_BACK != currentCameraId 
       && Camera.CameraInfo.CAMERA_FACING_FRONT != currentCameraId) { 
      currentCameraId = cams.get(cams.size() == 2 ? 1 : 0); 
     } 

    } 



    public boolean isSwitchCamAvailable() { 
     return Camera.getNumberOfCameras() > 1; 
    } 



    public void open(SurfaceTexture surfaceTexture) { 
     this.surfaceTexture = surfaceTexture; 
     init(NEXT_TASK_START_PREVIEW); 
    } 



    private void init(final int nextTask) { 

     if (cams.isEmpty()) { 
      Toast.makeText(Spin.getContext(), "Device have no camera", Toast.LENGTH_SHORT).show(); 
      return; 
     } 
     cameraReleased = false; 

     if (Looper.getMainLooper().getThread() == Thread.currentThread()) { 
      Observable.defer(new Callable<ObservableSource<?>>() { 
       @Override 
       public ObservableSource<?> call() throws Exception { 
        synchronized (this) { 
         try { 
          camera = Camera.open(currentCameraId); 
         } catch (RuntimeException e) { 
          e.printStackTrace(); 
         } 
        } 
        return Completable.complete().toObservable(); 
       } 
      }).doOnComplete(() -> { 
       initComplete(nextTask); 
       Log.d("Complete", "Complete"); 
      }) 
        .subscribeOn(Schedulers.computation()) 
        .observeOn(AndroidSchedulers.mainThread()).subscribe(); 



     } else { 
      try { 

       synchronized (this) { 
        this.camera = Camera.open(currentCameraId); 
       } 
       initComplete(nextTask); 
      } catch (RuntimeException ignored) { 

      } 
     } 
    } 



    private void initComplete(int nextTask) { 
     if (camera == null) { 
      return; 
     } 

     //noinspection SynchronizeOnNonFinalField 
     synchronized (camera) { 

      try { 

       Camera.Parameters params = camera.getParameters(); 
       Camera.Size size = getClosestFrameSize(params, 640); 
       params.setPreviewSize(size.width, size.height); 
       camera.setParameters(params); 
       camera.setPreviewCallbackWithBuffer(previewCallback); 

       int bufferSize = size.width * size.height 
         * ImageFormat.getBitsPerPixel(ImageFormat.NV21)/8; 


       buffer1 = new byte[bufferSize]; 
       buffer2 = new byte[bufferSize]; 
       buffer3 = new byte[bufferSize]; 

       camera.addCallbackBuffer(buffer1); 
       camera.addCallbackBuffer(buffer2); 
       camera.addCallbackBuffer(buffer3); 

       camera.setPreviewTexture(surfaceTexture); 

       int angle = rotateStream(); 

       camera.setDisplayOrientation(angle); 

       if (currentCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT && angle > 0) 
        angle = 360 - angle; 

       listener.onCameraOpened(size, angle); 

       routNextTask(nextTask); 

      } catch (IOException | RuntimeException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 



    private Camera.Size getClosestFrameSize(Camera.Parameters params, int width) { 

     Camera.Size result = null; 

     List<Camera.Size> sizes = params.getSupportedPreviewSizes(); 

     Camera.Size currentSize = null; 
     int closestDistance = 0; 
     int currentDistance = 0; 

     for (int i = 0; i < sizes.size(); ++i) { 

      if (null == result) { 
       result = sizes.get(i); 
       closestDistance = Math.abs(result.width - width); 
       continue; 
      } 

      currentSize = sizes.get(i); 
      currentDistance = Math.abs(currentSize.width - width); 

      if (currentDistance < closestDistance) { 
       closestDistance = currentDistance; 
       result = currentSize; 
       if (closestDistance == 0) break; 
      } 

     } 

     return result; 

    } 



    public void stopPreview() { 
     stopPreview(NO_NEXT_TASK); 
    } 

    private String nextTaskStr(final int nextTask) { 
     String nextTaskStr = null; 
     switch (nextTask) { 
      case NO_NEXT_TASK: 
       nextTaskStr = "NO_NEXT_TASK"; 
       break; 
      case NEXT_TASK_RELEASE_COMPLETE: 
       nextTaskStr = "NEXT_TASK_RELEASE_COMPLETE"; 
       break; 
      case NEXT_TASK_SWITCH_COMPLETE: 
       nextTaskStr = "NEXT_TASK_SWITCH_COMPLETE"; 
       break; 
      case NEXT_TASK_START_PREVIEW: 
       nextTaskStr = "NEXT_TASK_START_PREVIEW"; 
       break; 
     } 
     return nextTaskStr; 
    } 


    private void stopPreview(final int nextTask) { 


     if (null == camera) return; 

     if (Looper.getMainLooper().getThread() == Thread.currentThread()) { 
      Observable.defer(new Callable<ObservableSource<?>>() { 
       @Override 
       public ObservableSource<?> call() throws Exception { 

        synchronized (this) { 
         if ((null != camera) && (!cameraReleased)) { 
          if (LogDog.isEnabled) ; 
          camera.stopPreview(); 
         } 
        } 
        return Completable.complete().toObservable(); 
       } 
      }).doOnComplete(() -> { 
       routNextTask(nextTask); 
       Log.d("Complete", "Complete"); 
      }) 
        .subscribeOn(Schedulers.computation()) 
        .observeOn(AndroidSchedulers.mainThread()).subscribe(); 


     } else { 

      synchronized (this) { 
       if ((null != camera) && (!cameraReleased)) { 

        camera.stopPreview(); 
       } 
      } 
      routNextTask(nextTask); 
     } 
    } 

    private void routNextTask(int nextTask) { 

     if (NO_NEXT_TASK == nextTask) return; 

     if (NEXT_TASK_SWITCH_COMPLETE == nextTask) { 
      switchCamComplete(); 
     } else if (NEXT_TASK_RELEASE_COMPLETE == nextTask) { 
      releaseComplete(); 
     } else if (NEXT_TASK_START_PREVIEW == nextTask) { 
      startPreview(null); 
     } 
    } 

    public void startPreview(Camera.PreviewCallback cpc) { 


     if (null == camera) return; 
     synchronized (this) { 


      camera.startPreview(); 
      switchCamOnAir = false; 
     } 
    } 


    private void releaseCamera() { 
     synchronized (this) { 

      if (null == camera) return; 
      camera.setPreviewCallback(null); 

      camera.release(); 
      camera = null; 
      cameraReleased = true; 
     } 
    } 


    public void release() { 

     synchronized (this) { 
      if (null == camera) return; 
      stopPreview(NEXT_TASK_RELEASE_COMPLETE); 
     } 
    } 


    private void releaseComplete() { 
     synchronized (this) { 
      if (camera != null) { 

       camera.release(); 
       cameraReleased = true; 
       camera = null; 
      } 
     } 
     buffer1 = null; 
     buffer2 = null; 
     buffer3 = null; 
    } 



    private boolean switchCamOnAir = false; 


    public void switchCam() { 
     if (!isSwitchCamAvailable()) return; 
     if (null == camera) return; 
     if (switchCamOnAir) return; 
     this.switchCamOnAir = true; 
     stopPreview(NEXT_TASK_SWITCH_COMPLETE); 
    } 


    private void switchCamComplete() { 

     releaseCamera(); 

     if (currentCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) { 
      currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK; 
     } else { 
      currentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT; 
     } 

     Spin.INSTANCE.getSp().edit().putInt(SP_CAMERA_ID, currentCameraId).apply(); 

     init(NEXT_TASK_START_PREVIEW); 
    } 


    public int rotateStream() { 

     Camera.CameraInfo info = new Camera.CameraInfo(); 
     Camera.getCameraInfo(currentCameraId, info); 

     WindowManager wm = (WindowManager) Spin.getContext() 
       .getSystemService(Context.WINDOW_SERVICE); 

     int rotation = wm.getDefaultDisplay().getRotation(); 

     int degrees = 0; 
     switch (rotation) { 
      case Surface.ROTATION_0: 
       degrees = 0; 
       break; 
      case Surface.ROTATION_90: 
       degrees = 90; 
       break; 
      case Surface.ROTATION_180: 
       degrees = 180; 
       break; 
      case Surface.ROTATION_270: 
       degrees = 270; 
       break; 
     } 

     int result; 

     if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { 
      result = (info.orientation + degrees) % 360; 
      result = (360 - result) % 360; // compensate the mirror 
     } else { // back-facing 
      result = (info.orientation - degrees + 360) % 360; 
     } 

     return result; 
    } 



    @SuppressWarnings("unused") 
    public int getDeviceDefaultOrientation() { 

     WindowManager windowManager = (WindowManager) Spin.getContext() 
       .getSystemService(Context.WINDOW_SERVICE); 

     Configuration config = Spin.getContext().getResources().getConfiguration(); 

     int rotation = windowManager.getDefaultDisplay().getRotation(); 

     if (((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) && 
       config.orientation == Configuration.ORIENTATION_LANDSCAPE) 
       || ((rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) && 
       config.orientation == Configuration.ORIENTATION_PORTRAIT)) { 
      return Configuration.ORIENTATION_LANDSCAPE; 
     } else { 
      return Configuration.ORIENTATION_PORTRAIT; 
     } 
    } 

} 

cameras.java:

if (Looper.getMainLooper().getThread() == Thread.currentThread()) { 
      Observable.defer(new Callable<ObservableSource<?>>() { 
       @Override 
       public ObservableSource<?> call() throws Exception { 
        if (LogDog.isEnabled) LogDog.e("Debug::"+TAG + "::stopPreview()::AsyncTask::doInBackground()", " (camera != null) =" + (camera != null)); 
        synchronized (this) { 
         if ((null != camera) && (!cameraReleased)) { 
          if (LogDog.isEnabled) LogDog.e("Debug::" + TAG + "::stopPreview()::AsyncTask::doInBackground()", " XXX CALL camera.stopPreview()"); 
          camera.stopPreview(); 
         } 
        } 
        return Completable.complete().toObservable(); 
       } 
      }).doOnComplete(() -> { 
       routNextTask(nextTask); 
       Log.d("Complete", "Complete"); 
      }) 
        .subscribeOn(Schedulers.computation()) 
        .observeOn(AndroidSchedulers.mainThread()).subscribe(); 

Không chắc những gì tôi đang làm sai. Bất kỳ ý tưởng mà tôi có thể phát hành máy ảnh hoặc phân bổ nó, vì vậy nó hoạt động mà không có bất kỳ vấn đề? ngoại lệ như sau:

EXCEPTION Fatal: chính io.reactivex.exceptions.OnErrorNotImplementedException: Máy ảnh được được sử dụng sau khi Camera.release() được gọi tại io.reactivex.internal.functions.Functions $ OnErrorMissingConsumer.accept (Functions.java:704) tại io.reactivex.internal.functions.Functions $ OnErrorMissingConsumer.accept (Functions.java:701) tại io.reactivex.internal.observers.LambdaObserver.onError (LambdaObserver .java: 74) tại io.reactivex.internal.operators.observable.ObservableObserveOn $ ObserveOnObserver.checkTerminated (ObservableObserveOn.java:276) tại io.reactivex.internal.operators.observable.ObservableObserveOn $ ObserveOnObserver.drainNormal (ObservableObserveOn.java:172) tại io.reactivex.internal.operators.observable.ObservableObserveOn $ ObserveOnObserver.run (ObservableObserveOn.java:252) tại io.reactivex.android.schedulers.HandlerScheduler $ ScheduledRunnable.run (HandlerScheduler.java:109) tại android.os.Handler.handleCallback (Handler.java:751) tại android.os.Handler.dispatchMessage (Handler.java:95) tại android.os.Looper.loop (Looper.java:154) tại android.app.ActivityThread.main (ActivityThread.java:6119) tại java.lang.reflect.Method.invoke (Native Method) tại com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:886) tại com.android.internal.os.ZygoteInit.main (ZygoteInit.java:776) Gây ra bởi: java.lang.RuntimeException: Máy ảnh đang được sử dụng sau Camera.release() được gọi là tại android.hardware.Camera._stopPreview (Phương thức gốc) tại android.hardware.Camera.stopPreview (Camera.java:730) tại com.media.video.Cameras $ 2.call (Cameras.java:413) tại com.media.video.Cameras $ 2.call (Cameras.java:406) tại io.reactivex.internal.operators.observable .ObservableDefer.subscribeActual (ObservableDefer.java: 32) tại io.reactivex.Observable.subscribe (Observable.java:10842) tại io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual (ObservableDoOnEach.java:42) tại io.reactivex.Observable .subscribe (Observable.java:10842) tại io.reactivex.internal.operators.observable.ObservableSubscribeOn $ SubscribeTask.run (ObservableSubscribeOn.java:96) tại io.reactivex.internal.schedulers.ScheduledDirectTask.call (ScheduledDirectTask .java: 38) tại io.reactivex.internal.schedulers.ScheduledDirectTask.call (ScheduledDirectTask.java:26).210 tại java.util.concurrent.FutureTask.run (FutureTask.java:237) tại java.util.concurrent.ScheduledThreadPoolExecutor $ ScheduledFutureTask.run (ScheduledThreadPoolExecutor.java:272) tại java.util.concurrent.ThreadPoolExecutor. runWorker (ThreadPoolExecutor.java:1133) tại java.util.concurrent.ThreadPoolExecutor $ Worker.run (ThreadPoolExecutor.java:607) tại java.lang.Thread.run (Thread.java:761)

+1

Vui lòng thêm một số nhật ký gỡ lỗi khi #releaseComplete()/#startPreview()/#stopPreview()/init() đang được gọi. Sau đó, kích hoạt một ngoại lệ và đăng các dấu vết. –

+0

ngẫu nhiên của nó. đã cố gắng để tái tạo vụ tai nạn này nhưng rất khó để repro, xảy ra ngẫu nhiên. Tôi cũng có một vài bản ghi gỡ lỗi trong mã của tôi, nhưng đã không đăng nó ở đây vì nó sẽ làm cho tập tin lớn hơn và thường mọi người có tâm lý TLDR vì vậy có lẽ wounldt nhìn vào nó. –

+0

Bạn có ít nhất một stacktrace? –

Trả lời

3

Mã bạn viết để phát hành máy ảnh dễ bị có điều kiện chủng tộc. Một thay đổi nhỏ mà có thể tạo ra sự khác biệt là đặt cờ trước khi hành động xảy ra.

cameraReleased = true; 
camera.release(); 
camera = null; 

Nó có thể là cần thiết để kiểm tra lại lá cờ ngay trước khi phát hành của Camera, nhưng đối với điều này, bạn đồng bộ mã trước đó. Trong bối cảnh này, vấn đề là, bạn đang gọi số synchronized(this) trong số hoãn Observable. Thay vào đó, bạn nên đồng bộ hóa trên cùng một cá thể như mọi nơi khác, bởi vì this không đề cập đến lớp bên ngoài. Thay vì sử dụng [email protected]

@Override 
public ObservableSource<?> call() throws Exception { 
    if (LogDog.isEnabled) LogDog.e("Debug::"+TAG + "::stopPreview()::AsyncTask::doInBackground()", " (camera != null) =" + (camera != null)); 
    synchronized ([email protected]) { 
     if ((null != camera) && (!cameraReleased)) { 
      if (LogDog.isEnabled) LogDog.e("Debug::" + TAG + "::stopPreview()::AsyncTask::doInBackground()", " XXX CALL camera.stopPreview()"); 
      camera.stopPreview(); 
     } 
    } 
    return Completable.complete().toObservable(); 
} 

bổ sung vào đó, bạn use-case cho Observable.defer() không nhìn bên phải. Nhà máy Completable.fromAction() có thể phù hợp hơn.

Các vấn đề liên quan