活动<App Name>泄漏了ServiceConnection <ServiceConnection Name>@438030a8,它最初是在这里绑定的。

142

我正在开发我的第一个Android应用程序。我的应用程序中有三个活动,并且用户经常在这些活动之间切换。我还有一个远程服务,处理telnet连接。应用程序需要绑定到此服务才能发送/接收telnet消息。

编辑 感谢BDLS提供的详细答案。在您对bindService()作为独立函数或在startService()之后使用的区别进行了澄清后,我重新编写了代码,现在仅在使用返回按钮循环浏览活动时偶尔出现泄漏错误消息。

我的连接活动具有以下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()

因此,服务在活动启动时启动,在未成功建立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()

所以绑定发生在 onResume() 中,这样它就可以从连接活动中获取更改后的状态,在 onDestroy() 函数中解除绑定(如果必要)。

编辑结束

但是我仍然会不时地收到“Activity has leaked ServiceConnection @438030a8 that was originally bound here”内存泄漏错误消息,当切换活动时。

我做错了什么?

以下是完整的错误消息(来自修改后的代码):

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.access$2100(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

编辑第二次
再次感谢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.access$1800(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

我认为可能像你所说的那样,当调用unbindService()时,与服务的绑定尚未完成,但是我尝试在每个活动返回时调用服务上的方法来验证绑定是否完成,它们都通过了。

总的来说,这种行为似乎与我在每个活动中停留的时间无关。然而,一旦第一个活动泄漏其serviceConnection,之后我回退到它们中的所有活动都会出现同样的问题。

还有一件事,如果我在Dev Tools中打开“立即销毁活动”,它就可以防止这种错误。

有什么想法吗?


你能添加LightfactoryRemote.onCreate()的代码吗?(com.wingedvictorydesign.LightfactoryRemote.LightfactoryRemote.onCreate(LightfactoryRemote.java:97)) - tbruyelle
10个回答

58
你没有提供来自LightFactoryRemote的任何代码,因此这只是一种假设,但如果你仅使用bindService方法,则看起来会遇到这种问题。

为了确保服务在启动它的活动调用其onDestroy方法后仍然运行,您应该首先使用startService

Android文档对于startService说明:

使用startService()覆盖了由bindService(Intent, ServiceConnection, int)管理的默认服务生命周期:它要求服务保持运行状态,直到调用stopService(Intent),而不管是否有客户端连接到该服务。

而对于bindService

只要调用上下文存在,系统就认为服务是必需的。例如,如果此上下文是已停止的Activity,则不需要继续运行服务,直到Activity恢复。


所以发生的情况是绑定(启动)服务的活动已停止,因此系统认为服务不再需要并导致该错误(然后可能停止服务)。


示例

在此示例中,无论调用的活动是否正在运行,该服务都应保持运行状态。

ComponentName myService = startService(new Intent(this, myClass.class));
bindService(new Intent(this, myClass.class), myServiceConn, BIND_AUTO_CREATE);

第一行启动了服务,第二行将其绑定到活动。


我使用START_STICKY启动服务,并且它也通过启动意图启动。如果我调用StartService,则会创建服务的另一个实例,而我不希望同时运行2个服务。在这种情况下,我该如何修复错误? - opc0de
20
@opc0de,当你调用startService()两次时,你并不会创建另一个服务。服务天生就是逻辑上的单例。无论你启动服务多少次,只有一个服务在运行。 - mahkie
2
如何在音乐播放器的后台中保持服务的持续运行? - Anand Savjani

50

你可以使用:

@Override
public void onDestroy() {
    super.onDestroy();

    if (mServiceConn != null) {
        unbindService(mServiceConn);
    }
}

优秀。就是这样。 - Seltsam

32

你在onResume中绑定,但在onDestroy中解绑。相反,你应该在onPause中执行解绑操作,这样就会始终有匹配的绑定/解绑调用对。当你的活动暂停但没有销毁,然后再次恢复时,你就会遇到间歇性错误。


13

你只需要在onDestroy()方法中解除服务的绑定,这样警告就会消失。

参见这里

正如Activity文档所解释的那样,你将使用三种主要的绑定/解绑分组:onCreate()和onDestroy(),onStart()和onStop(),以及onResume()和onPause()。


7
如你所知,操作系统几乎从不调用onDestroy方法。 - martyglaubitz
这里的链接指向被禁止的内容 --> 禁止内容警告。有人可以访问吗?对我来说,“继续前往群组”按钮只会刷新页面并显示相同的警告。 - Blaze Gawlik

11

你提到用户很快地在Activity之间切换。是否可能是您在服务连接建立之前调用了 unbindService?这可能会导致失败无法解除绑定,从而造成泄漏绑定的情况。

不太确定如何处理这个问题...也许当 onServiceConnected 被调用时,如果 onDestroy 已经被调用,你可以调用unbindService。不确定这是否有效。


如果还没有的话,你可以在你的服务中添加一个 onUnbind 方法。这样你就可以精确地看到你的类何时与它解除绑定,这可能有助于调试。

@Override
public boolean onUnbind(Intent intent) {
    Log.d(this.getClass().getName(), "UNBIND");
    return true;
}

2

尝试在 OnUserLeaveHint() 中使用 unbindService()。它可以防止 ServiceConnection 泄漏的情况和其他异常。
我已经在我的代码中使用了它,并且工作得很好。


2

当您要绑定一个已绑定的服务时,会出现此错误。因此,解决方法应该是:

  1. in service connection add serviceBound as below:

    private final ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // your work here.
    
        serviceBound = true;
    
    }
    
    @Override
    public void onServiceDisconnected(ComponentName name) {
    
        serviceBound = false;
    }
    

    };

  2. unbind service onDestroy

        if (serviceBound) {
            unbindService(serviceConnection);
        }
    

