Android Activity <App Name> 泄露了原本绑定在这里的 ServiceConnection <ServiceConnection Name>@438030a8
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1992676/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Activity <App Name> has leaked ServiceConnection <ServiceConnection Name>@438030a8 that was originally bound here
提问by catdotgif
I'm working on my first Android app. I've got three activities in my app, and the user switches back and forth pretty frequently. I've also got a remote service, which handles a telnet connection. The apps need to bind to this service in order to send/receive telnet messages.
我正在开发我的第一个 Android 应用程序。我的应用程序中有三个活动,用户来回切换非常频繁。我还有一个远程服务,它处理 telnet 连接。应用程序需要绑定到此服务才能发送/接收 telnet 消息。
EditThank you BDLS for your informative answer. I have re-written my code in light of your clarification on the difference between using bindService()
as a stand-alone function or after startService()
, and I now only get the leak error message intermittently when using the back button to cycle between activities.
编辑感谢 BDLS 提供的信息丰富的答案。根据您对bindService()
用作独立函数或之后的区别的澄清,我重新编写了我的代码,startService()
现在我只在使用后退按钮在活动之间循环时间歇性地收到泄漏错误消息。
My connection activity has the following onCreate()
and onDestroy()
:
我的连接活动具有以下onCreate()
和onDestroy()
:
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*
* Initialize the ServiceConnection. Note that this is the only place startService() is run.
* It is also the only time bindService is run without dependency on connectStatus.
*/
conn = new TelnetServiceConnection();
//start the service which handles telnet
Intent i = new Intent();
i.setClassName( "com.wingedvictorydesign.LightfactoryRemote", "com.wingedvictorydesign.LightfactoryRemote.TelnetService" );
startService(i);
//bind to the service
bindService(i, conn, 0);
setContentView(R.layout.connect);
setupConnectUI();
}//end OnCreate()
@Override
protected void onDestroy() {
super.onDestroy();
//unbind the service and null it out
if (conn != null) {
unbindService(conn);
conn = null;
}
if(connectStatus == 0) {
//stop the service
Intent i = new Intent();
i.setClassName( "com.wingedvictorydesign.LightfactoryRemote", "com.wingedvictorydesign.LightfactoryRemote.TelnetService" );
stopService(i);
Log.d("LightfactoryRemote", "Connect onDestroy() attempted to stop service");
}
Log.d("LightfactoryRemote", "Connect onDestroy()");
}//end onDestroy()
So the service is started when the activity is started, and stopped when the activity is destroyed if no successful telnet connection was made (connectStatus == 0
). The other activities bind to the service only if a successful connection was made (connectStatus == 1
, saved to a shared preferences). Here is their onResume()
and onDestroy()
:
因此,服务在活动启动时启动,如果没有成功建立 telnet 连接(connectStatus == 0
),则在活动被破坏时停止。其他活动仅在建立成功连接时绑定到服务(connectStatus == 1
保存到共享首选项)。这是他们的onResume()
和onDestroy()
:
@Override
protected void onResume() {
super.onResume();
//retrieve the shared preferences file, and grab the connectionStatus out of it.
SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_WORLD_WRITEABLE);
connectStatus = settings.getInt("connectStatus", 0);
Log.d("LightfactoryRemote", "Focus onResume with " + connectStatus);
//if a telnet connection is active, start the service and bind to it
if (connectStatus == 1) {
conn = new TelnetServiceConnection();
Intent i = new Intent();
i.setClassName("com.wingedvictorydesign.LightfactoryRemote", "com.wingedvictorydesign.LightfactoryRemote.TelnetService");
bindService(i, conn, 0);
//TODO write restore texview code
}//end if
}//end onResume
@Override
protected void onDestroy() {
super.onDestroy();
//unbind the service and null it out.
if (conn != null) {
Log.d("LightfactoryRemote", "Focus onDestroy() attempted to unbind service");
unbindService(conn);
conn = null;
}
Log.d("LightfactoryRemote", "Focus onDestroy()");
}//end onDestroy()
So the binding happens in onResume()
so that it will pick up the changed state from the connection activity, and in the onDestroy()
function it is unbound, if necessary.
所以绑定发生在onResume()
它会从连接活动中获取更改的状态,并且在onDestroy()
函数中它是未绑定的,如果需要的话。
End Edit
结束编辑
But I still get the memory leak error message "Activity has leaked ServiceConnection @438030a8 that was originally bound here" intermittently when switching activities. What am I doing wrong?
但我仍然在切换活动时间歇性地收到内存泄漏错误消息“活动已泄漏最初绑定在这里的 ServiceConnection @438030a8”。我究竟做错了什么?
Thanks in advance for any tips or pointers!!!
在此先感谢您的任何提示或指示!!!
Full error message follows (from the revised code):
完整的错误信息如下(来自修改后的代码):
01-02 22:04:26.642: DEBUG/LightfactoryRemote(2024): Focus onStop()
01-02 22:04:26.642: DEBUG/LightfactoryRemote(2024): Focus onDestroy() attempted to unbind service
01-02 22:04:26.642: DEBUG/LightfactoryRemote(2024): Focus onDestroy()
01-02 22:04:26.672: ERROR/ActivityThread(2024): Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@439e51e8 that was originally bound here
01-02 22:04:26.672: ERROR/ActivityThread(2024): android.app.ServiceConnectionLeaked: Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@439e51e8 that was originally bound here
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.app.ActivityThread$PackageInfo$ServiceDispatcher.<init>(ActivityThread.java:927)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.app.ActivityThread$PackageInfo.getServiceDispatcher(ActivityThread.java:822)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.app.ApplicationContext.bindService(ApplicationContext.java:842)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.content.ContextWrapper.bindService(ContextWrapper.java:319)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote.onResume(LightfactoryRemote.java:102)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1225)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.app.Activity.performResume(Activity.java:3559)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2838)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2866)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2420)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.app.ActivityThread.access00(ActivityThread.java:116)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.os.Handler.dispatchMessage(Handler.java:99)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.os.Looper.loop(Looper.java:123)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at android.app.ActivityThread.main(ActivityThread.java:4203)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at java.lang.reflect.Method.invokeNative(Native Method)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at java.lang.reflect.Method.invoke(Method.java:521)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)
01-02 22:04:26.672: ERROR/ActivityThread(2024): at dalvik.system.NativeStart.main(Native Method)
01-02 22:04:26.692: WARN/ActivityManager(558): Unbind failed: could not find connection for android.os.BinderProxy@43c509a8
Edit the 2nd
Thanks once again bdls for your suggestions. I did as you suggested and added an onUnBind()
override to the service. onUnBind()
is actually only triggered when all clients disconnect from the service, but when I hit the home button, it executed, then the error message popped up! This makes no sense to me, as all the clients have been unbound from the service, so how could the one destroyed leak a serviceConnection? Check it out:
编辑第二
次再次感谢 bdls 的建议。我按照你的建议做了,并为onUnBind()
服务添加了一个覆盖。 onUnBind()
实际上只有在所有客户端与服务断开连接时才会触发,但是当我点击主页按钮时,它执行,然后弹出错误消息!这对我来说毫无意义,因为所有客户端都已与服务解除绑定,那么销毁的客户端怎么会泄漏 serviceConnection 呢?一探究竟:
01-03 19:38:30.837: DEBUG/LightfactoryRemote(1118): Focus onPause()1
01-03 19:38:31.577: WARN/IInputConnectionWrapper(1118): showStatusIcon on inactive InputConnection
01-03 19:38:31.587: DEBUG/LightfactoryRemote(1118): Focus onStop()
01-03 19:38:31.600: DEBUG/LightfactoryRemote(1118): Focus onDestroy() attempted to unbind service
01-03 19:38:31.607: DEBUG/LightfactoryRemote(1118): Focus onDestroy()
01-03 19:38:31.677: DEBUG/LightfactoryRemote(1125): TelnetService onUnBind()
01-03 19:38:31.727: ERROR/ActivityThread(1118): Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@435baeb0 that was originally bound here
01-03 19:38:31.727: ERROR/ActivityThread(1118): android.app.ServiceConnectionLeaked: Activity com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote has leaked ServiceConnection com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote$TelnetServiceConnection@435baeb0 that was originally bound here
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.app.ActivityThread$PackageInfo$ServiceDispatcher.<init>(ActivityThread.java:886)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.app.ActivityThread$PackageInfo.getServiceDispatcher(ActivityThread.java:781)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.app.ApplicationContext.bindService(ApplicationContext.java:820)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.content.ContextWrapper.bindService(ContextWrapper.java:307)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote.onResume(LightfactoryRemote.java:102)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1225)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.app.Activity.performResume(Activity.java:3530)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2619)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2647)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2287)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.app.ActivityThread.access00(ActivityThread.java:112)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1692)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.os.Handler.dispatchMessage(Handler.java:99)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.os.Looper.loop(Looper.java:123)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at android.app.ActivityThread.main(ActivityThread.java:3948)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at java.lang.reflect.Method.invokeNative(Native Method)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at java.lang.reflect.Method.invoke(Method.java:521)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:782)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:540)
01-03 19:38:31.727: ERROR/ActivityThread(1118): at dalvik.system.NativeStart.main(Native Method)
01-03 19:38:31.777: WARN/ActivityManager(564): Unbind failed: could not find connection for android.os.BinderProxy@4370f8a8
I thought it might be something like you said, where the binding to the service is not complete when unbindService()
is called, however I tried calling a method on the service as I backed through each activity to verify that the binding is complete, and they all went through fine.
我认为这可能像您所说的那样,在unbindService()
调用时与服务的绑定未完成,但是当我支持每个活动以验证绑定是否完成时,我尝试在服务上调用一个方法,并且它们都进行了通过罚款。
In general, this behavior doesn't seem related to how long I stay in each activity. Once the first activity leaks its serviceConnection, however, they all do as I back through them after that.
一般来说,这种行为似乎与我在每项活动中停留的时间无关。然而,一旦第一个活动泄漏了它的 serviceConnection,它们就会像我之后通过它们一样做。
One other thing, if I turn on "Immediately destroy activities" in Dev Tools, it prevents this error.
另一件事,如果我在开发工具中打开“立即销毁活动”,它可以防止此错误。
Any ideas?
有任何想法吗?
回答by bdls
You haven't provided any of your code from LightFactoryRemote
, so this is only a presumption, but it looks like the kind of problem you'd be seeing if you were using the bindService
method on it's own.
您还没有提供来自 的任何代码LightFactoryRemote
,因此这只是一个假设,但如果您单独使用该bindService
方法,它看起来像是您会看到的那种问题。
To ensure a service is kept running, even after the activity that started it has had its onDestroy
method called, you should first use startService
.
为了确保服务保持运行,即使在启动它的活动已经onDestroy
调用了其方法之后,您也应该首先使用startService
.
The android docs for startServicestate:
startService状态的 android 文档:
Using startService() overrides the default service lifetime that is managed by bindService(Intent, ServiceConnection, int): it requires the service to remain running until stopService(Intent) is called, regardless of whether any clients are connected to it.
使用 startService() 覆盖由 bindService(Intent, ServiceConnection, int) 管理的默认服务生命周期:它要求服务保持运行直到调用 stopService(Intent),无论是否有任何客户端连接到它。
Whereas for bindService:
而对于bindService:
The service will be considered required by the system only for as long as the calling context exists. For example, if this Context is an Activity that is stopped, the service will not be required to continue running until the Activity is resumed.
只要调用上下文存在,系统就会认为该服务是必需的。例如,如果此 Context 是一个已停止的 Activity,则该服务将不需要继续运行,直到 Activity 恢复。
So what's happened is the activity that bound (and therefore started) the service, has been stopped and thus the system thinks the service is no longer required and causes that error (and then probably stops the service).
所以发生的事情是绑定(并因此启动)服务的活动已停止,因此系统认为不再需要该服务并导致该错误(然后可能停止该服务)。
Example
例子
In this example the service should be kept running regardless of whether the calling activity is running.
在此示例中,无论调用 Activity 是否正在运行,服务都应保持运行。
ComponentName myService = startService(new Intent(this, myClass.class));
bindService(new Intent(this, myClass.class), myServiceConn, BIND_AUTO_CREATE);
The first line starts the service, and the second binds it to the activity.
第一行启动服务,第二行将其绑定到活动。
回答by Mostafa Rostami
You can use:
您可以使用:
@Override
public void onDestroy() {
super.onDestroy();
if (mServiceConn != null) {
unbindService(mServiceConn);
}
}
回答by Nick
You bind in onResume
but unbind in onDestroy
. You should do the unbinding in onPause
instead, so that there are always matching pairs of bind/unbind calls. Your intermittent errors will be where your activity is paused but not destroyed, and then resumed again.
您绑定onResume
但解除绑定onDestroy
。您应该onPause
改为执行解除绑定,以便始终存在匹配的绑定/解除绑定调用对。您的间歇性错误将是您的活动暂停但未销毁,然后再次恢复的地方。
回答by pierrotlefou
You should only need to unbind the service in onDestroy()
. Then, The warning will go.
您应该只需要在onDestroy()
. 然后,警告将消失。
See here.
见这里。
As the Activity doc tries to explain, there are three main bind/unbind groupings you will use: onCreate() and onDestroy(), onStart() and onStop(), and onResume() and onPause().
正如活动文档试图解释的那样,您将使用三个主要的绑定/取消绑定分组:onCreate() 和 onDestroy()、onStart() 和 onStop(),以及 onResume() 和 onPause()。
回答by bdls
You mention the user switching between Activities pretty quickly. Could it be that you're calling unbindService
before the service connection has been established? This may have the effect of failing to unbind, then leaking the binding.
您提到用户在活动之间切换非常快。可能是您在unbindService
服务连接建立之前调用?这可能会导致无法解除绑定,然后泄漏绑定。
Not entirely sure how you could handle this... Perhaps when onServiceConnected
is called you could call unbindService
if onDestroy
has already been called. Not sure if that'll work though.
不完全确定你如何处理这个......也许什么时候onServiceConnected
被调用,unbindService
如果onDestroy
已经被调用,你可以调用。不知道这是否会奏效。
If you haven't already, you could add an onUnbind method to your service. That way you can see exactly when your classes unbind from it, and it might help with debugging.
如果您还没有,您可以向您的服务添加一个 onUnbind 方法。这样你就可以准确地看到你的类何时解除绑定,这可能有助于调试。
@Override
public boolean onUnbind(Intent intent) {
Log.d(this.getClass().getName(), "UNBIND");
return true;
}
回答by Ashwini Shahapurkar
Try using unbindService() in OnUserLeaveHint(). It prevents the the ServiceConnection leaked scenario and other exceptions.
I used it in my code and works fine.
尝试在 OnUserLeaveHint() 中使用 unbindService()。它防止了 ServiceConnection 泄漏的情况和其他异常。
我在我的代码中使用了它并且工作正常。
回答by D4rWiNS
You can just controll it with a boolean, so you only call unbind if bind has been made
你可以用一个布尔值来控制它,所以你只有在绑定时才调用 unbind
public void doBindService()
{
if (!mIsBound)
{
bindService(new Intent(this, DMusic.class), Scon, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
}
public void doUnbindService()
{
if (mIsBound)
{
unbindService(Scon);
mIsBound = false;
}
}
If you only want to unbind it if it has been connected
如果你只是想解除绑定,如果它已经连接了
public ServiceConnection Scon = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder binder)
{
mServ = ((DMusic.ServiceBinder) binder).getService();
mIsBound = true;
}
public void onServiceDisconnected(ComponentName name)
{
mServ = null;
}
};
回答by Punit
Every service that is bound in activity must be unbind on app close.
活动中绑定的每个服务都必须在应用程序关闭时解除绑定。
So try using
所以尝试使用
onPause(){
unbindService(YOUR_SERVICE);
super.onPause();
}