You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Activity内存占用过高且无法释放,如何优化?

解决Activity finish()后内存未释放的内存泄漏问题

先理清楚你的问题场景:

我有一个Activity内存占用约70MB,调用finish()关闭该Activity后内存并未释放,再次启动时内存占用额外增加70MB。该Activity使用数据库,但我认为数据库并非问题所在,因为另一个使用相同数据库的Activity仅占用14MB内存。

你提供的问题类代码如下:

package ir.noideaw.aufbau; 
import android.graphics.Color; 
import android.graphics.drawable.ColorDrawable; 
import android.graphics.drawable.GradientDrawable; 
import android.os.Build; 
import android.os.Bundle; 
import android.support.annotation.Nullable; 
import android.support.design.widget.AppBarLayout; 
import android.support.design.widget.CollapsingToolbarLayout; 
import android.support.v7.app.AppCompatActivity; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.ImageView; 
import android.widget.ListAdapter; 
import android.widget.ListView; 
import java.util.ArrayList; 
import java.util.List; 
import ir.noideaw.aufbau.Adapter.UnitAdapter; 
import ir.noideaw.aufbau.DatabaseHandler.DatabaseAccess; 
import ir.noideaw.aufbau.Model.Unit; 

public class ElementInfo extends AppCompatActivity { 
    private ImageView elementPic; 
    private CollapsingToolbarLayout ct; 
    private AppBarLayout appBarLayout; 
    private int currentApiVersion; 
    private ArrayList<Unit> ovUnitsData; 
    private List<String> properties; 
    private ListView listView; 
    // 新增:保存监听器成员变量
    private AppBarLayout.OnOffsetChangedListener appBarOffsetListener;
    private View.OnSystemUiVisibilityChangeListener systemUiListener;

    @Override 
    protected void onCreate(@Nullable Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.element_details); 
        initialize(); 
        Bundle data = getIntent().getExtras(); 
        final String sy = data.getString("elementSymbol"); 
        DatabaseAccess database = DatabaseAccess.getInstance(this); 
        database.open(); 
        properties = database.setProperties(sy); 
        database.close(); 
        int atomicNumber = Integer.valueOf(database.getAtomicNumber()); 
        int id = getResources().getIdentifier("element_" + atomicNumber, "drawable", getPackageName()); 
        if (id != 0) { 
            elementPic.setImageResource(id); 
        } else { 
            elementPic.setBackgroundColor(Color.parseColor(database.getColor())); 
        } 

        // 把监听器赋值给成员变量
        appBarOffsetListener = new AppBarLayout.OnOffsetChangedListener() { 
            boolean isShow = true; 
            int scrollRange = -1; 
            @Override 
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { 
                if (scrollRange == -1) { 
                    scrollRange = appBarLayout.getTotalScrollRange(); 
                } 
                if (scrollRange + verticalOffset == 0) { 
                    ct.setTitle(sy); 
                    isShow = true; 
                } else if (isShow) { 
                    ct.setTitle(" ");//careful there should be a space between double quote otherwise it wont work 
                    isShow = false; 
                } 
            } 
        };
        appBarLayout.addOnOffsetChangedListener(appBarOffsetListener); 

