繁体   English   中英

onPictureTaken方法中的位图不会被垃圾收集

[英]Bitmap in onPictureTaken method is not garbage collected

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

使用bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);创建的bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); 即使bitmap.recycle()System.gc()也不会回收内存,也永远不会收回内存。

这是一些有关回收位图的线程,但是它们都不起作用。

  1. 回收位图不会释放内存
  2. 即使我回收位图,内存使用量也不会减少

这是我与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;
        }
    }
}

这是图像保存过程的内存分析器。 应用程序在运行相机预览时正在使用16 mb RAM。 当我触摸按钮以保存图像时,它在保存时上升到110mb,大约在25.00s内开始显示,我没有使用线程进行视觉检查,在保存时应用程序冻结,然后减小到75mb,并保持在此水平如果我不手动GC或使用主页按钮暂停应用程序。 我以43.00s手动GC。 我将应用程序保持打开状态,并且7分钟后仍未垃圾收集位图。 我还检查了CameraKit应用程序和CameraView ,在拍照后还没有进行GC处理 没有任何方法可以手动从Bitmap声明内存。

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

图像保存过程的Memory Profiler 内存配置文件2

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

Memory Profiler Camera2 Api 虚线是具有锯齿图案的对象分配,对象在边缘上进行了GC'处理,但所有内存均稳定且未遵循对象分配模式。 这怎么可能?

我也较早地发现了这个问题,我不知道为什么会这样,但我认为您不应该为此担心。

正如recycle方法的文档所述,不能保证在调用此方法后立即释放位图资源。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM