Android应用空闲RAM占用超300MB,修复泄漏后仍异常,LeakCanary报错求解决
问题描述
我发现我的应用内存占用过高,使用Profiler检测后发现,应用刚启动、闪屏页展示时,尚未打开主页面或其他任何Activity,内存占用就已经超过150MB。
更新:我已经修复了所有已知内存泄漏,但在真机上仅浏览带有RecyclerView的MainActivity时,应用RAM占用仍接近300+MB。目前LeakCanary持续报错,提示存在内存泄漏问题,烦请各位帮忙告知解决方法:
- MainActivity内存状态截图:

- 点击任意RecyclerView条目后的内存状态截图:

- LeakCanary泄漏报告截图:

相关WorkDetailsActivity代码
public class WorkDetailsActivity extends AppCompatActivity { ArrayList<String> imagesFromURL = new ArrayList<String>(); ActivityWorkDetailsBinding binding; DatabaseReference databaseReference; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityWorkDetailsBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); String workUid_details = getIntent().getExtras().getString("UID_Details"); String title = getIntent().getExtras().getString("name"); String description = getIntent().getExtras().getString("description"); String location = getIntent().getExtras().getString("location"); String path = getIntent().getExtras().getString("path"); databaseReference = FirebaseDatabase.getInstance().getReference().child("Work").child(path); databaseReference.child(workUid_details).addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { binding.workDetailsTitle.setText(title); binding.workDetailsDescription.setText(description); binding.workDetailsLocation.setText(location); binding.getUIDDetails.setText(workUid_details); for (DataSnapshot dataSnapshot : snapshot.child("images").getChildren()) { String value = String.valueOf(dataSnapshot.child("image").getValue()); imagesFromURL.add(value); //Log.i("Value", String.valueOf(imagesFromURL)); } initRecyclerView(); } @Override public void onCancelled(@NonNull DatabaseError error) { } }); } private void initRecyclerView(){ binding.workDetailsImage.setNestedScrollingEnabled(false); binding.workDetailsImage.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); binding.workDetailsImage.setHasFixedSize(true); PrivateRecyclerAdapter adapter = new PrivateRecyclerAdapter(this, imagesFromURL); binding.workDetailsImage.setAdapter(adapter); binding.progressBar.setVisibility(View.VISIBLE); /* //Add Divider between recyclerView items DividerItemDecoration itemDecorator = new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL); binding.workDetailsImage.addItemDecoration(itemDecorator); final int radius = getResources().getDimensionPixelSize(R.dimen.radius); final int dotsHeight = getResources().getDimensionPixelSize(R.dimen.dots_height); final int color = ContextCompat.getColor(this, R.color.green); binding.workDetailsImage.addItemDecoration(new DotsIndicatorDecoration(radius, radius * 2, dotsHeight, color, color)); binding.workDetailsImage.setOnFlingListener(null); new PagerSnapHelper().attachToRecyclerView(binding.workDetailsImage);*/ } @Override protected void onDestroy() { super.onDestroy(); imagesFromURL = null; binding.workDetailsImage.setAdapter(null); binding.workDetailsTitle.setText(null); binding.workDetailsLocation.setText(null); binding.workDetailsDescription.setText(null); binding.getUIDDetails.setText(null); binding.workDetailsImage.setAdapter(null); } }
修复方案
- 移除Firebase事件监听
当前代码的核心泄漏点是匿名ValueEventListener没有主动移除,该内部类持有Activity引用,Firebase内部会一直持有监听实例,导致Activity销毁后无法被回收。修改方法:
- 新增成员变量存储监听实例
private ValueEventListener workValueListener;
- onCreate中给变量赋值后再添加监听
workValueListener = new ValueEventListener() { // 原有onDataChange、onCancelled逻辑保持不变 }; databaseReference.child(workUid_details).addValueEventListener(workValueListener);
- onDestroy中新增移除监听的逻辑
if (databaseReference != null && workValueListener != null) { databaseReference.removeEventListener(workValueListener); }
- 优化图片加载逻辑
RecyclerView滑动后内存上涨到300MB+基本是图片加载框架的配置问题:
- 检查图片框架(如Glide、Fresco)的内存缓存配置,建议设置最大缓存为App可用内存的1/5~1/4
- 在WorkDetailsActivity的onDestroy中添加当前页面图片内存清理逻辑,以Glide为例:
Glide.with(this).clear(binding.workDetailsImage);
- 列表加载图片时根据控件尺寸做采样压缩,避免加载过大分辨率的图片到内存。
冗余代码清理
onDestroy中重复调用了两次binding.workDetailsImage.setAdapter(null),可删除其中一次,无需手动给TextView设置null,仅需清理持有Context的长生命周期引用即可。启动内存过高排查
闪屏页内存超过150MB需排查Application初始化逻辑,检查是否存在不必要的第三方SDK同步初始化、是否内置了过大的资源文件或so库,可通过Profiler的内存分配追踪定位占用内存最高的资源/类进行针对性优化。
内容的提问来源于stack exchange,提问作者MiniDev99
相关产品推荐
相关产品推荐

