Skip to content

Commit

Permalink
feat(android): stop/release camera in non-UI thread (react-native-cam…
Browse files Browse the repository at this point in the history
…era#2685)

* stop/release camera in non-UI thread so we prevent ANRs and UI freezing.

Some phones may take up to a second to release the camera and preview.

* fix for surface destroy and resume events.

Co-authored-by: Cristiano Coelho <cristianocca@hotmail.com>
  • Loading branch information
cristianoccazinsp and cristianocca committed Feb 13, 2020
1 parent c683076 commit ba0e427
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 6 deletions.
60 changes: 56 additions & 4 deletions android/src/main/java/com/google/android/cameraview/Camera1.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
private boolean mIsScanning;

private boolean mustUpdateSurface;
private boolean surfaceWasDestroyed;

private SurfaceTexture mPreviewTexture;

Expand All @@ -133,12 +134,56 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
preview.setCallback(new PreviewImpl.Callback() {
@Override
public void onSurfaceChanged() {
updateSurface();

// if we got our surface destroyed
// we must re-start the camera and surface
// otherwise, just update our surface


synchronized(Camera1.this){
if(!surfaceWasDestroyed){
updateSurface();
}
else{
mBgHandler.post(new Runnable() {
@Override
public void run() {
start();
}
});
}
}
}

@Override
public void onSurfaceDestroyed() {
stop();

// need to this early so we don't get buffer errors due to sufrace going away.
// Then call stop in bg thread since it might be quite slow and will freeze
// the UI or cause an ANR while it is happening.
synchronized(Camera1.this){
if(mCamera != null){

// let the instance know our surface was destroyed
// and we might need to re-create it and restart the camera
surfaceWasDestroyed = true;

try{
mCamera.setPreviewCallback(null);
// note: this might give a debug message that can be ignored.
mCamera.setPreviewDisplay(null);
}
catch(Exception e){
Log.e("CAMERA_1::", "onSurfaceDestroyed preview cleanup failed", e);
}
}
}
mBgHandler.post(new Runnable() {
@Override
public void run() {
stop();
}
});
}
});
}
Expand Down Expand Up @@ -235,8 +280,13 @@ void stop() {

if (mCamera != null) {
mIsPreviewActive = false;
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
try{
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
}
catch(Exception e){
Log.e("CAMERA_1::", "stop preview cleanup failed", e);
}
}

releaseCamera();
Expand All @@ -247,6 +297,8 @@ void stop() {
@SuppressLint("NewApi")
void setUpPreview() {
try {
surfaceWasDestroyed = false;

if(mCamera != null){
if (mPreviewTexture != null) {
mCamera.setPreviewTexture(mPreviewTexture);
Expand Down
11 changes: 9 additions & 2 deletions android/src/main/java/org/reactnative/camera/RNCameraView.java
Original file line number Diff line number Diff line change
Expand Up @@ -522,10 +522,17 @@ public void onHostDestroy() {
mGoogleBarcodeDetector.release();
}
mMultiFormatReader = null;
stop();
mThemedReactContext.removeLifecycleEventListener(this);

this.cleanup();
// camera release can be quite expensive. Run in on bg handler
// and cleanup last once everything has finished
mBgHandler.post(new Runnable() {
@Override
public void run() {
stop();
cleanup();
}
});
}

private boolean hasCameraPermissions() {
Expand Down

0 comments on commit ba0e427

Please sign in to comment.