在非Activity状态下,BluetoothAdapter.getDefaultAdapter()抛出RuntimeException异常。

12

当我尝试在TimerTask(在Service内部创建)中获取默认的蓝牙适配器,而不是在Activity中时,我可以使用以下代码:

BluetoothAdapter.getDefaultAdapter();

我遇到了以下异常:

Exception while invoking java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

我的应用程序没有任何活动 - 那么有可能将此适配器从活动中移除吗?

请查看Android对此错误的响应:https://code.google.com/p/android/issues/detail?id=16587 - Jojo56400
5个回答

14
这似乎是Android中的一个错误,且在Android 4.0(Ice Cream Sandwich)中仍存在。
为了解决这个问题并能够从工作线程(例如AsyncTask)调用BluetoothAdapter.getDefaultAdapter(),您只需要在主UI线程上调用一次BluetoothAdapter.getDefaultAdapter()(例如在当前活动的onCreate()内部)即可。
RuntimeException仅在初始化期间抛出,并且BluetoothAdapter.getDefaultAdapter()仅在第一次调用时初始化。即使在后台线程中,对它的后续调用也将成功。

1
一个 bug 页面上有任何链接吗? - Denis Gladkiy

6

在UI线程中调用BluetoothAdapter.getDefaultAdapter()是可行的,但不太实际。我尝试了使用虚拟活动的解决方法,但由于我讨厌这种解决方法,所以我决定阅读错误消息并发现它只是因为线程没有调用Looper.prepare()

因此,在调用BluetoothAdapter.getDefaultAdapter()之前,仅需调用Looper.prepare()即可解决问题,不仅限于UI线程。

目前对我来说运行良好。


这似乎是个坏主意,除非您确实打算让您的工作线程成为一个 Looper。 - Brodo Fraggins
1
请注意,您应该检查当前线程是否已经调用了 Looper.prepare(),像这样:if (Looper.myLooper() == null) { Looper.prepare(); } 否则,您将会得到一个 RuntimeException,因为此函数每个线程只能调用一次。 - Elad Nava

3

我不确定是否正确,但我添加了这个包装函数:

static boolean m_calledLooperAlready = false;

BluetoothAdapter getDefaultBluetoothAdapter() {
    if ( !m_calledLooperAlready ) {
        try  {
            android.os.Looper.prepare();
        } catch ( RuntimeException e ) { e.printStackTrace(); }
        m_calledLooperAlready = true;
    }
    return BluetoothAdapter.getDefaultAdapter();
}

...并将所有BluetoothAdapter.getDefaultAdapter()替换为getDefaultBluetoothAdapter()。这在我使用的以下版本上运行良好:2.2.1、2.3.3、4.0.4、4.3。


1

请注意2.3.x版本中存在的一个陷阱,但在4.x中已经修复:如果您在除主应用程序线程之外的任何线程上调用BluetoothAdapter.getDefaultAdapter(),则该线程必须调用Looper.prepare()并随后调用Looper.loop()

未能这样做将导致至少遇到一个问题,即我遇到的问题:accept()在第一次尝试连接时会成功,但在连续尝试后不会成功,即使在ServerSocket上使用close()也是如此。

这是因为在旧版BluetoothAdapter的实现中,通过在调用getDefaultAdapter()的线程上创建的处理程序发送消息来清除SDP条目。


0

1
你的例子是从Activity获取BluetoothAdapter。这不是我想要的。 - Kocus
是的,但我认为BluetoothAdapter.getDefaultAdapter()不能从工作线程调用。它应该从UI线程调用。 - Sujit
1
不太理解什么是假的Activity。您能提供一些代码吗? - Ivan

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