【问题标题】:Some help uncovering memory leaks一些帮助发现内存泄漏
【发布时间】:2011-01-03 20:30:21
【问题描述】:

我有几个人在使用我的应用程序,听起来几天后,如果我的应用程序没有被杀死,手机的响应速度就会变慢并且有点奇怪。

我需要一些关于内存管理的帮助。我有几个问题:

1) 我在我的应用程序中很少引用静态类或变量。我有一个类(在它自己的文件中),它本质上是一个单例。这个类只有方法。没有变量之类的。我的应用程序大量使用了 Web 服务调用,而这个类就是它的访问点。调用此类中的方法将启动一个 IntentService,它会进行实际的 Web 服务调用。这是一个例子。请注意,callWebService 方法还引用了 MyIntentService.JOB_DELETE 和 MyIntentService.ITEM_ALL,它们是 MyIntentService 中的静态 int:

public class MySingleton {

    private static MySingleton mInstance = null;
    protected MySingleton() { }
    public synchronized static MySingleton getInstance() {
        if (mInstance == null) {
            mInstance = new MySingleton();
        }
        return mInstance;
    }

    public void callWebService(Context appContext, int intUserId) {
        Intent intent = new Intent();
        intent.setClass(appContext, MyIntentService.class);

        intent.putExtra("job", MyIntentService.JOB_DELETE);
        intent.putExtra("data_type", MyIntentService.ITEM_ALL);
        intent.putExtra("user_id", intUserId);

        appContext.startService(intent);
    }
}

在我的活动中,我调用:

MySingleton.getInstance().callWebService(getApplicationContext(), ((MyApplication)getApplicationContext()).getUserId());

我在这里做错了什么吗?由于这个 Singleton 只包含方法,所以最好不要让它成为 Singleton 并且所有方法都是静态的?

2) 光标。

我有几个使用 CursorAdapters 的 Activity。我将使用 AsyncQueryHandler 查询游标。当 AsyncQueryHandler 收到 Cursor 时,我在 CursorAdapter 上调用 changeCursor,传入这个新的 Cursor。在我的 CursorAdapter 的 changeCursor 处理程序中,我将 Cursor 传递给 super,然后将我的 Activity 的私有 Cursor 成员变量设置为此 Cursor 并在其上调用 startManagingCursor。

在此之后,我不会在 onPause() 或 onStop() 方法中执行任何操作来停止管理 Cursor 或将其清空或任何其他操作。这是错的吗?我想通过运行一个小测试可能会发生一些泄漏。如果我向下滚动 ListView 并旋转屏幕,Activity 被“销毁”,重新创建,查询一个新的 Cursor,ListView 自动滚动到屏幕旋转之前的位置。它显然仍然可以访问以前的光标,对吧?我不太确定 Android 是如何做到这一点的。

如果我调用 mCursor.close();在 onStop() 处理程序中,这会产生问题。当屏幕旋转并填充时,用户会失去滚动位置。这显然是不可取的。那我该怎么办?是否获取滚动位置,关闭Cursor,然后通过saveInstanceState 传递滚动位置?

3) 我如何单独运行该死的 DDMS?如果我在 Eclipse 打开的情况下打开它,它会抱怨 DDMS 实例已经在运行并关闭。如果我关闭 Eclipse,启动模拟器,然后启动独立的 DDMS,它什么也看不到。

4) 鉴于您对我的 Android 体验的了解(基于这篇文章或其他文章),我还应该注意什么?我已经阅读了 Romain Guy 的几篇文章,我已经摆脱了潜在的问题,但肯定还有更多我还没有意识到。

【问题讨论】:

  • 不是答案,而是查找内存问题的有用工具,尤其是与对象保留有关的问题:我建议使用 Eclipse MAT 查看应用程序的 HPROF 转储(community.developer.motorola.com/t5/… 是一个很好的解释,使用Motodev 工作室;您可以在没有它的情况下执行此操作,只需将 HPROF 转储并自己加载到 MAT 中即可)。

标签: android memory-management memory-leaks


【解决方案1】:

为什么要从 callWebService 方法启动服务?只需按照该方法进行工作即可。如果您真的认为它应该是一个服务,只需放弃单例并将您的活动绑定到该服务并在那里调用它的方法。

是的,这是错误的,您必须关闭光标。如果必须,您应该在 onStop 中关闭它并在 onStart 或 onResume 中打开它。你真的应该尽快关闭它。

【讨论】:

  • 从 Google I/O 2010 演示文稿中,我们被告知在服务中运行 Web 服务调用(具体来说,是服务中的线程,但 IntentService 会处理它)。可以从应用程序中的 10 个不同位置调用该 callWebService 方法;所以它已经被模块化到一个地方,而不是在 10 个不同的地方有相同的 6 行代码。
  • 没有内在的理由将服务用于网络调用,只要它们在不同的线程中。向我们展示您服务中的一些代码。但我敢打赌,你正在泄漏游标。你的应用是做什么的?为什么它会在记忆中停留这么久?您是否要离开 24/7 全天候运行的服务?你不应该那样做。
  • IntentServices 会在您的代码执行完毕后自动关闭。当我的 IntentService 处理 Intent 时,它会获取与 Intent 一起传递的数据(例如:用户 ID),调用 Web 服务,解析返回的 JSON 并将数据写入应用程序的数据库。在某些情况下,我会查询游标,但在 IntentService 中,当我使用完游标时,我总是关闭()并将游标归零。 IntentService 然后通过广播通知可能需要重新查询列表的任何 Activity 新数据可用。 Activity 会听到广播并查询新的 Cursor。
  • 如果您在 Activity 的 onStop() 中关闭光标,您是如何处理滚动位置问题的?如果我是您的用户,我将列表向下滚动一半,单击某个按钮启动另一个 Activity,单击返回按钮,我现在再次滚动到列表顶部,我会很生气。
  • 我从来没有使用过CursorAdapter,但是你不能在类成员中保留滚动位置,然后在重新打开光标时滚动到它吗?
猜你喜欢
  • 2011-03-28
  • 2011-02-18
  • 1970-01-01
  • 2011-06-10
  • 2020-10-11
  • 2011-01-14
  • 1970-01-01
相关资源
最近更新 更多