【问题标题】:Inflate a view in a background thread在后台线程中膨胀视图
【发布时间】:2026-01-09 09:00:01
【问题描述】:

我有一个非常简单的问题:

是否可以在后台线程(例如:AsyncTaskdoInBackground 中)膨胀 视图(不将其添加到布局中)?

我知道这是可能的,因为我已经以这种方式在我的应用程序中实现了大多数活动并且从来没有遇到过问题,直到我在 Galaxy S 上遇到这个问题:Android: android.view.InflateException: Binary XML file line #13: Error inflating class <unknown> in SAMSUNG Galaxy S

有人告诉我不应该在后台线程中为 Views 充气,但具体原因是什么?为什么我的方法在大多数设备上都有效,但在 Galaxy S 中无效? p>

【问题讨论】:

    标签: android multithreading thread-safety inflate


    【解决方案1】:

    LayoutInflater 不对它运行的线程做任何假设。在其文档中没有提到这一点。它的代码接缝也与线程无关。

    另一方面,LayoutInflater 创建的视图可能会在其构造函数中实例化Handlers。好吧,他们可能不应该那样做,但没有要求他们不在构造函数中创建/使用Handlers。

    我的猜测是,三星 Galaxy S 在其 EditText 中进行了一些修改,以某种方式触发了 Handler 的创建(根据您的另一个问题实例的崩溃日志,GestureDetector 被实例化,这反过来又创建了新的 Handler )。虽然默认实现不这样做。


    总的来说,我想说的是因为没有明确要求 Views 在其构造函数中不使用 Handlers 和 Loopers 你不能假设从非 UI 线程膨胀视图是安全的.

    您实际上可以创建HandlerThread 并尝试在其中膨胀Views。但我会说这是非常冒险的,因为在三星 Galaxy S 示例中,视图假定该线程在View 生命周期内将处于活动状态,并将使用其Looper 处理所有消息。这可能会导致以后崩溃。

    【讨论】:

    • 我在运行最新库存固件的 Galaxy Nexus 上重现了同样的问题(截至本文为止)。在后台膨胀视图绝对不再是一种选择。
    • HandlerThread forward Message to MainLooper应该可以解决这个问题
    【解决方案2】:

    使用最新的支持库,您可以使用android.support.v4.view.AsyncLayoutInflater 异步扩展视图。请注意,如果不满足特定要求,它可能会退回到 UI 线程上的膨胀:

    对于要异步膨胀的布局,它需要有一个其 generateLayoutParams(AttributeSet) 是线程安全的父级,并且作为膨胀的一部分构造的所有视图不得创建任何处理程序或以其他方式调用 myLooper()。如果尝试膨胀的布局由于某种原因无法异步构造,AsyncLayoutInflater 将自动退回到 UI 线程上的膨胀。

    【讨论】:

      【解决方案3】:

      是否可以在后台线程(例如:在 AsyncTask 的 doInBackground 中)扩展视图(不将其添加到布局中)?

      可能,是的。受到推崇的?不。如文档中所述:

      因此,Android 的单线程模型只有两条规则:

      1. 不要阻塞 UI 线程
      2. 不要从 UI 线程外部访问 Android UI 工具包

      通过:Processes and Threads

      更新 [02/06/19]:

      显然,支持库有一个工具可以做到这一点: AsyncLayoutInflater (Jetpack version)。它是在 2016 年左右(我回答后 2 年)的第 24 版中引入的

      但是,正如其他答案所述,请小心使用此工具,因为它很容易适得其反。

      【讨论】: