6

我的应用程序使用另一个(我的)应用程序提供的服务。我正在使用一个绑定服务Messenger访问它并与之通信(因为它是一个不同的应用程序,它也是一个远程服务)。

当我bindService以适当的意图调用并且调用返回时(正如预期的那样,当提供服务的 APK 不存在时),我从文档和示例代码中false假设我不需要. 但是,在我的 Galaxy Nexus (Jelly Bean) 设备上执行此操作时,我在完成.unbindServiceConnectionServiceConnectionLeakedActivity

我通过这样做挽救了这一点

if (!ctxt.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) {
    try { 
        ctxt.unbindService(serviceConnection);
    } catch (Throwable t) {}
    // Clean up 
    return;
}
// Store serviceConnection for future use

我很好奇:我是否错过了文档中的某些内容,它应该以这种方式工作吗?我添加了try ... catch以确保即使这种行为在其他设备或 Android 版本上确实不同,我的应用程序也不会(负面)受到它的影响。

4

1 回答 1

8

一般来说,一个ServiceConnection总是由框架分配和注册,不管bindService()调用返回true还是false。请参阅android.app.ContextImpl中的 bindService() 实现:

public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) {
    IServiceConnection sd;
    if (conn == null) {
        throw new IllegalArgumentException("connection is null");
    }
    if (mPackageInfo != null) {
        // A new ServiceDispatcher will be created and registered along with 
        // ServiceConnection in LoadedApk.mService for your application context.
        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                mMainThread.getHandler(), flags);
    } else {
        throw new RuntimeException("Not supported in system context");
    }
    try {
        ... ...
        return res != 0;
    } catch (RemoteException e) {
        return false;
    }
}

按照官方开发指南的建议,在完成服务后,您应该始终取消绑定服务,这是一种良好的编程方式:

  • 要断开与服务的连接,请调用 unbindService()。

    当您的客户端被销毁时,它将与服务解除绑定,但是当您完成与服务的交互或活动暂停时,您应该始终解除绑定,以便服务可以在不使用时关闭。(下面将详细讨论绑定和解除绑定的适当时间。)

当框架开始执行最终清理(例如,当您的应用程序退出时)并发现有未注册的 ServiceConnection 时,会引发 ServiceConnectionLeaked,然后框架将尝试为您解除绑定。请参阅android.app.LoadedApk中的 removeContextRegistrations() 实现:

public void removeContextRegistrations(Context context,
        String who, String what) {
    final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();
    ... ...
    //Slog.i(TAG, "Receiver registrations: " + mReceivers);
    HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
        mServices.remove(context);
    if (smap != null) {
        Iterator<LoadedApk.ServiceDispatcher> it = smap.values().iterator();
        while (it.hasNext()) {
            LoadedApk.ServiceDispatcher sd = it.next();
            ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
                    what + " " + who + " has leaked ServiceConnection "
                    + sd.getServiceConnection() + " that was originally bound here");
            leak.setStackTrace(sd.getLocation().getStackTrace());
            Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
            if (reportRegistrationLeaks) {
                StrictMode.onServiceConnectionLeaked(leak);
            }
            try {
                ActivityManagerNative.getDefault().unbindService(
                        sd.getIServiceConnection());
            } catch (RemoteException e) {
                // system crashed, nothing we can do
            }
            sd.doForget();
        }
    }
    mUnboundServices.remove(context);
    //Slog.i(TAG, "Service registrations: " + mServices);
}
于 2013-01-13T21:46:41.783 回答