【问题标题】:Prevent memory leak in Android防止Android中的内存泄漏
【发布时间】:2016-07-10 19:11:39
【问题描述】:

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

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

在大约 30 秒时,应用程序崩溃并出现明显的“OutOfMemory”错误:

抛出 OutOfMemoryError “无法分配 17469452 字节分配,804912 可用字节和 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()(只有super),问题仍然存在,即使内存泄漏也很差(只有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 ---

在下图中,您可以看到两种情况下的内存分配:我的所有应用程序垃圾(如 430kB 图像)[a] 和没有(干净的onCreate())[b]。
请注意,如上所述,内存泄漏在这两种情况下仍然存在,即使它不是那么大(但确实存在!)。

内存泄漏,案例[a]

内存泄漏,案例[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 上看到一个示例

【问题讨论】:

  • 你是使用普通的 Activity 还是 Activity 中的 Fragments?您能否也展示您的其他生命周期方法(尤其是 onCreate)?
  • 如果显示更多代码会很有帮助。此外,您在 onDestroy() 中所做的事情并没有真正做任何事情,除非您的 Activity / Activity 的静态成员之外的东西正在引用您的视图,这无论如何都不会被您的 unbindDrawables() 解决。
  • 按要求添加代码,希望对您有所帮助
  • 什么是组件名?我看到您将“this”(即活动)传递给它。
  • ComponentName 用于设置 DevicePolicyManager.setLockTaskPackages() 然后,必须将应用程序上下文传递给新的 ComponentName。您发现了内存泄漏,现在我已使用 getApplicationContext() 进行了更正。但不幸的是,它并没有破坏应用程序。

标签: android performance memory memory-leaks


【解决方案1】:

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

android:configChanges="orientation|screenSize"

【讨论】:

  • 我知道,但这是有史以来最糟糕的解决方案,正如谷歌和这里所说的那样,check this
  • 这并不能解决问题,它只是避免了一段时间,因为最终活动可能会再次加载,这会泄漏内存,最大限度地减少泄漏内存的频率而不是修复泄漏就像从沉船中捞水一样,您需要堵住孔。
【解决方案2】:

我已经设法解决了这个问题。
这完全是因为一个该死的计时器,它在每次屏幕方向更改时设置一个滴答事件。
为了解决这个问题,我简单地将计时器绑定到一个变量,并在onSaveInstance() 中调用了timer.cancel()

现在我的内存泄漏已经消失了(如下图):

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-21
    • 2011-12-05
    • 2015-02-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多