防止Android中的内存泄漏

时间:2016-03-23 13:09:47

标签: android performance memory memory-leaks

我在Android上遇到内存泄漏问题 我有一个简单的应用程序,相对图片作为背景,它工作正常,直到我改变方向。

以下是显示内存泄漏(随时间分配的内存)的图表。每个尖峰对应一个方向变化,每个尖峰是+ 20M的分配内存
Memory leak over time

在大约30秒时,应用程序崩溃了一个明显的" OutOfMemory"错误:

  

抛出OutOfMemoryError"无法分配带有804912个空闲字节的17469452字节分配和786KB直到OOM"`

布局只是一个简单的RelativeLayout,背景是jpeg图像  大小〜430k

我确实实施了onDestroy()反内存泄漏解决方案(建议here):

@Override
protected void onDestroy()
{
    unbindDrawables(view);
    view = null;
    System.gc();
    super.onDestroy();
}

private void unbindDrawables(View view) {
    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }
    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }
        ((ViewGroup) view).removeAllViews();
    }
}

但实际上并没有改变任何事情。

我能解决这个问题吗? 我确实需要图像那么大,因为运行应用程序的设备有一个大屏幕。

- 编辑 -
按要求添加onCreate()代码, 请注意,即使没有调用initMainView(),即使有一个空onCreate()(仅限超级),问题仍然存在,即使内存泄漏较差(仅0.8M),但仍然存在。<登记/> 我在Pixel C上运行Android 6.0.1,如果这可能会有所帮助。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    System.gc();
    w = getWindow();
    w.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    dpm = (DevicePolicyManager) this.getSystemService(Context.DEVICE_POLICY_SERVICE);
    deviceAdmin = new ComponentName(this, AdmRcvr.class);

    if(!dpm.isDeviceOwnerApp(getPackageName())) {
        setContentView(R.layout.deviceownertutorial);
        final TextView tvTutorial = (TextView) w.findViewById(R.id.tutorialtv);
        tvTutorial.setText(Html.fromHtml(getString(R.string.deviceownertutorial)));

        final TextView tvDeviceOwnerError = (TextView) w.findViewById(R.id.tverrordeviceadmin);

        Button checkDeviceAdmin = (Button) w.findViewById(R.id.cdevadmin);
        checkDeviceAdmin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (dpm.isDeviceOwnerApp(getPackageName())) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            setContentView(R.layout.startview);
                            initMainView();
                        }
                    });
                } else {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            tvDeviceOwnerError.setText("App is not the device admin");
                            tvDeviceOwnerError.setVisibility(View.VISIBLE);
                        }
                    });
                }
            }
        });
    }
    else
    {
        setContentView(R.layout.startview);
        initMainView();
    }
}


public void initMainView(){
    // We're device owners!
    View mDecorView = w.getDecorView();
    mDecorView.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);
    mDecorView = null;
    view = findViewById(R.id.sv_relativelayout);
    initReader();
    initBitmapCache();
    enableTimer();
    currentStep = 1;
}

---编辑2 ---

在下面的图像中,你可以看到两种情况下的内存分配:我的所有app垃圾(如430kB图像)[a]和没有(一个干净的onCreate())[b]。 请注意,如上所述,在这两种情况下仍然存在内存泄漏,即使很难,也不会那么大(但有!)。

Memory leak in case [a] 内存泄漏,案例[a]

Memory leak in case [b] 内存泄漏,案例[b]

该应用程序是针对SDK v.23和buildToolsVersion 23.0.2编译的。 依赖关系如下:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.2.1'
    compile 'com.android.support:design:23.2.1'
}

app / libs如下:

libs
└── acssmc-1.1.2.jar  

---编辑3 ---
在使用新创建的应用程序进行测试时,内存将按预期进行管理,您可以在我的GitHub repo上看到一个示例

2 个答案:

答案 0 :(得分:0)

您可以尝试避免在您的活动中的清单中重新加载您的活动:

android:configChanges="orientation|screenSize"

答案 1 :(得分:0)

我设法解决了这个问题 这完全是因为一个该死的计时器在每个屏幕方向变化上设置了一个滴答事件 要解决此问题,我只需将计时器绑定到变量,并在timer.cancel()中调用onSaveInstance()

现在我的内存泄漏消失了(如下图所示):
no more memory leaks