【问题标题】:Android Memory Leak When Passing A Fragment传递片段时的Android内存泄漏
【发布时间】:2018-10-23 07:11:43
【问题描述】:

我有这个代码,我正在使用 Barcode Google API Vision。当我打开 Fragment 并将设备旋转 6 次或更多次时,我在 Dump Heap 中看到许多实例保留在内存中(见图。)即使在我执行强制垃圾收集之后,它们也保持不变。在我下面的代码中,我没有看到任何内存泄漏。

图片在 GC 之后

奇怪的是有些设备只显示 1 个实例 GC之后的classes是正常的。

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(); 

        }
    });
}

扫描仪

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);
}
}

请查看我的代码,如果有内存泄漏,请告诉我。

Leak Canary 显示:

【问题讨论】:

    标签: android memory-leaks android-vision


    【解决方案1】:

    为什么要在 onDestroy 方法中释放 barcodecameraSource? 根据this onDestroy() 方法可以被跳过并且not被调用。也许onStop() 是更适合释放资源的地方?并分别在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();
    }
    

    另外,在创建BarcodeDetectorCameraSource 时不要传递Activity,并尽可能传递ApplicationContext

    【讨论】:

    • 我每次都在日志中检查,每次都调用了destroy方法。
    • 也可以在destroy方法中查看官方google代码github.com/googlesamples/android-vision/blob/master/…
    • @Nick,当我在谈论 Fragment.onDestroy 时,你已经检查了 Activity.onDestroy
    • 而且 Fragment 的 onDestroyed 每次都会被调用,我检查过了。
    • @Nick,也许你在创建条形码和相机源时不应该传递Activity,它们是否接受应用程序上下文?
    猜你喜欢
    • 2013-11-30
    • 2019-12-22
    • 1970-01-01
    • 2015-07-20
    • 1970-01-01
    • 2018-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多