I have this code and Iam using the 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. In my code below i dont see any memory leaks.
The weird part is that some devices only show 1 instance of the classes after GC which is normal.
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.
Why you're releasing barcode
and cameraSource
in onDestroy
method?
According to this onDestroy()
method could be skipped and not called. Maybe onStop()
is more proper place to release resources? And acquire them in onStart()
respectively.
@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.