        setUnitsData(); 
        UnitAdapter adapter = new UnitAdapter(this, ovUnitsData); 
        listView.setAdapter(adapter); 
        listView.setMinimumHeight(listView.getChildCount()*40); 
        currentApiVersion = android.os.Build.VERSION.SDK_INT; 
        final int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; 
        if (currentApiVersion >= Build.VERSION_CODES.KITKAT) { 
            getWindow().getDecorView().setSystemUiVisibility(flags); 
            final View decorView = getWindow().getDecorView(); 
            // 保存系统UI监听器到成员变量
            systemUiListener = new View.OnSystemUiVisibilityChangeListener() { 
                @Override 
                public void onSystemUiVisibilityChange(int visibility) { 
                    if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { 
                        decorView.setSystemUiVisibility(flags); 
                    } 
                } 
            };
            decorView.setOnSystemUiVisibilityChangeListener(systemUiListener); 
        } 
        setListViewHeightBasedOnChildren(listView); 
        setListViewDevider(listView); 
        listView.setDividerHeight(1); 
        // 建议移除手动GC调用,交给系统自动处理
        // System.gc(); 
    } 

    @Override 
    public void onBackPressed() { 
        finish(); 
    } 

    @Override 
    public void onWindowFocusChanged(boolean hasFocus) { 
        super.onWindowFocusChanged(hasFocus); 
        if (currentApiVersion >= Build.VERSION_CODES.KITKAT && hasFocus) { 
            getWindow().getDecorView().setSystemUiVisibility( 
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); 
        } 
    } 

    @Override 
    protected void onDestroy() { 
        super.onDestroy(); 
        // 移除AppBarLayout的监听器,避免持有Activity引用
        if (appBarOffsetListener != null) {
            appBarLayout.removeOnOffsetChangedListener(appBarOffsetListener);
        }
        // 移除系统UI监听器
        if (systemUiListener != null && currentApiVersion >= Build.VERSION_CODES.KITKAT) {
            getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(null);
        }
        // 清空ListView的Adapter,切断引用
        if (listView != null) {
            listView.setAdapter(null);
        }
        // 清空数据源,帮助GC回收
        if (ovUnitsData != null) {
            ovUnitsData.clear();
            ovUnitsData = null;
        }
        properties = null;
        // 同样不建议手动调用GC
        // Runtime.getRuntime().gc(); 
    } 

    private void initialize() { 
        elementPic = findViewById(R.id.backdrop); 
        ct = findViewById(R.id.collapsing_toolbar); 
        appBarLayout = findViewById(R.id.appbar); 
        listView = findViewById(R.id.ov_list_view); 
        ovUnitsData = new ArrayList<>(); 
    } 

    public static void setListViewHeightBasedOnChildren(ListView listView) { 
        ListAdapter listAdapter = listView.getAdapter(); 
        if (listAdapter == null) { 
            // pre-condition return; 
        } 
        int totalHeight = 0; 
        for (int i = 0; i < listAdapter.getCount(); i++) { 
            View listItem = listAdapter.getView(i, null, listView); 
            listItem.measure(0, 0); 
            totalHeight += listItem.getMeasuredHeight(); 
        } 
        ViewGroup.LayoutParams params = listView.getLayoutParams(); 
        params.height = totalHeight +
        // 这里你的代码好像没写完,注意补全这部分逻辑,避免潜在问题
    }
}

接下来咱们分析几个核心的泄漏点和解决思路:

1. 未移除的监听器是最大嫌疑

你在onCreate里给AppBarLayout和DecorView都添加了匿名内部类监听器,这些匿名类会隐式持有Activity的强引用。如果不在Activity销毁时移除这些监听器,系统GC就无法回收这个Activity实例,导致内存泄漏。

解决方法就是把监听器保存为Activity的成员变量,然后在onDestroy方法中主动移除它们,就像上面修改后的代码那样。

2. 数据库单例的上下文问题

虽然你提到另一个用相同数据库的Activity没问题,但还是要检查DatabaseAccess.getInstance(this)这里的上下文:如果这个单例内部持有了传入的Activity上下文,而不是Application上下文,那么这个单例会一直持有Activity的引用,导致泄漏。

建议修改DatabaseAccess的getInstance方法,改用getApplicationContext():

DatabaseAccess database = DatabaseAccess.getInstance(getApplicationContext());

3. ListView和Adapter的引用问题

你的UnitAdapter传入了Activity的上下文,如果Adapter内部持有这个上下文的强引用,或者数据源ovUnitsData里的对象有其他引用,也可能导致泄漏。在onDestroy里清空Adapter和数据源,可以切断这些引用链。

4. 避免手动调用GC

你在onCreate和onDestroy里手动调用了System.gc()和Runtime.getRuntime().gc(),其实这没啥必要——GC的触发时机由系统控制,手动调用不仅不能立即回收内存,还可能影响应用性能,建议移除这些调用。

最后,你可以用Android Studio的Profiler工具来确认内存泄漏:打开Profiler的Memory面板,多次启动和关闭这个Activity,查看内存快照里是否有残留的ElementInfo实例,这样就能验证咱们的修复是否生效了。

内容的提问来源于stack exchange,提问作者Jack Johnson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:21:40