onPictureTaken方法中的位图不是垃圾回收

时间:2017-10-30 14:05:38

标签: android bitmap garbage-collection android-camera

我正在使用Camera1 Api使用SurfaceView,TextureView测试一些功能。

使用bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);创建的位图永远不会被回收,即使调用了bitmap.recycle()System.gc(),也永远无法回复内存。

以下是关于回收Bitmap的几个主题,但它们都不起作用。

  1. Recycling Bitmap does not free memory
  2. Memory usage does not decrease even I recycle bitmaps
  3. 这是我使用SurfaceView的代码,图像为4160,高度为3120,返回的位图大约为50mb。

    CameraActivity

    public class CameraActivity extends Activity {
        private static final String SAVE_DIR = "Folder";
        private Camera mCamera;
        private FrameLayout preview;
        private CameraPreview mPreview;
    
        public static final int MEDIA_TYPE_IMAGE = 1;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // Create an instance of Camera
            if (checkCameraHardware(this)) {
                mCamera = getCameraInstance();
            }
    
            if (mCamera == null) {
                Toast.makeText(this, "Camera null ", Toast.LENGTH_SHORT).show();
                return;
            }
            // Create our Preview view and set it as the content of our activity.
            mPreview = new CameraPreview(this, mCamera);
            preview = (FrameLayout) findViewById(R.id.camera_preview);
            preview.addView(mPreview);
    
            Button captureButton = (Button) findViewById(R.id.button_capture);
            captureButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // get an image from the camera
                    mCamera.takePicture(null, null, mPicture);
    
                }
            });
        }
    
        /**
         * Check if this device has a camera
         */
        private boolean checkCameraHardware(Context context) {
            if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
                // this device has a camera
                return true;
            } else {
                // no camera on this device
                return false;
            }
        }
    
        /**
         * A safe way to get an instance of the Camera object.
         */
        public Camera getCameraInstance() {
            Camera c = null;
            try {
                c = Camera.open(); // attempt to get a Camera instance
                setCameraDisplayOrientation(this, CameraInfo.CAMERA_FACING_BACK, c);
            } catch (Exception e) {
                // Camera is not available (in use or does not exist)
            }
            return c; // returns null if camera is unavailable
        }
    
        public void setCameraDisplayOrientation(Activity activity, int cameraId, android.hardware.Camera camera) {
            android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
            android.hardware.Camera.getCameraInfo(cameraId, info);
            int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
            int orientation = getResources().getConfiguration().orientation;
    
            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;
            }
            camera.setDisplayOrientation(result);
            Camera.Parameters params = camera.getParameters();
            params.setRotation(90);
            camera.setParameters(params);
        }
    
        private PictureCallback mPicture = new PictureCallback() {
    
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                long startTime = System.currentTimeMillis();
    
                File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
                FileOutputStream fos = null;
                Bitmap bitmap = null;
    
                if (pictureFile == null) {
                    return;
                }
    
                try {
                    fos = new FileOutputStream(pictureFile);
                    bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                    bitmap.compress(CompressFormat.JPEG, 100, fos);
    
                } catch (FileNotFoundException e) {
                    System.out.println("CameraActivityonPictureTaken() File not found: " + e.getMessage());
                } finally {
                    try {
                        if (fos != null) {
                            fos.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
    
                    fos = null;
                    pictureFile = null;
    
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                        System.out.println("CameraActivity onPictureTaken() Bitmap recycled: " + bitmap.isRecycled() + ", size: " + bitmap.getAllocationByteCount() / (1024) + "kb");
                    }
                    bitmap.recycle();
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                        System.out.println("CameraSurfaceTextureListener onPictureTaken() Bitmap recycled: " + bitmap.isRecycled() + ", size: " + bitmap.getAllocationByteCount() / (1024) + "kb");
                    }
    
    
                    bitmap = null;
                    System.gc();
    
                    long finishTime = System.currentTimeMillis();
                    System.out.println("CameraActivity onPictureTaken() TIME: " + (finishTime - startTime) + "ms");
                    mPreview.refreshPreview();
                }
            }
        };
    
        /**
         * Create a File for saving an image or video
         */
        private File getOutputMediaFile(int type) {
            // To be safe, you should check that the SDCard is mounted
            // using Environment.getExternalStorageState() before doing this.
    
            File mediaStorageDir = new File(Environment.getExternalStorageDirectory(), "MyCameraApp");
            // This location works best if you want the created images to be shared
            // between applications and persist after your app has been uninstalled.
    
            // Create the storage directory if it does not exist
            if (!mediaStorageDir.exists()) {
                if (!mediaStorageDir.mkdirs()) {
                    Log.d("MyCameraApp", "failed to create directory");
                    return null;
                }
            }
    
            // Create a media file name
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
            File mediaFile;
            if (type == MEDIA_TYPE_IMAGE) {
                mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
            } else {
                return null;
            }
    
            return mediaFile;
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            releaseCamera(); // release the camera immediately on pause event
            if (preview != null && mPreview != null) {
                preview.removeView(mPreview);
                mPreview = null;
            }
        }
    
        @Override
        protected void onResume() {
            super.onResume();
    
    
            // Create an instance of Camera
            if (checkCameraHardware(this)) {
                if (mCamera == null) {
                    mCamera = getCameraInstance();
                    System.out.println("onResume() mCamera: " + mCamera);
                    if (mPreview == null) {
                        // Create our Preview view and set it as the content of our
                        // activity.
                        mPreview = new CameraPreview(this, mCamera);
                        System.out.println("onResume() preview child count: " + preview.getChildCount());
                        preview.removeAllViews();
                        preview.addView(mPreview);
                    } else {
                        mPreview.refreshPreview();
                    }
                }
            }
        }
    
        private void releaseCamera() {
            if (mCamera != null) {
                mCamera.release(); // release the camera for other applications
                mCamera = null;
            }
        }
    }
    

    这是图像保存过程的Memory Profiler。应用程序在运行相机预览时使用16 MB RAM。当我触摸按钮保存图像时,它保存时上升到110mb,它在大约25.00s开始,我没有使用线程在视觉上检查,应用程序在保存时冻结,然后它减少到75 mb并保持在这个水平如果我不使用主页按钮手动GC或暂停应用程序。我在43.00s手动GC。我打开应用程序,7分钟后Bitmap仍然没有被垃圾回收。我还检查了CameraKit app和CameraView,拍摄照片后它们也没有GC。有没有办法手动从Bitmap声明内存。

    如何检查Activity是否与新的Memory Profiler泄漏并创建.hprof文件?

    Memory Profiler of the image saving process Memory Profile 2

    我还测试Camera2 Api代码。以下是此代码的内存配置文件。

    Memory Profiler Camera2 Api 虚线是具有锯齿图案的对象分配,边缘上的对象GC,但是所有存储器都是稳定的并且不遵循对象分配模式。这怎么可能?

1 个答案:

答案 0 :(得分:0)

我之前也发现了这个问题,我不知道为什么会这样,但我认为你不应该为此担心。

由于recycle方法的文档说不能保证在调用此方法后立即释放位图资源。

要解释为什么您不必担心,当您尝试在内存中分配新图像时,将释放内存。尝试拍摄新照片并检查内存,不会添加内存。或者甚至更好,尝试拍摄5张照片,你会发现它不会占用5张图像的内存,因为在创建新的Bitmap时会释放内存。