我正在通过Android的测试程序,当我使用StrictMode进行测试时,我收到了关于我的相机资源泄漏的警告,但我似乎找不到位置。这是警告:
A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
java.lang.Throwable: Explicit termination method 'release' not called
at dalvik.system.CloseGuard.open(CloseGuard.java:184)
at android.view.Surface.<init>(Surface.java:301)
at android.view.SurfaceView.<init>(SurfaceView.java:102)
at com.example.sition.diggin.camera.CameraView.<init>(CameraView.java:20)
at com.example.sition.diggin.CameraActivity.onResume(CameraActivity.java:60)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1202)
at android.app.Activity.performResume(Activity.java:5404)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2837)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2903)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2321)
at android.app.ActivityThread.access$700(ActivityThread.java:158)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1296)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:176)
at android.app.ActivityThread.main(ActivityThread.java:5365)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
at dalvik.system.NativeStart.main(Native Method)
我知道它与相机有关,但我不确定它是CameraView还是CameraActivity,所以这是CameraView:
public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
private Camera mCamera;
// Constructor that obtains context and camera
public CameraView(Context context, Camera camera) {
super(context);
this.mCamera = camera;
SurfaceHolder mSurfaceHolder = this.getHolder();
mSurfaceHolder.addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
try {
SurfaceHolder mSurfaceHolder = this.getHolder();
mSurfaceHolder.addCallback(this);
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
mCamera.stopPreview();
getHolder().removeCallback(this);
mCamera.release();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format,int width, int height) {
// start preview with new settings
try {
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();
} catch (Exception e) {
e.printStackTrace();
}
}
}
这是CameraActivity:
public class CameraActivity extends AppCompatActivity implements BearingToNorthProvider.ChangeEventListener {
//region fields
private Camera mCamera;
private float mDist = 0f;
private String flashMode;
private ImageButton flashButton;
private BearingToNorthProvider mBearingProvider;
private boolean pictureTaken = false;
private byte[] currentData;
private double bearing;
private double currentBearing = 0d;
private String cardinalDirection = "?";
private double rotation = 0d;
private static final int REQUEST_CODE_ASK_PERMISSIONS = 2;
//endregion
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
}
@Override protected void onResume() {
super.onResume();
mCamera = getCameraInstance();
CameraView mCameraView = new CameraView(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mCameraView);
ImageButton captureButton = (ImageButton) findViewById(R.id.btnCapture);
captureButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Camera.Parameters params = mCamera.getParameters();
params.setFlashMode(flashMode);
mCamera.setParameters(params);
mCamera.takePicture(null, null, mPicture);
}
});
SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.flashMode), Context.MODE_PRIVATE);
flashMode = sharedPref.getString(getString(R.string.flashMode), Camera.Parameters.FLASH_MODE_OFF);
flashButton = (ImageButton) findViewById(R.id.btnFlash);
setFlashButton();
mBearingProvider = new BearingToNorthProvider(this,this);
mBearingProvider.setChangeEventListener(this);
mBearingProvider.start();
}
@Override
protected void onPause() {
super.onPause();
mBearingProvider.stop();
}
/**
* Helper method to access the camera returns null if it cannot get the
* camera or does not exist
*
* @return the instance of the camera
*/
private Camera getCameraInstance() {
Camera camera = null;
try {
camera = Camera.open();
} catch (Exception e) {
e.printStackTrace();
}
return camera;
}
private Camera.PictureCallback mPicture = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
currentData = data;
mBearingProvider.updateBearing();
bearing = mBearingProvider.getBearing();
cardinalDirection = bearingToString(bearing);
findViewById(R.id.btnFlash).setVisibility(View.INVISIBLE);
findViewById(R.id.btnCapture).setVisibility(View.INVISIBLE);
findViewById(R.id.ivCompass).setVisibility(View.INVISIBLE);
findViewById(R.id.ivFocusPoint).setVisibility(View.INVISIBLE);
findViewById(R.id.ivCompassPointer).setVisibility(View.INVISIBLE);
findViewById(R.id.pictureOverlay).setVisibility(View.VISIBLE);
}
};
//region Picture Intents
/**
* Method that puts the necessary data in the intent and then sends it back to the ProjectOverview
* @param v the view that activated this method
*/
public void confirmPicture(View v) {
String path = createFile(currentData);
Intent intent = new Intent();
intent.putExtra("path", path);
String description = String.valueOf(((EditText) findViewById(R.id.tvPictureDesc)).getText());
intent.putExtra("direction", cardinalDirection);
intent.putExtra("description", description);
//close this Activity...
setResult(Activity.RESULT_OK, intent);
finish();
}
/**
* Method that puts no data in the intent and then sends it back to the ProjectOverview
* @param v the view that activated this method
*/
public void deletePicture(View v) {
setResult(Activity.RESULT_CANCELED);
finish();
}
/**
* Method that restarts the camera giving the user a change to retake the picture
* @param v the view that activated this method
*/
public void retryPicture(View v) {
findViewById(R.id.btnFlash).setVisibility(View.VISIBLE);
findViewById(R.id.btnCapture).setVisibility(View.VISIBLE);
findViewById(R.id.ivCompass).setVisibility(View.VISIBLE);
findViewById(R.id.ivFocusPoint).setVisibility(View.VISIBLE);
findViewById(R.id.ivCompassPointer).setVisibility(View.VISIBLE);
findViewById(R.id.pictureOverlay).setVisibility(View.INVISIBLE);
pictureTaken = false;
mCamera.startPreview();
}
//endregion
//region File Methods
/**
* Method that creates a file from the given byte array and saves the file in the Pictures Directory
* @param data is the array of bytes that represent the picture taken by the camera
* @return the path of created file
*/
private String createFile(byte[] data){
checkFilePermissions();
File picFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + "tempPic.jpg" + File.separator);
String path = picFile.getPath();
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(picFile));
bos.write(data);
bos.flush();
bos.close();
return path;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
/**
* Checks the permission for reading to and writing from the external storage
*/
private void checkFilePermissions() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
int hasWriteExternalStoragePermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (hasWriteExternalStoragePermission != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_ASK_PERMISSIONS);
}
}
}
//endregion
//region Zoom Methods
@Override
public boolean onTouchEvent(MotionEvent event) {
// Get the pointer ID
Camera.Parameters params = mCamera.getParameters();
int action = event.getAction();
if (event.getPointerCount() > 1) {
// handle multi-touch events
if (action == MotionEvent.ACTION_POINTER_DOWN) {
mDist = getFingerSpacing(event);
} else if (action == MotionEvent.ACTION_MOVE && params.isZoomSupported()) {
mCamera.cancelAutoFocus();
handleZoom(event, params);
}
} else {
// handle single touch events
if (action == MotionEvent.ACTION_UP) {
handleFocus(event, params);
}
}
return true;
}
/**
* Method that handles the zoom of the camera
* @param event the event that activated this method
* @param params the parameters of the camera
*/
private void handleZoom(MotionEvent event, Camera.Parameters params) {
int maxZoom = params.getMaxZoom();
int zoom = params.getZoom();
float newDist = getFingerSpacing(event);
if (newDist > mDist) {
//zoom in
if (zoom < maxZoom)
zoom++;
} else if (newDist < mDist) {
//zoom out
if (zoom > 0)
zoom--;
}
mDist = newDist;
params.setZoom(zoom);
mCamera.setParameters(params);
}
/**
* Method that handles the focus of the camera when zooming
* @param event the event that activated this method
* @param params the parameters of the camera
*/
private void handleFocus(MotionEvent event, Camera.Parameters params) {
int pointerId = event.getPointerId(0);
int pointerIndex = event.findPointerIndex(pointerId);
// Get the pointer's current position
List<String> supportedFocusModes = params.getSupportedFocusModes();
if (supportedFocusModes != null && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
mCamera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean b, Camera camera) {
// currently set to auto-focus on single touch
}
});
}
}
/**
* Method that determines the space between the first two fingers
* @param event the event that activated this method
* @return the distance between the two fingers
*/
private float getFingerSpacing(MotionEvent event) {
double x = event.getX(0) - event.getX(1);
double y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
//endregion
//region Flash Methods
/**
* Method that changes the flash mode the camera currently uses (on/off/auto)
* @param v the view that activated this method
*/
public void changeFlashMode(View v) {
switch (flashMode) {
case Camera.Parameters.FLASH_MODE_ON :
flashMode = Camera.Parameters.FLASH_MODE_AUTO;
break;
case Camera.Parameters.FLASH_MODE_AUTO :
flashMode = Camera.Parameters.FLASH_MODE_OFF;
break;
case Camera.Parameters.FLASH_MODE_OFF :
flashMode = Camera.Parameters.FLASH_MODE_ON;
break;
}
SharedPreferences sharedPref = getSharedPreferences(getString(R.string.flashMode), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(getString(R.string.flashMode), flashMode);
editor.apply();
setFlashButton();
}
/**
* Method that changes the image of the button based on flash mode
*/
private void setFlashButton() {
switch (flashMode) {
case Camera.Parameters.FLASH_MODE_ON :
flashButton.setImageDrawable(ContextCompat.getDrawable(this,R.drawable.camera_flash_on));
break;
case Camera.Parameters.FLASH_MODE_AUTO :
flashButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.camera_flash_auto));
break;
case Camera.Parameters.FLASH_MODE_OFF :
flashButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.camera_flash_off));
break;
}
}
//endregion
//region Bearing Methods
/**
* Method that gives a cardinal direction based on the current bearing to the true north
* @param bearing is the bearing to the true north
* @return cardinal direction that belongs to the bearing
*/
private String bearingToString(Double bearing) {
String strHeading = "?";
if (isBetween(bearing,-180.0,-157.5)) { strHeading = "South"; }
else if (isBetween(bearing,-157.5,-112.5)) { strHeading = "SouthWest"; }
else if (isBetween(bearing,-112.5,-67.5)) { strHeading = "West"; }
else if (isBetween(bearing,-67.5,-22.5)) { strHeading = "NorthWest"; }
else if (isBetween(bearing,-22.5,22.5)) { strHeading = "North"; }
else if (isBetween(bearing,22.5,67.5)) { strHeading = "NorthEast"; }
else if (isBetween(bearing,67.5,112.5)) { strHeading = "East"; }
else if (isBetween(bearing,112.5,157.5)) { strHeading = "SouthEast"; }
else if (isBetween(bearing,157.5,180.0)) { strHeading = "South"; }
return strHeading;
}
/**
* Method that checks if a certain number is in a certain range of numbers
* @param x is the number to check
* @param lower is the number that defines the lower boundary of the number range
* @param upper is the number that defines the upper boundary of the number range
* @return true if the number is between the other numbers, false otherwise
*/
private boolean isBetween(double x, double lower, double upper) {
return lower <= x && x <= upper;
}
@Override
public void onBearingChanged(double bearing) {
this.bearing = bearing;
mBearingProvider.setContext(this);
ImageView image = (ImageView) findViewById(R.id.ivCompass);
if (image.getVisibility() == View.VISIBLE) {
// create a rotation animation (reverse turn degree degrees)
if (bearing < 0) {
bearing += 360;
}
RotateAnimation ra = new RotateAnimation((float) currentBearing, (float) -bearing, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
// how long the animation will take place
ra.setDuration(210);
// set the animation after the end of the reservation status
ra.setFillAfter(true);
// Start the animation
image.startAnimation(ra);
rotation += currentBearing + bearing;
currentBearing = -bearing;
} else if (!pictureTaken){
ImageView image2 = (ImageView) findViewById(R.id.ivCompass2);
image2.setRotation((float) -rotation);
image.clearAnimation();
pictureTaken = true;
}
mBearingProvider.setContext(this);
}
//endregion
}
我也想知道这个警告有多糟糕,我担心这很糟糕,因为警告是红色的,而我到现在所有的其他警告都是灰色的。如果这不是一个重要的警告,请告诉我,以便继续进行测试。
修改
我现在几乎可以肯定问题在于CameraView,我现在测试了VideoActivity(它也使用了CameraView),并且我得到了同样的警告。
编辑2:
这个警告似乎并不是每次都会发生(大部分时间都是这样),但我找不到一种模式或逻辑上的原因,为什么它一次工作而另一次不起作用,但我'我会一直在寻找。
答案 0 :(得分:0)
以下是同一问题的错误报告 https://code.google.com/p/android/issues/detail?id=54285
这种解决方法可能会解决您的问题 How to fix memory leak in SurfaceView
修改强>
像这样,public class CameraView extends ... {
...
public void release(){
getHolder().getSurface().release();
}
...
}
public class CameraActivity extends ... {
...
CameraView mCameraView;
...
@Override protected void onResume() {
...
mCameraView = new CameraView(this, mCamera);
...
}
@Override protected void onPause() {
super.onPause();
mCameraView.release();
}
...
}