android - 旋转和按钮点击可能导致内存泄漏

标签 android memory memory-management memory-leaks reference

我认为我的应用正在占用 GC 应该能够重新分配的额外内存。我不知道这些是否会被视为内存泄漏,但我注意到有 2 个地方可能存在问题

  • 从 App 开始,不断旋转我的设备,从纵向到横向,再到纵向,再到横向……

    • +300KB 每次旋转累积内存使用量
  • 有 2 个输入,每个按钮都点击

    • +30KB 每次点击累积内存使用量

这个问题是只要应用仍在 View 中,内存就永远不会释放。

示例:旋转设备 10 次,单击按钮 50 次 -> 消耗 4.5MB 内存。如果我让应用程序保持打开状态并且 1 小时内不执行任何操作,那么我的应用程序仍将消耗 4.5MB 内存;尽管很多内存应该在 59 分钟前释放!!

我担心的是为什么当应用程序始终在 View 中时内存永远不会释放?

我对它的工作原理有误吗?

注意:该应用名为ContrivedCalculator

代码

界面

public class Calculator extends AppCompatActivity implements ICalculatorInteraction {

    private EditText txtNumber1, txtNumber2, txtResult;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_calculator);

        Button btnAdd = (Button) findViewById(R.id.btnAddition);
        Button btnSub = (Button) findViewById(R.id.btnSubtract);
        Button btnMul = (Button) findViewById(R.id.btnMultiple);
        Button btnDiv = (Button) findViewById(R.id.btnDivide);

        txtNumber1 = (EditText) findViewById(R.id.txtNumber1);
        txtNumber2 = (EditText) findViewById(R.id.txtNumber2);
        txtResult = (EditText) findViewById(R.id.txtResult);

        btnAdd.setOnClickListener(new OperationClick(Add).listenerOn(this));   
        btnSub.setOnClickListener(new OperationClick(Subtract).listenerOn(this));
        btnMul.setOnClickListener(new OperationClick(Multiply).listenerOn(this));
        btnDiv.setOnClickListener(new OperationClick(Divide).listenerOn(this));

    @Override
    public String getFirstNumber() { return valueOf(this.txtNumber1); }

    @Override
    public String getSecondNumber() { return valueOf(this.txtNumber2); }

    @Override
    public void updateResult(String result) { this.txtResult.setText(result); }

    private String valueOf(EditText textbox) {

        String text = textbox.getText().toString();

        if (text.isEmpty()) {

            textbox.setText("0");
            return "0";

        }

        return text;
    }

// default android activity methods

}

监听器逻辑

public class OperationClick {

    private BinaryOperation operation;   // ENUM - advanced
    private View.OnClickListener listener;

    public OperationClick(final BinaryOperation operation) { this.operation = operation; }

    public View.OnClickListener listenerOn(final ICalculatorInteraction UI) {

        if (listener != null) return listener;
        return listener = new View.OnClickListener() {

            @Override
            public void onClick(View v) {

                double  num1, num2, total;
                String result, sign;

                num1 = Double.parseDouble(UI.getFirstNumber());
                num2 = Double.parseDouble(UI.getSecondNumber());
                total = operation.execute(num1, num2);
                sign = operation.getSymbol();

                result = String.format("%s %s %s = %s", num1, sign, num2, total);

                UI.updateResult(result);

            }

        };

    }

计算逻辑

public enum BinaryOperation {

    Add      ("+") { @Override double execute(final double a, final double b) { return a + b; } },
    Subtract ("-") { @Override double execute(final double a, final double b) { return a - b; } },
    Multiply ("×") { @Override double execute(final double a, final double b) { return a * b; } },
    Divide   ("÷") { @Override double execute(final double a, final double b) { return a / b; } };

    private final String symbol;

    abstract double execute(double a, double b);

    BinaryOperation(String symbol) { this.symbol = symbol; }

    public String getSymbol() { return this.symbol; }

}

最佳答案

跟踪内存泄漏最好使用 Eclipse MAT 来完成(不用担心名称,它也适用于 Android Studio 的内存转储)。该工具的学习曲线非常陡峭,但它是您能想象到的用于跟踪此类内存问题的最强大工具。

确实,您在这一行中造成了内存泄漏:

btnAdd.setOnClickListener(new OperationClick(Add).listenerOn(this));

问题在于 OperationClick 的实例包含对 Activity 的引用,因为它是通过 .listenerOn(this) 传递的。

当您旋转设备时,您知道 Activity 已重新创建。我假设您没有在 onDestroy() 方法中清除监听器,所以您最终得到了泄漏的 Activity(每个旋转 4 次是正确的)。

顺便说一句,上周 Leak Canary由来自 SquareUp 的帅哥发布。这是一个用于在 Android 上查找内存泄漏的漂亮库。第一个此类库,强烈推荐你试试看!

编辑:要修复泄漏,请不要使用匿名 OperationClick 对象。还要在其中添加一个“清理”方法,您可以在其中删除在 listenerOn() 中创建的 listener

关于android - 旋转和按钮点击可能导致内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30155789/

相关文章:

android - 从其他应用程序接收数据在android中为空

java - Docker 容器 - JVM 内存峰值 - Arena block 内存空间

java - 如何修复在 tablayout 之间切换时谷歌登录选项功能重复并崩溃的问题

Android -TranslateAnimation 不工作

c - 释放字符指针

c++ - 从 C++ 中的函数返回动态分配的缓冲区的最佳模式是什么?

C++ 指针设置为地址,但在程序的不同部分莫名其妙地指向不同的值

memory - 为什么32位机器最大有4GB?

java - 如何解决Android中NameValuePair不能为String的问题

c++ - C++中的全局变量内存分配