feat(android): stop/release camera in non-UI thread (#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>
This commit is contained in:
cristianoccazinsp 2020-02-13 18:02:39 -03:00 committed by GitHub
parent c683076a48
commit ba0e427304
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 65 additions and 6 deletions

View File

@ -124,6 +124,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
private boolean mIsScanning;
private boolean mustUpdateSurface;
private boolean surfaceWasDestroyed;
private SurfaceTexture mPreviewTexture;
@ -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();
}
});
}
});
}
@ -235,8 +280,13 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
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();
@ -247,6 +297,8 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
@SuppressLint("NewApi")
void setUpPreview() {
try {
surfaceWasDestroyed = false;
if(mCamera != null){
if (mPreviewTexture != null) {
mCamera.setPreviewTexture(mPreviewTexture);

View File

@ -522,10 +522,17 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
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() {