简体   繁体   English

传递片段时Android内存泄漏

[英]Android Memory Leak When Passing A Fragment

I have this code and Iam using the Barcode Google API Vision.我有这个代码,我正在使用 Barcode Google API Vision。 When i open the Fragment and rotate the device many times 6 or more, i see in the Dump Heap that many instances remain in memory (see pic.) Even after i do a forced Garbage Collection they stay the same.当我打开 Fragment 并多次旋转设备 6 次或更多次时,我在转储堆中看到许多实例保留在内存中(见图)。即使在我执行强制垃圾收集之后,它们也保持不变。 In my code below i dont see any memory leaks.在我下面的代码中,我没有看到任何内存泄漏。

Image is after GC图像是在 GC 之后在此处输入图片说明

The weird part is that some devices only show 1 instance of the classes after GC which is normal.奇怪的是,有些设备在 GC 之后只显示 1 个类的实例,这是正常的。

Emulator API 27  : NO MEMORY LEAKS
Samsung j500FN   : NO MEMORY LEAKS
Xiaomi mi8       : Memory Leak
Galaxy Tablet E  : Memory Leak

MainActivity主要活动

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
            Fragment sf = getSupportFragmentManager().findFragmentByTag("Scanner");
                transaction.add(R.id.root, new Scanner(), "Scanner");
                transaction.addToBackStack(null);
                transaction.commit(); 

        }
    });
}

Scanner扫描器

public class Scanner extends Fragment{

public SurfaceView cameraView;
public BarcodeDetector barcode;
public CameraSource cameraSource;
private SurfaceHolder.Callback cameraCallback;
private ActivityScanBinding mbinding;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    Log.d("ActivityScan","onCreate");
}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    Log.d("ActivityScan","onCreateView");

    mbinding = DataBindingUtil.inflate(inflater, R.layout.activity_scan, container, false);


    mbinding.getRoot().setOnTouchListener(new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            return true;
        }

    });
    cameraView = mbinding.getRoot().findViewById(R.id.cameraView);

    return mbinding.getRoot();
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    Scan();
}

@Override
public void onDestroy() {

    Log.d("ActivityScan","Destroyed");
    if(barcode!=null) {
        barcode.release();
        Log.d("barcode","Released");
    }
    if(cameraSource!=null) {
        cameraSource.release();
        Log.d("cameraSource ","Released");
    }
    if(cameraView!=null) {
        removeCameraViewCallback();
    }

    super.onDestroy();
}

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
}



public void Scan(){

    cameraView.setZOrderMediaOverlay(true);

    barcode = new BarcodeDetector.Builder(getActivity())
            .setBarcodeFormats(Barcode.QR_CODE)
            .build();

        if(!barcode.isOperational()){
            return;
        }


    cameraSource = new CameraSource.Builder(getActivity(), barcode)
            .setFacing(CameraSource.CAMERA_FACING_FRONT)
            .setRequestedFps(24)
            .setAutoFocusEnabled(true)
            .setRequestedPreviewSize(1920,1080)
            .build();

    cameraCallback = new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {

                if(ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
                    cameraSource.start(cameraView.getHolder());
                }
            }
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            cameraSource.stop();
        }
    };

    cameraView.getHolder().addCallback(cameraCallback);

    barcode.setProcessor(new Detector.Processor<Barcode>() {

        @Override
        public void release() {}

        @Override
        public void receiveDetections(Detector.Detections<Barcode> detections) {
            final SparseArray<Barcode> barcodes =  detections.getDetectedItems();
            if(barcodes.size() > 0){

            }
        }
    });


}

public void removeCameraViewCallback(){
    cameraView.getHolder().removeCallback(cameraCallback);
}
}

Please see my code and let me know if there is a memory leak.请查看我的代码并让我知道是否存在内存泄漏。

Leak Canary shows this: Leak Canary 显示了这一点: 在此处输入图片说明 在此处输入图片说明

Why you're releasing barcode and cameraSource in onDestroy method?为什么要在onDestroy方法中释放barcodecameraSource According to this onDestroy() method could be skipped and not called.根据这个onDestroy()方法可以跳过而不是调用。 Maybe onStop() is more proper place to release resources?也许onStop()是释放资源更合适的地方? And acquire them in onStart() respectively.并分别在onStart()获取它们。

@Override
public void onStop() {

    Log.d("ActivityScan","Destroyed");
    if(barcode!=null) {
        barcode.release();
        Log.d("barcode","Released");
    }
    if(cameraSource!=null) {
        cameraSource.release();
        Log.d("cameraSource ","Released");
    }
    if(cameraView!=null) {
        removeCameraViewCallback();
    }

    super.onStop();
}

Also, do not pass Activity when creating BarcodeDetector and CameraSource , and pass ApplicationContext if possible.此外,在创建BarcodeDetectorCameraSource时不要传递Activity ,并尽可能传递ApplicationContext

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

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