在非UI线程上实例化视图
我知道UI元素(视图层次结构)可能只能从UI线程中操作。 对于后台操作,可以使用AsyncTask,它提供事件处理程序以访问UI线程。
简而言之,是否允许在非UI线程中实例化View(与getApplicationContext()
)? 此自定义View后代 – 一旦实例化 – 将从UI线程添加到视图层次结构中。 所以只有构造函数调用是在Asynctask.doInBackground()
; 它附加( addView(...)
)到Activity的根布局层次结构仍然在UI线程中完成。
详情如下:
public MyView extends View { public MyView(Context context) { ... } ... }
-
我制作了一个自定义视图,重写了
onDraw(...)
等。 -
当用户单击我的主Activity中的某个MenuItem时,会创建另一个Activity(MyOtherActivity)并显示哪个屏幕正好是MyView
-
由于MyOtherActivity的屏幕必须立即显示,我在AsyncTask中预先实例化MyView,而用户在主Activity中的其他位置(即他还没有点击那个MenuItem)。 MyView引用存储在静态数据成员中。
-
调用
MyOtherActivity.onCreate()
,其构造函数代码从静态获取MyView,并通过addView(...)
将其添加到其布局根层次结构中。 -
(我知道静态变量可能会引入内存泄漏,所以一旦不需要,我就把它设置为
null
。)
MyView是否在另一个线程中实例化(并在其构造函数中获取getApplicationContext()
的返回值getApplicationContext()
是不是问题(可能会引入意外问题)?
最终答案出现在View的文档中,标题为“事件处理和线程”:
注意:整个视图树是单线程的。 在任何视图上调用任何方法时,您必须始终位于UI线程上。 如果您正在处理其他线程并希望从该线程更新视图的状态,则应使用Handler。
所以它不仅仅是明显影响UI外观的东西,比如addView()
,而是“ 任何 View上的任何方法”。
关于 @CommonsWare链接到的Android开发人员的讨论 ,有来自Android框架团队的不止一位高级工程师确认这一点值得认真对待。
这是一个示例,介绍如何使用AsyncTask将视图添加到FrameLayout
public void addFLview(View view) { MyAsyncTask as = new MyAsyncTask(); as.execute(view); }
AsyncTask类
private class MyAsyncTask extends AsyncTask { @Override protected View doInBackground(View... params) { return params[0]; } @Override protected void onPostExecute(View view) { super.onPostExecute(view); myFrameLayout.addView(view); } }