Android Messenger到远程服务导致内存泄漏
我有一个应用程序,它使用Android Messenger到远程服务导致内存泄漏,android,service,memory-leaks,Android,Service,Memory Leaks,我有一个应用程序,它使用Messenger界面在远程进程中与服务通信。以下是如何设置事物的基本架构: 应用程序生成几个需要访问服务的“操作”对象 每个“操作”都包含一个处理程序包装在信使中,用于从服务 当操作执行时,它将其信使包装成意图,并调用startService()将消息传递给远程服务 远程服务根据意图的参数执行一些工作,然后通过向信使发送消息返回响应以进行该操作 以下是操作中的基本代码: public class SessionOperation { /* ... */
Messenger
界面在远程进程中与服务
通信。以下是如何设置事物的基本架构:
- 应用程序生成几个需要访问服务的“操作”对象
- 每个“操作”都包含一个
包装在处理程序
中,用于从信使
服务
- 当操作执行时,它将其
包装成信使
,并调用意图
将消息传递给远程服务startService()
- 远程服务根据
的参数执行一些工作,然后通过向意图
信使发送
返回响应以进行该操作消息
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
,当队列为空时不会关闭):
这一切都非常有效。我可以将数吨操作从几个不同的应用程序发送到同一个服务,它们都会处理并将响应发送到正确的位置。然而
我注意到,如果应用程序运行的时间足够长,并且具有足够的活动,它将出现OutOfMemoryError
崩溃。在查看MAT中的HPROF数据时,我注意到所有这些操作都保留在内存中,并且由于Messenger
,它们被垃圾收集器劫持。显然,Messenger
实例正在创建一个到Binder的长期本机连接,该连接将计为GC根,这将无限期地将每个“操作”对象保留在内存中
是否有人知道在“操作”结束时,是否有办法清除或禁用Messenger,以免造成内存泄漏?是否有其他方法可以以相同的方式将IPC实现到服务
,以便多个不同的对象可以发出请求并异步获得结果
提前谢谢 我不确定这是否是最好的方式,因为即使
活动
在后台,您也会从服务
收到消息
我认为您应该绑定到服务
,并在服务连接后立即向该服务注册messenger
。然后在断开连接时注销messenger
签入AOSP。这是沿着这些思路进行的。多亏了安卓团队Dianne Hackborn的一些非常有用的见解,问题在于远程服务进程尚未对Messenger的实例进行垃圾收集,实际上,Messenger在此之前一直将应用程序进程中的实例作为人质
以下是她的答复:
的确,跨进程发送messenger需要在其上保留一个GREF,以便其他进程与其通信。除了bug(已经发生了,但我不确定是否在任何发布的平台版本中),GREF将在另一个进程本身不再引用它时发布。当我们谈论Dalvik中的事情时,“不再持有引用”通常意味着“另一方已经对Java代理对象进行了垃圾收集”
这意味着,当您将Messenger(或任何IBinder对象)抛出到另一个进程时,您自己进程中的Dalvik VM无法再管理该对象本身的内存,并且依赖于所有远程对象来释放它,直到它可以在本地释放为止。这将包括IBinder有任何引用的所有对象
处理这个问题的一种常见模式是在IBinder/Messenger中使用WeakReference,它保存对它将访问的其余对象的引用。这允许您的本地垃圾收集器清理所有其他对象(可能很重,包含位图之类的大对象),即使远程进程在您的IBinder上仍然有引用。当然,如果您这样做,则需要有其他东西在这些其他对象上保留引用,直到不再需要它们,否则垃圾收集器可以在不再需要它们之前清理它们
我建议的另一件事是不要为每个IPC实例化Messenger对象。创建一个Messenger,将其传递给每个IPC呼叫。否则,您可以生成许多远程对象,这些对象由于其他进程继续持有引用而被保留,因为另一方没有积极地进行垃圾收集,因为它由于这些调用而创建的所有对象都很小
更多信息:
实际上,这些都不是在活动的上下文中发生的,它们都是完全后台操作,需要绑定到服务直到完成,而不管用户做什么。无论如何,即使我使用绑定机制访问服务而不是意图,内存泄漏仍然存在。您不应该通过意图发送messenger,而是在服务连接后注册它们。并在工作完成后注销
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);
}
}