2

我有一个应用程序使用该接口与Service远程进程中的 a 进行通信。Messenger以下是事物设置的基本架构:

  • 应用程序生成几个需要访问服务的“操作”对象。
  • 每个“操作”都包含一个Handler包裹在一个Messenger用于接收从响应数据返回的Service
  • 当操作执行时,它将其包装Messenger成一个Intent并调用startService()以将消息传递给远程服务
  • 远程服务根据 的参数执行一些工作Intent,然后通过向该操作发送 aMessage来返回响应Messenger

这是操作中存在的基本代码:

public class SessionOperation {

    /* ... */

    public void runOperation() {
        Intent serviceIntent = new Intent(SERVICE_ACTION);
        /* Add some other extras specific to each operation */
        serviceIntent.putExtra(Intent.EXTRA_EMAIL, replyMessenger);

        context.startService(serviceIntent);
    }

    private Handler mAckHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //Process the service's response
        }
    };
    protected Messenger replyMessenger = new Messenger(mAckHandler);
}

以及服务结构的片段(基本上是IntentService在队列为空时不会关闭):

public class WorkService extends Service {
    private ServiceHandler mServiceHandler;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //If intent has a message, queue it up
        Message msg = mServiceHandler.obtainMessage();
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);

        return START_STICKY;
    }

    private void onHandleIntent(Intent intent) {
        Messenger replyTarget = intent.getParcelableExtra(Intent.EXTRA_EMAIL);

        /* Do some work */

        Message delivery = Message.obtain(...);
        replyTarget.send(delivery);
    }
}

这一切都非常好。我可以将来自多个不同应用程序的大量操作发送到同一个服务,它们都会处理并将响应发送到正确的位置。然而...

我注意到,如果应用程序运行的时间足够长并且有足够的活动,它会以OutOfMemoryError. 在查看 MAT 中的 HPROF 数据时,我注意到所有这些操作都保留在内存中,并且由于Messenger. 显然,该Messenger实例正在创建与 Binder 的长期本机连接,该连接算作 GC Root,它将每个“操作”对象无限期地保存在内存中。

MAT 跟踪示例

有谁知道是否有办法Messenger在“操作”结束时清除或禁用它,以免造成这种内存泄漏?是否有另一种方法可以以Service相同的方式实现 IPC,以便多个不同的对象可以发出请求并异步获取结果?

提前致谢!

4

2 回答 2

12

多亏了来自 Android 团队的 Dianne Hackborn 的一些非常有用的见解,这个问题是因为远程服务进程还没有垃圾收集它的 Messenger 实例,实际上,直到那个时候,它一直将应用程序进程中的实例作为人质。

这是她回复的正文:

确实,跨进程发送信使需要在其上保存 GREF,以便其他进程与之通信。除非出现错误(已经发生,但我不确定是否在任何已发布的平台版本中),当其他进程本身不再持有对此的引用时,GREF 将被释放。当我们在 Dalvik 中谈论事物时,“不再持有引用”一般意味着“对方已经对 Java 代理对象进行了垃圾回收”。

这意味着当你将一个 Messenger(或任何 IBinder 对象)扔给另一个进程时,你自己进程中的 Dalvik VM 不能再管理该对象本身的内存,并且依赖于所有远程对象释放它,直到它可以在当地发布。这将包括 IBinder 引用的所有对象。

处理这个问题的一个常见模式是在你的 IBinder/Messenger 中使用 Wea​​kReference,它保存对它将访问的其余对象的引用。这允许您的本地垃圾收集器清理所有其他对象(可能很重,包含位图等大东西),即使远程进程仍然在您的 IBinder 上有引用。当然,如果您这样做,则需要有其他东西持有对这些其他对象的引用,直到不再需要它们,否则垃圾收集器可以在不再需要它们之前清理它们。

我建议的另一件事是不要为您所做的每个 IPC 实例化 Messenger 对象的设计。创建一个传递给每个 IPC 调用的 Messenger。否则,您可能会生成许多由于其他进程继续持有引用而被保留的远程对象,因为另一方没有积极地进行垃圾收集,因为由于这些调用而创建的所有对象都很小。

更多信息: https ://groups.google.com/d/msg/android-developers/aK2o1W2xrMU/Z0-QujnU3wUJ

于 2012-08-30T22:32:09.093 回答
0

我不确定这是否是最好的方法,因为即使Activity在后台你也会得到messagefrom Service

我认为您应该在服务连接后立即绑定到服务service并注册messenger服务。然后messenger在断开连接时取消注册。

检查AOSP 中的ExportVcardActivity。它正在遵循这些思路。

于 2012-08-30T20:48:36.887 回答