AsyncTask 的 "UI interactions" 是什么?
What constitutes "UI interactions" for AsyncTask?
AsyncTask 是在后台线程上异步执行长 运行 操作而不占用 UI 线程的标准方法。不应从 doInBackground() 方法执行任何 UI 交互。
我的问题:UI 禁止互动的例子有哪些?会不会是以下任何一种:
- LayoutInflater.inflate()
- View.findViewById()
- TextView.setText()
我倾向于说是,但我们现在有一些代码可以完成所有这些(以及更多)并从 doInBackground() 方法调用,但代码仍在运行。我看到其他人表示他们在尝试从 doInBackground() 执行 UI activity 时收到异常,但这不是我们的经验。
我们的代码正在生成一个屏幕报告,该报告在整个操作完成之前是不可见的。在极少数情况下(难以重现)当尝试非常迅速地取消操作时,我们会看到应用程序进入一种奇怪的状态,但它不会崩溃。
在更改我们的代码以期找到这种罕见情况之前,我想看看是否有人对为什么我们的代码保持原样 "working" 有一些想法。
唯一可能有用的信息是我们的 doInBackground 方法具有以下代码模板:
protected Boolean doInBackground(Void... voids) {
if (null == Looper.myLooper()) {
Looper.prepare();
}
publishProgress(0.0);
// Perform ui/non-ui logic here
Looper myLooper = Looper.myLooper();
if (null != myLooper && Looper.getMainLooper() != myLooper) {
myLooper.quit();
}
return true;
}
一些使用new Handler()生成数据的报告生成代码(省略)需要Looper。我不确定创建 Looper 是否以某种方式使我们的 ui 交互合法。
(我确实有一个堆栈跟踪,清楚地显示了我们的 UI activity 是从 doInBackground 调用的,以防您认为我们可能会分离一些单独的线程来更新我们的 UI)
AsyncTask
并不意味着很长的 运行ning 工作,它应该在几秒钟内完成。它是一次性完全托管的线程上下文,不应该有自己的 Looper
附加到它。这实际上会破坏支持 AsyncTask
功能 - 使您将来可能开始的其他 AsyncTask
操作挨饿。如果您有需要 Looper
的东西,您应该使用自己的 Thread
或 ThreadPool
而不是 AsyncTask
。您还需要确保保留对 AsyncTask
的引用,以便可以适当地取消它 - 这是许多内存泄漏的来源 and/or 由于 onPostExecute()
时的无效状态导致的异常被调用。
publishProgress()
方法的目的是让您的应用能够获取可以反映在用户体验上的更新。你是对的,setText()
等在doInBackground()
回调中不应该是运行。该回调在您无法控制且无法进行 UI 更新的任意线程上下文中执行。
您也许可以使用 inflateLayout()
和 findViewById()
,但在初始化之外执行此操作不是一个好的做法,因为这些操作可能代价高昂。 Inflation 必须解析二进制布局并即时创建视图对象。按 ID 查找遍历整个视图层次结构以查找所需的组件。更好的做法是在创建时缓存它们(对于 Activity
或 Fragment
)或在创建视图作为适配器的一部分时(例如 RecyclerView
中的 ViewHolder
.
AsyncTask 是在后台线程上异步执行长 运行 操作而不占用 UI 线程的标准方法。不应从 doInBackground() 方法执行任何 UI 交互。
我的问题:UI 禁止互动的例子有哪些?会不会是以下任何一种:
- LayoutInflater.inflate()
- View.findViewById()
- TextView.setText()
我倾向于说是,但我们现在有一些代码可以完成所有这些(以及更多)并从 doInBackground() 方法调用,但代码仍在运行。我看到其他人表示他们在尝试从 doInBackground() 执行 UI activity 时收到异常,但这不是我们的经验。
我们的代码正在生成一个屏幕报告,该报告在整个操作完成之前是不可见的。在极少数情况下(难以重现)当尝试非常迅速地取消操作时,我们会看到应用程序进入一种奇怪的状态,但它不会崩溃。
在更改我们的代码以期找到这种罕见情况之前,我想看看是否有人对为什么我们的代码保持原样 "working" 有一些想法。
唯一可能有用的信息是我们的 doInBackground 方法具有以下代码模板:
protected Boolean doInBackground(Void... voids) {
if (null == Looper.myLooper()) {
Looper.prepare();
}
publishProgress(0.0);
// Perform ui/non-ui logic here
Looper myLooper = Looper.myLooper();
if (null != myLooper && Looper.getMainLooper() != myLooper) {
myLooper.quit();
}
return true;
}
一些使用new Handler()生成数据的报告生成代码(省略)需要Looper。我不确定创建 Looper 是否以某种方式使我们的 ui 交互合法。
(我确实有一个堆栈跟踪,清楚地显示了我们的 UI activity 是从 doInBackground 调用的,以防您认为我们可能会分离一些单独的线程来更新我们的 UI)
AsyncTask
并不意味着很长的 运行ning 工作,它应该在几秒钟内完成。它是一次性完全托管的线程上下文,不应该有自己的 Looper
附加到它。这实际上会破坏支持 AsyncTask
功能 - 使您将来可能开始的其他 AsyncTask
操作挨饿。如果您有需要 Looper
的东西,您应该使用自己的 Thread
或 ThreadPool
而不是 AsyncTask
。您还需要确保保留对 AsyncTask
的引用,以便可以适当地取消它 - 这是许多内存泄漏的来源 and/or 由于 onPostExecute()
时的无效状态导致的异常被调用。
publishProgress()
方法的目的是让您的应用能够获取可以反映在用户体验上的更新。你是对的,setText()
等在doInBackground()
回调中不应该是运行。该回调在您无法控制且无法进行 UI 更新的任意线程上下文中执行。
您也许可以使用 inflateLayout()
和 findViewById()
,但在初始化之外执行此操作不是一个好的做法,因为这些操作可能代价高昂。 Inflation 必须解析二进制布局并即时创建视图对象。按 ID 查找遍历整个视图层次结构以查找所需的组件。更好的做法是在创建时缓存它们(对于 Activity
或 Fragment
)或在创建视图作为适配器的一部分时(例如 RecyclerView
中的 ViewHolder
.