【问题标题】:Instantiating a View on non-UI thread在非 UI 线程上实例化视图
【发布时间】:2012-06-26 20:18:21
【问题描述】:

我知道 UI 元素(视图层次结构)只能从 UI 线程进行操作。对于后台操作,可以使用 AsyncTask,它提供事件处理程序以到达 UI 线程。

简而言之,是否允许在非 UI 线程中实例化视图(绑定到 getApplicationContext())?这个自定义视图后代——一旦被实例化——就会从UI 线程 添加到视图层次结构中。所以只有构造函数调用是在 Asynctask.doInBackground() 内完成的;将 (addView(...)) 附加到 Activity 的根布局层次结构仍然在 UI 线程中完成。

详细说明:

public MyView extends View {
     public MyView(Context context) { 
            ...
     }
...
}
  1. 我创建了一个自定义视图,覆盖了onDraw(...) 等。

  2. 当用户在我的主 Activity 中单击某个 MenuItem 时,会创建另一个 Activity (MyOtherActivity) 并显示哪个屏幕正是 MyView

  3. 由于必须立即显示 MyOtherActivity 的屏幕,我在 AsyncTask 中预实例化 MyView,而用户位于主 Activity 的其他位置(即他尚未单击该 MenuItem)。 MyView 引用存储在静态数据成员中。

  4. MyOtherActivity.onCreate()被调用时,它的构造器代码从静态中获取MyView,并通过addView(...)将它添加到它的布局根层次结构中。

  5. (我知道静态变量可能会导致内存泄漏,所以我在不需要时将其设置为null。)

MyView 在不同的线程中实例化(并在其构造函数中获取getApplicationContext() 的返回值)不是一个问题(并且可能会引入意想不到的问题)吗?

【问题讨论】:

  • 交叉引用android-developers对此的讨论:groups.google.com/group/android-developers/browse_thread/thread/…
  • 谢谢,马克。对于所有读者:简而言之,不要做我做过的事。始终从 UI 线程实例化所有视图。我会在 2 天的限制结束后回答我自己的问题。现在我将对其进行编辑。
  • @ThomasCalc 我看不出有什么理由需要等待两天才能得到答复。我认为这是一个很好的问题,值得一个真正的答案(所以我刚刚提供了一个)。
  • @DarshanComputing 当我新注册的时候,有 2 天的限制,但也许它仍然适用。所以你添加一个是个好主意,谢谢。我刚刚将其设置为接受。

标签: java android android-asynctask android-ui android-view


【解决方案1】:

确定的答案出现在View 的文档中,标题为“事件处理和线程”:

注意:整个视图树是单线程的。在任何视图上调用任何方法时,您必须始终在 UI 线程上。 如果您在其他线程上工作并希望从该线程更新视图的状态,则应使用 Handler .

因此,明显影响 UI 外观的不仅仅是 addView(),而是“any View 上的any 方法”。

@CommonsWare 链接到的discussion on android-developers 有不止一名来自 Android 框架团队的高级工程师确认这将被认真对待。

【讨论】:

  • 您引用的文档可能会让初学者有些困惑。我们知道“任何方法”也意味着构造函数,因为构造函数实际上也是方法。可能会让初学者感到困惑的是,文档暗示该规则仅适用于现有视图(“在任何视图上”)上的方法调用,并且没有说明任何关于它们的 CONSTRUCTION(实例化)的内容。
  • 当然,我并不是说一般会令人困惑。只是如果他们问:“嗯,我可以在这里实例化一个视图吗?让我们检查一下文档”,那么他只会看到方法调用。他们在学校教什么?只有在实例化对象后才能调用(实例)方法。因此初学者可能会得出错误的结论,即文档仅适用于现有视图。
  • @ThomasCalc 部分原因在于我认为这是一个很好的问题。我也发现自己在想“我知道你不应该在 UI 线程之外对 UI 做任何事情,但也许你可以在另一个线程上实例化一个 View,只要你 add 它到UI 线程上的 UI。”我很高兴你问我,我很高兴 Dianne Hackborn 和 Romain Guy 确认了答案。我确实认为“任何观点的任何方法”都是清晰明确的,即使没有他们的确认。无论如何,谢谢——我现在有一个比“UI 事情发生在 UI 线程上”更好的心智模型。
  • IMO 这很明确,但对于初学者来说,有时更多信息可能会很方便(例如“任何视图上的任何方法,包括它们的构造函数”)。是的,“UI 事情发生在 UI 线程上”很笼统,有时边框不是那么明显,例如考虑官方的 AsyncDrawable(它根本不是异步创建的,只是它的底层位图)。
  • 在讨论中总是有一个很好的解释,它更清楚地说明了为什么你“不能”做某事。希望 android 文档更详细一点,就像这个 “即使从一个线程实例化一个视图并将其附加到另一个视图层次结构中也不安全,因为许多视图将在内部创建绑定到它们的任何线程的 Handler 对象是在上面创建的,导致您的视图层次结构的一部分在错误的线程上运行,结果很糟糕。” 简单的一行可能值得整个糟糕的 android 文档,只说“不得”
【解决方案2】:

这是一个示例,说明如何使用 AsyncTask 将视图添加到 FrameLayout

public void addFLview(View view) {
    MyAsyncTask as = new MyAsyncTask();
    as.execute(view);
}

AsyncTask 类

private class MyAsyncTask extends AsyncTask<View, Void, View> {
    @Override
    protected View doInBackground(View... params) {
        return params[0];
    }
    @Override
    protected void onPostExecute(View view) {
        super.onPostExecute(view);
        myFrameLayout.addView(view);
    }
}

【讨论】:

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