【问题标题】:How to manage a Bluetooth connection on Android through configuration changes?如何通过配置更改管理 Android 上的蓝牙连接?
【发布时间】:2013-03-06 03:21:15
【问题描述】:

问题:
如何管理(连接、读取、写入、断开)通过配置更改持续存在的蓝牙连接?

首选与设备版本 2.2“Froyo”兼容的解决方案,使用 ActionBarSherlock

问题...

  • BluetoothDeviceBluetoothSocket 都不能保留在 onSaveState 中。

  • 为了keep my app responsive,12 秒阻塞调用BluetoothSocket.connect() 必须在单独的线程上进行。启动Runnable 是线程长任务的推荐方法,但尝试在配置更改时恢复是一场噩梦。官方文档指出了三种不同的解决方案。

更新 1

  • 进一步研究将我带到asyncTaskLoader,但似乎这只能在完成时更新 UI,而不能提供更新。

  • BluetoothHDP 示例使用服务。服务似乎专注于进程间通信以及在活动生命周期之外持续存在的需求。我不需要这些功能中的任何一个。

更新 2

As pointed out by ReubenFragment.setRetainInstance(bool) 已替换已弃用的 getLastNonConfigurationInstance()。在这一点上,似乎最好的选择是使用setRetainInstance(true) 制作一个持久的非 UI 片段。

【问题讨论】:

    标签: java android multithreading sockets bluetooth


    【解决方案1】:

    我将通过使用处理您的蓝牙连接的Service 来解决这个问题,并按照this answer. 中的描述使该服务与您的活动对话

    然后,您可以使用 ASyncTask 简单地显示/隐藏对话框,并在轮换时取消/重新启动 ASyncTasks,正如您所提到的,这是由 Shelves Example 完成的。

    服务并没有那么可怕,它可能是解决您问题的最佳工具。

    【讨论】:

    • 这似乎是一个安全的解决方案。然而,这也让我觉得这相当于用手提钻敲钉子。服务似乎真的是为了与多个进程进行通信。是的,它们可以用于流程中的一些简单任务,但对于应该相对简单的事情来说,这似乎过于繁重了。
    • 我不同意。我希望我的应用程序能够处理到服务器的后台备份 - 一个简单的 20 行 IntentService 是我能找到的最简单的解决方案。
    【解决方案2】:

    您可以尝试使用singleton pattern,它可以处理所有事情并在必要时调用主要活动。

    所以这是获取 MySingleton 对象实例的一种静态方法,每次调用 getInstance 时都是同一个实例。您可以在其中“存储”所有蓝牙对象,它不会被破坏并且可以从每个活动中访问。

    public class MySingleton { 
        private static MySingleton instance; 
        public static MySingleton getInstance() { 
            if (null == instance) { 
                instance = new MySingleton(); 
            } 
        return instance; 
    } 
    
        private MySingleton() { 
        } 
    }
    

    【讨论】:

    • 据我了解,Application类是android为每个应用提供的单例,有自己的生命周期。在极少数情况下,它也可以在不销毁整个应用程序的情况下被销毁。你的意思是一个单独的单例类?您能否提供一个代码示例来说明您在这里所说的内容?
    • 我并没有真正尝试过蓝牙连接,但单例类不应该被破坏。抱歉格式化,我不是 SO 导出,但它只是一个普通的单例类,您将在其中拥有与蓝牙连接相关的所有内容。 ` public class Singleton { 私有静态 Singleton 实例; publicstatic Singleton getInstance() { if (null == instance) { instance = new Singleton(); } 返回实例; } 私有 Singleton() { } } `
    • 感谢您的澄清!你能用代码更新你的答案吗?您可以将每一行缩进 4 个空格,使其格式类似于代码。
    【解决方案3】:

    对此有很多解决方案。如果您不使用 Fragments,那么最简单的选择是覆盖 onRetainNonConfigurationInstance()。不要介意 API 已被弃用,这只是因为他们希望您使用 Fragments(公平地说,Fragment.setRetainInstance() 使整个问题变得理所当然地应该一直存在)。这个 API 很长一段时间都不会去任何地方。

    我要做的是重写 onRetainNonConfiguration() 以返回“this”,即对已死 Activity 实例的引用,然后从 onCreate() 复制您需要的对象引用,即:

    Object obj = getLastNonConfigurationInstance();
    if (obj != null) {
        MyActivity deadActivity = (MyActivity)obj;
        this.foo = deadActivity.foo;
        this.bar = deadActivity.bar;
        ...
    }
    

    或者您可以使用服务,但我个人不喜欢它们。毫无疑问,它们对于做跨进程的东西很有用,但除此之外,它们是寻找问题的解决方案。

    最后,作为一般顺序,您可以安全地将“全局”数据填充到您的应用程序上下文中。为此,您将 android.app.Application 子类化并在清单中使用 属性。在全局静态数据中保留对 Application 对象的引用也很有用,因此 AsyncTasks 和其他无上下文代码始终可以访问 Application 上下文,而无需无缘无故地传递 Context 参数。

    class MyApp extends Application {
    ...
    
        public static MyApp context;
        ...
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            context = this;
    
            ... set up global data here ...
        }
    ...
    }
    

    【讨论】:

    • 是否值得为此活动创建一个片段以便我可以使用setRetainInstance? “保留”片段是否能够在方向更改时旋转和调整自身大小?此外,this SO question 告诉我,Application 的子类不是存储全局变量的安全方法。事实上,它可能会导致几乎无法重现的错误。
    • 回复。片段,是的。早点而不是晚点采用片段的做事方式是值得的。重新子类化应用程序,有人很困惑。是的,当进程被杀死时,操作系统可以冻结任务状态,但那又如何呢?当进程/任务重新启动/恢复时,Application.onCreate() 仍然运行。
    • 那又怎样?如果进程重新启动并调用 Application.onCreate(),则您已经丢失了这些全局变量中的数据。唯一可行的方法是常量。
    【解决方案4】:

    永远不要将应用模型(逻辑或数据)放在可视化/UI 组件中。如您所见,它们来来去去,不断变化。

    存放非 UI 相关内容的地方,例如数据收集、实时连接、线程等:

    • 应用程序类。包含所有其他组件的生命周期。 几乎就像一个全球单身人士。您可以将其用于临时存储。

    示例:

    public class App extends Application {
    
      private static Beer sBeer;
    
      public static void brbHoldMyBeer(Beer b){
        sBeer = b;
      }
    
      public static Beer imBackWheresMyBeer(){
        return sBeer;
      }
    
    }
    

    另外,在 Application 类中拥有一个静态线程 Executor 服务,将有助于保留正在运行的任务。

    • 正在运行的后台服务。哪些活动、片段等可以绑定/取消绑定,并发布命令/请求。这是应用范围内运行的进程和数据的推荐位置。

    • 带有setRetainInstance(true) 的非可视片段。这里的非可视化意味着它是一个附加到 Activity 但不显示任何视图的虚拟片段。它只是用作 Activity 的可保留对象持有者。建议将这用于 Activity 范围的流程和数据。

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-27
    相关资源
    最近更新 更多