1

你可以使用布尔值来控制它,这样只有在绑定后才调用解绑方法。

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;
    }
}

如果只想在已连接的情况下解除绑定。
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;
    }
};

0
每个在活动中绑定的服务都必须在应用关闭时解除绑定。
因此,请尝试使用


 onPause(){
   unbindService(YOUR_SERVICE);
   super.onPause();
 }

0

我最近一直在阅读有关Android服务的文章,并有机会深入了解它。我遇到了一个服务泄漏问题,因为我的非绑定服务启动了一个绑定服务,但是在这种情况下,我的非绑定服务被一个活动替换。

因此,当我使用 stopSelf() 停止我的非绑定服务时,发生了泄漏。原因是我停止了父级服务而没有解除绑定的服务。现在,绑定的服务正在运行,它不知道归属于谁。

简单明了的解决方法是,在你的父级Activity/Service的 onDestroy() 函数中调用 unbindService(YOUR_SERVICE);。这样,生命周期将确保在父级Activity/Service关闭之前停止或清理你的绑定服务。

这个问题还有另一种变化。有时在绑定服务中,您希望某些函数仅在服务被绑定时才起作用,因此我们会在onServiceConnected中放置一个绑定标志:

public void onServiceConnected(ComponentName name, IBinder service) {
            bounded = true;
            // code here
        }

这个代码在这里运行得很好,但当我们把onServiceDisconnected函数作为unbindService函数调用的回调函数时,问题就出现了。根据文档,这个函数只有在服务被杀死或崩溃时才会被调用。而且你永远不会同一个线程中收到这个回调。因此,我们最终会做出类似以下的操作:

public void onServiceDisconnected(ComponentName name) {
            bounded = false;
        }

这在代码中创建了主要的错误,因为我们的绑定标志从未重置为false,当该服务再次连接时,大多数情况下它是true。因此,为了避免这种情况,您应该在调用unbindService时立即将bound设置为false。

这在Erik的博客中有更详细的介绍。

希望来到这里的人已经满足了他的好奇心。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接