【问题标题】:Custom adapter using bound service - when to unbind?使用绑定服务的自定义适配器 - 何时解除绑定?
【发布时间】:2012-12-14 22:03:28
【问题描述】:

设置:将数据输入ListView 的自定义适配器。数据基于外部服务计算的一些数字。我正在使用 AIDL 来绑定服务并获取我需要的数据。

问题:我如何知道何时解除绑定服务?连接对适配器本身是私有的,检测整个应用程序正在关闭的唯一方法是覆盖unregisterDataSetObserver。或者至少我没有找到另一种方法,并且这个方法与使用内容提供者+内容观察者的相同适配器配合得很好。但不适用于 AIDL - 我收到 ServiceConnectionLeaked 错误。

我知道我可以向我的适配器添加一个“unbindFromService”方法并从我的活动的 onDestroy() 中调用它,但这对我来说不够优雅。如果可能的话,我更喜欢一些“诚实”的触发器。

【问题讨论】:

  • 为什么服务连接是私有的并且在适配器中使用?您可以在活动级别处理它并仅将数据传递给适配器吗?然后你可以在 Activity 的 onCreate/onDestroy 上使用 bind/unbind。
  • 数据不仅来自服务,还需要请求。适配器知道它需要哪些数据,其他人都不知道。
  • 这意味着您的适配器应该从其他一些组件(例如,Activity)请求数据,然后应该获取数据。只是对Single Responsibility Principle 的一些重构和使用。适配器通常只负责渲染数据和一些事件处理,比如 onItemClick 等。您可以在适配器级别处理连接(就像您所描述的那样),但它不会很优雅。希望它会有所帮助。
  • 适配器是唯一需要该数据的组件,也是唯一知道如何处理它的组件。我的应用程序的早期版本通过活动使用“集中式”服务访问,但现在只有适配器正在使用服务。我不喜欢通过将连接对象移动到活动来添加另一层间接的想法。
  • 那我没有什么要补充的了。我在Bound services doc 中找到的最后一件事,请参阅附加说明部分:You should usually pair the binding and unbinding during matching bring-up and tear-down moments of the client's lifecycle

标签: android android-adapter aidl


【解决方案1】:

在您的 CustomAdapter 中实现 ActivityLifecycleCallbacks,然后从您的活动调用中实现

mAdapter.setActivity(this);

适配器类

public class CustomAdapter  extends ArrayAdapter<String> implements ActivityLifecycleCallbacks{

    Activity mActivity;

    public void setActivity(Activity activity) {
           /* or you could remove setactivity and do below operation in Constructor */
        mActivity = activity;
        mActivity.getApplication().registerActivityLifecycleCallbacks(this);
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        /*unregister so that you do not get callbacks after activity is destroyed*/
        if(activity instanceof MainActivity)   
              mActivity.getApplication().unregisterActivityLifecycleCallbacks(this);

            /*unregister your activity or in any other callbacks*/
    }

    /* skipped other dummy functions*/

}

【讨论】:

    【解决方案2】:

    Connection is private to adapter 是错误的。 适配器是无上下文的,因此服务不应该是适配器私有的

    如果没有上下文,您的适配器将永远无法绑定到服务。本质上,您有一个函数为适配器提供一个上下文以绑定到服务。因此,绑定的连接不是您的适配器私有的:它是传递的上下文允许创建它。

    真正的问题是 AFAIK 没有办法听上下文。意思是,假设您在适配器中保存了上下文实例:没有办法让适配器侦听此上下文并以某种方式知道 Activity 已完成。这导致您接受不够优雅的解决方案:您的活动应该通知正在使用其上下文的适配器!否则,您最终会得到一个更加复杂的解决方案。


    请注意,cmets 中的整个辩论都涉及您的“服务是适配器私有的”事情。实际上,执行此操作的优雅方法是拥有一个提供连接的 ServiceProvider 接口,这将由您的活动实现。您的适配器应该使用传递的 ServiceProvider 进行初始化。一些函数是getConnectionisRunning。这样,您的 Activity 将在 Adapter 使用它时处理连接。 (同步

    【讨论】:

    • 适配器实际上不是无上下文的:developer.android.com/reference/android/widget/…
    • ServiceProvider 接口是“又一个间接层”,我倾向于将其视为不必要的混乱。
    • @aragaer ArrayAdapter 是一个实现。在构造函数中传递 Context 只是一种选择。它用于膨胀/创建视图。
    • LikeWise,您可能选择在构造函数中使用上下文来初始化服务连接。
    • 好吧,我的自定义适配器扩展了 ArrayAdapter。我会根据从服务接收到的数据来膨胀/创建视图。
    【解决方案3】:

    我会使用 Service 将新数据插入 SQLite 表或刷新 ContentProvider,然后使用 SimpleCursorAdapter(向后兼容的 android.support.v4.widget.SimpleCursorAdapter)和 LoaderManager、SQLiteCursorLoader/CursorLoader、Loader模式以在 List 或 ListFragment 上显示数据。

    Android SDK 没有提供 SQLiteCursorLoader,但 Mark Murphy 在 GitHub 上有一个,因此如果您想将新数据持久保存在本地 SQLite DB 中,可以查看它。

    由于服务可以独立于 SQLite 中的基础数据和列表的适配器,您可以在完成插入新数据或刷新提供程序时取消绑定它,以便您可以继续使用推荐的模式来取消绑定服务(请参阅此问题:Android: How to safely unbind a service) 看看http://developer.android.com/reference/android/app/Service.html#ProcessLifecycle

    我真的希望这能给你一个提示。祝你好运!

    【讨论】:

    • 实际上并没有那么多数据,正如我之前提到的,我确实尝试使用ContentProviderMatrixCursor,效果很好,但I have been told 这不是我应该在这里使用的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 2021-12-03
    • 2018-01-15
    • 2021-12-25
    • 1970-01-01
    相关资源
    最近更新 更多