Android 传递片段时内存泄漏
Android Memory Leak When Passing A Fragment
我有这个代码,我正在使用条形码 Google API 视觉。当我打开片段并将设备旋转 6 次或更多次时,我在转储堆中看到许多实例保留在内存中(参见图片)。即使在我执行强制垃圾收集之后,它们也保持不变。在我下面的代码中,我没有看到任何内存泄漏。
图片是GC后的
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();
}
});
}
扫描仪
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 显示:
为什么要在 onDestroy
方法中释放 barcode
和 cameraSource
?
根据 this onDestroy()
方法可以跳过并且 不 被调用。也许 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();
}
另外,创建BarcodeDetector
和CameraSource
时不要传Activity
,可以的话传ApplicationContext
我有这个代码,我正在使用条形码 Google API 视觉。当我打开片段并将设备旋转 6 次或更多次时,我在转储堆中看到许多实例保留在内存中(参见图片)。即使在我执行强制垃圾收集之后,它们也保持不变。在我下面的代码中,我没有看到任何内存泄漏。
图片是GC后的
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();
}
});
}
扫描仪
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 显示:
为什么要在 onDestroy
方法中释放 barcode
和 cameraSource
?
根据 this onDestroy()
方法可以跳过并且 不 被调用。也许 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();
}
另外,创建BarcodeDetector
和CameraSource
时不要传Activity
,可以的话传ApplicationContext