Android 相机:stopPreview 与 releaseCamera
Android camera: stopPreview vs releaseCamera
我正在开发一个应用程序,我在 viewPager 中安装了摄像头。我想知道什么最适合 "pause" 和 "resume" 相机,这样它在预加载时就不会占用资源。我觉得 stopPreview 更适合这个,因为它不会释放相机但会保留它但是它不会显示相机,这是它占用资源的主要原因。
- 进入和退出应用程序:startCamera() & releaseCamera()
- 选项卡可见和不可见:startPreview() 和停止 Preview()
这是一个好的经验法则吗?
最好的解决办法是在onResume()中启动Camera(),然后在onPause()中释放它,这样你就可以处理了,在onResume()中相机不是空闲的。
在 ViewPager 中,您可以在选择片段时使用 startPreview(),否则使用 stopPreview()。你也可以在 onCreateView() 中的 startPreview() 和片段中 onDestroyView() 中的 stopPreview()。
我也遇到过类似的情况。 :
如果我将摄像头(在 ViewPager 中)保持在打开状态,滑动会很笨拙,并且 OOM 异常很常见。
我想到了两个选项:
将整个实例转移到不同的线程中
或
使用 stopPreview() 和 startPreview()
我选择了第二个:
但是,我没有在片段生命周期回调上执行此操作,而是在片段上提供了一个按钮来切换预览。原因是,如果用户滑动速度非常快,您仍然会收到 OOm 异常,因为预览调用将排队,尤其是在 viewPager 中的片段非常少的情况下。
实质上是在 onPause() 中释放相机,在 onResume() 中获取相机并在片段中提供一个 groovy 按钮,这将切换您在表面上的预览!
你好卡尔我有同样的事情需要在视图寻呼机中实现。我有一个圆形查看器,其中一个片段有相机片段。我想以这种方式处理相机预览,这样它就不会消耗相机资源。
如您所知android 查看寻呼机默认将两个片段加载到内存中。我们实现了视图分页器更改监听器并调用片段方法来启动和停止预览。甚至在片段的销毁方法中也销毁相机预览。
class ViewPagerChangeListener implements ViewPager.OnPageChangeListener {
int currentPosition = DEFAULT_FRAGMENT;
@Override
public void onPageScrollStateChanged(int state) {
TimberLogger.d(TAG, "onPageScrollStateChanged");
}
@Override
public void onPageScrolled(int index, float arg1, int arg2) {
TimberLogger.d(TAG, "onPageScrolled" + index);
}
@Override
public void onPageSelected(int position) {
mWatchPosition = position;
TimberLogger.d(TAG, "onPageSelected" + mWatchPosition);
int newPosition = 0;
if (position > 4) {
newPosition = position;
}
TimberLogger.d(TAG, "newPosition" + newPosition);
/**
* Listener knows the new position and can call the interface method
* on new Fragment with the help of PagerAdapter. We can here call
* onResumeFragment() for new fragment and onPauseFragment() on the
* current one.
*/
// new fragment onResume
loadedFragment(newPosition).onResumeFragment();
// current fragment onPuase called
loadedFragment(currentPosition).onPauseFragment();
currentPosition = newPosition;
TimberLogger.d(TAG, "currentPosition" + currentPosition);
}
}
看到onResumeFragment和onPuaseFragment这两个方法,这两个是每个view pager fragment实现的自定义函数。在视图分页器更改事件中,我们调用当前片段的暂停和新片段的 onResume。
// new fragment onResume
loadedFragment(newPosition).onResumeFragment();
// current fragment onPuase called
loadedFragment(currentPosition).onPauseFragment();
您可以在自定义方法 onResumeFragment 中编写相机开始预览并在 onPauseFragment 中停止预览,并确保您应该覆盖相机片段的 onDestory() 方法以释放相机资源。
这会处理大部分操作CameraPreview.java
:
package com.example.fela;
import android.app.Activity;
import android.content.Context;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.ErrorCallback;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import java.util.List;
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holder;
private Camera camera;
private int cameraId;
private Activity activity;
private CameraPreviewActivityInterface activityInterface;
public CameraPreview(Activity activity, int cameraId) {
super(activity);
try {
activityInterface = (CameraPreviewActivityInterface) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement ExampleFragmentCallbackInterface ");
}
this.activity = activity;
this.cameraId = cameraId;
holder = getHolder();
holder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {}
/**
* custom camera tweaks and startPreview()
*/
public void refreshCamera() {
if (holder.getSurface() == null || camera == null) {
// preview surface does not exist, camera not opened created yet
return;
}
Log.i(null, "CameraPreview refreshCamera()");
// stop preview before making changes
try {
camera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
int rotation = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
int degrees = 0;
// specifically for back facing camera
switch (rotation) {
case Surface.ROTATION_0:
degrees = 90;
break;
case Surface.ROTATION_90:
degrees = 0;
break;
case Surface.ROTATION_180:
degrees = 270;
break;
case Surface.ROTATION_270:
degrees = 180;
break;
}
camera.setDisplayOrientation(degrees);
setCamera(camera);
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (Exception e) {
// this error is fixed in the camera Error Callback (Error 100)
Log.d(VIEW_LOG_TAG, "Error starting camera preview: " + e.getMessage());
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.i(null, "CameraPreview surfaceChanged()");
// if your preview can change or rotate, take care of those events here.
// make sure to stop the preview before resizing or reformatting it.
// do not start the camera if the tab isn't visible
if(activityInterface.getCurrentPage() == 1)
startCamera();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {}
public Camera getCameraInstance() {
Camera camera = Camera.open();
// parameters for camera
Parameters params = camera.getParameters();
params.set("jpeg-quality", 100);
params.set("iso", "auto");
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
params.setPictureFormat(PixelFormat.JPEG);
// set the image dimensions
List<Size> sizes = params.getSupportedPictureSizes();
int max = 0, width = 0, height = 0;
for(Size size : sizes) {
if(max < (size.width*size.height)) {
max = (size.width*size.height);
width = size.width;
height = size.height;
}
}
params.setPictureSize(width, height);
camera.setParameters(params);
// primarily used to fix Error 100
camera.setErrorCallback(new ErrorCallback() {
@Override
public void onError(int error, Camera camera) {
if(error == Camera.CAMERA_ERROR_SERVER_DIED) {
releaseCamera();
startCamera();
}
}
});
return camera;
}
/**
* intitialize a new camera
*/
protected void startCamera() {
if(getCamera() == null)
setCamera(getCameraInstance());
refreshCamera();
}
/**
* release camera so other applications can utilize the camera
*/
protected void releaseCamera() {
// if already null then the camera has already been released before
if (getCamera() != null) {
getCamera().release();
setCamera(null);
}
}
public Camera getCamera() {
return camera;
}
public void setCamera(Camera camera) {
this.camera = camera;
}
public void setCameraId(int cameraId) {
this.cameraId = cameraId;
}
/**
* get the current viewPager page
*/
public interface CameraPreviewActivityInterface {
public int getCurrentPage();
}
}
在我的 FragmentCamera.java
文件中:
private CameraPreview cameraPreview;
// code...
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// code...
cameraPreview = new CameraPreview(getActivity(), cameraId);
previewLayout.addView(cameraPreview);
// code...
}
// code...
@Override
public void onPause() {
super.onPause();
cameraPreview.releaseCamera();
}
@Override
public void onResume() {
super.onResume();
cameraPreview.startCamera();
}
protected void fragmentVisible() {
onResume();
}
protected void fragmentNotVisible() {
onPause();
}
和 MainActivity.java 文件(实现 CameraPreviewActivityInterface):
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
currentPage = position;
if (currentPage == 1) {
fragmentCamera.fragmentVisible();
} else {
fragmentCamera.fragmentNotVisible();
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
@Override
public int getCurrentPage() {
return currentPage;
}
我正在开发一个应用程序,我在 viewPager 中安装了摄像头。我想知道什么最适合 "pause" 和 "resume" 相机,这样它在预加载时就不会占用资源。我觉得 stopPreview 更适合这个,因为它不会释放相机但会保留它但是它不会显示相机,这是它占用资源的主要原因。
- 进入和退出应用程序:startCamera() & releaseCamera()
- 选项卡可见和不可见:startPreview() 和停止 Preview()
这是一个好的经验法则吗?
最好的解决办法是在onResume()中启动Camera(),然后在onPause()中释放它,这样你就可以处理了,在onResume()中相机不是空闲的。 在 ViewPager 中,您可以在选择片段时使用 startPreview(),否则使用 stopPreview()。你也可以在 onCreateView() 中的 startPreview() 和片段中 onDestroyView() 中的 stopPreview()。
我也遇到过类似的情况。 :
如果我将摄像头(在 ViewPager 中)保持在打开状态,滑动会很笨拙,并且 OOM 异常很常见。
我想到了两个选项:
将整个实例转移到不同的线程中
或
使用 stopPreview() 和 startPreview()
我选择了第二个:
但是,我没有在片段生命周期回调上执行此操作,而是在片段上提供了一个按钮来切换预览。原因是,如果用户滑动速度非常快,您仍然会收到 OOm 异常,因为预览调用将排队,尤其是在 viewPager 中的片段非常少的情况下。
实质上是在 onPause() 中释放相机,在 onResume() 中获取相机并在片段中提供一个 groovy 按钮,这将切换您在表面上的预览!
你好卡尔我有同样的事情需要在视图寻呼机中实现。我有一个圆形查看器,其中一个片段有相机片段。我想以这种方式处理相机预览,这样它就不会消耗相机资源。
如您所知android 查看寻呼机默认将两个片段加载到内存中。我们实现了视图分页器更改监听器并调用片段方法来启动和停止预览。甚至在片段的销毁方法中也销毁相机预览。
class ViewPagerChangeListener implements ViewPager.OnPageChangeListener {
int currentPosition = DEFAULT_FRAGMENT;
@Override
public void onPageScrollStateChanged(int state) {
TimberLogger.d(TAG, "onPageScrollStateChanged");
}
@Override
public void onPageScrolled(int index, float arg1, int arg2) {
TimberLogger.d(TAG, "onPageScrolled" + index);
}
@Override
public void onPageSelected(int position) {
mWatchPosition = position;
TimberLogger.d(TAG, "onPageSelected" + mWatchPosition);
int newPosition = 0;
if (position > 4) {
newPosition = position;
}
TimberLogger.d(TAG, "newPosition" + newPosition);
/**
* Listener knows the new position and can call the interface method
* on new Fragment with the help of PagerAdapter. We can here call
* onResumeFragment() for new fragment and onPauseFragment() on the
* current one.
*/
// new fragment onResume
loadedFragment(newPosition).onResumeFragment();
// current fragment onPuase called
loadedFragment(currentPosition).onPauseFragment();
currentPosition = newPosition;
TimberLogger.d(TAG, "currentPosition" + currentPosition);
}
}
看到onResumeFragment和onPuaseFragment这两个方法,这两个是每个view pager fragment实现的自定义函数。在视图分页器更改事件中,我们调用当前片段的暂停和新片段的 onResume。
// new fragment onResume
loadedFragment(newPosition).onResumeFragment();
// current fragment onPuase called
loadedFragment(currentPosition).onPauseFragment();
您可以在自定义方法 onResumeFragment 中编写相机开始预览并在 onPauseFragment 中停止预览,并确保您应该覆盖相机片段的 onDestory() 方法以释放相机资源。
这会处理大部分操作CameraPreview.java
:
package com.example.fela;
import android.app.Activity;
import android.content.Context;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.ErrorCallback;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import java.util.List;
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holder;
private Camera camera;
private int cameraId;
private Activity activity;
private CameraPreviewActivityInterface activityInterface;
public CameraPreview(Activity activity, int cameraId) {
super(activity);
try {
activityInterface = (CameraPreviewActivityInterface) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement ExampleFragmentCallbackInterface ");
}
this.activity = activity;
this.cameraId = cameraId;
holder = getHolder();
holder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {}
/**
* custom camera tweaks and startPreview()
*/
public void refreshCamera() {
if (holder.getSurface() == null || camera == null) {
// preview surface does not exist, camera not opened created yet
return;
}
Log.i(null, "CameraPreview refreshCamera()");
// stop preview before making changes
try {
camera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
int rotation = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
int degrees = 0;
// specifically for back facing camera
switch (rotation) {
case Surface.ROTATION_0:
degrees = 90;
break;
case Surface.ROTATION_90:
degrees = 0;
break;
case Surface.ROTATION_180:
degrees = 270;
break;
case Surface.ROTATION_270:
degrees = 180;
break;
}
camera.setDisplayOrientation(degrees);
setCamera(camera);
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (Exception e) {
// this error is fixed in the camera Error Callback (Error 100)
Log.d(VIEW_LOG_TAG, "Error starting camera preview: " + e.getMessage());
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.i(null, "CameraPreview surfaceChanged()");
// if your preview can change or rotate, take care of those events here.
// make sure to stop the preview before resizing or reformatting it.
// do not start the camera if the tab isn't visible
if(activityInterface.getCurrentPage() == 1)
startCamera();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {}
public Camera getCameraInstance() {
Camera camera = Camera.open();
// parameters for camera
Parameters params = camera.getParameters();
params.set("jpeg-quality", 100);
params.set("iso", "auto");
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
params.setPictureFormat(PixelFormat.JPEG);
// set the image dimensions
List<Size> sizes = params.getSupportedPictureSizes();
int max = 0, width = 0, height = 0;
for(Size size : sizes) {
if(max < (size.width*size.height)) {
max = (size.width*size.height);
width = size.width;
height = size.height;
}
}
params.setPictureSize(width, height);
camera.setParameters(params);
// primarily used to fix Error 100
camera.setErrorCallback(new ErrorCallback() {
@Override
public void onError(int error, Camera camera) {
if(error == Camera.CAMERA_ERROR_SERVER_DIED) {
releaseCamera();
startCamera();
}
}
});
return camera;
}
/**
* intitialize a new camera
*/
protected void startCamera() {
if(getCamera() == null)
setCamera(getCameraInstance());
refreshCamera();
}
/**
* release camera so other applications can utilize the camera
*/
protected void releaseCamera() {
// if already null then the camera has already been released before
if (getCamera() != null) {
getCamera().release();
setCamera(null);
}
}
public Camera getCamera() {
return camera;
}
public void setCamera(Camera camera) {
this.camera = camera;
}
public void setCameraId(int cameraId) {
this.cameraId = cameraId;
}
/**
* get the current viewPager page
*/
public interface CameraPreviewActivityInterface {
public int getCurrentPage();
}
}
在我的 FragmentCamera.java
文件中:
private CameraPreview cameraPreview;
// code...
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// code...
cameraPreview = new CameraPreview(getActivity(), cameraId);
previewLayout.addView(cameraPreview);
// code...
}
// code...
@Override
public void onPause() {
super.onPause();
cameraPreview.releaseCamera();
}
@Override
public void onResume() {
super.onResume();
cameraPreview.startCamera();
}
protected void fragmentVisible() {
onResume();
}
protected void fragmentNotVisible() {
onPause();
}
和 MainActivity.java 文件(实现 CameraPreviewActivityInterface):
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
currentPage = position;
if (currentPage == 1) {
fragmentCamera.fragmentVisible();
} else {
fragmentCamera.fragmentNotVisible();
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
@Override
public int getCurrentPage() {
return currentPage;
}