Android 为什么我们应该使用带有返回的AsyncTask,而不是在UI线程中运行?

Android 为什么我们应该使用带有返回的AsyncTask,而不是在UI线程中运行?,android,asynchronous,Android,Asynchronous,在填充ListView时,我很难理解一件事,因为我的经验表明,在UI线程上运行的方法具有最高优先级,因此运行速度比在后台运行的方法快很多,但区别在于UI线程不会等待它们完成,所以我们不会感觉到任何延迟 但当我们使用AsyncTask时,结果如下: Class async extends AsyncTasl<Void,Void,Bitmap>{ @Override public Bitmap doInBackGround(Void...Params){ . . . r

在填充ListView时,我很难理解一件事,因为我的经验表明,在UI线程上运行的方法具有最高优先级,因此运行速度比在后台运行的方法快很多,但区别在于UI线程不会等待它们完成,所以我们不会感觉到任何延迟

但当我们使用AsyncTask时,结果如下:

 Class async extends AsyncTasl<Void,Void,Bitmap>{

 @Override
 public Bitmap doInBackGround(Void...Params){
 .
 .
 .
  return new bitMap;
  }
}
Class async扩展了AsyncTasl{
@凌驾
公共位图doInBackGround(无效…参数){
.
.
.
返回新位图;
}
}
因为它返回了一些东西,所以主线程必须等待它完成,这否定了在后台运行的主要目的,所以我是否理解了一些错误,或者确实没有理由在后台执行这些事情,而它们可以在UI线程上运行得更快

更新:


我的观点是当我们使用async.execute().get()

异步任务是在后台执行操作而不阻塞主UI线程的好方法

调用asyncTask.execute()时

  • 在UI线程上发出onPreExecute(..)回调
  • doInBackground(..)是在后台线程上调用的(如果我正确理解了您的问题,那么主线程不会在这里等待)。当您使用此方法从服务器(比如)获取数据时,您将返回数据,以便Android系统将在UI线程上使用此数据调用onPostExecute
  • onPostExecute(..)在UI线程上发出,并将您在doInBackground(..)中返回的数据作为参数
否,主线程不会等待任务完成,返回值将传递给
onPostExecute(…)
方法。此方法将在主线程上运行,并将该返回值作为自己的输入参数

Class async extends AsyncTasl<Void,Void,Bitmap>{

    @Override
    public Bitmap doInBackGround(Void...Params){
        /* Here is background thread */
        return new Bitmap(); ----+
    }                            |
                                 | //this object will be passed here
    @Override                    V
    public void onPostExecute(Bitmap result){
        /* Here is main thread */
    }
}
Class async扩展了AsyncTasl{
@凌驾
公共位图doInBackGround(无效…参数){
/*这里是背景线*/
返回新位图()----+
}                            |
|//将在此处传递此对象
@超越
public void onPostExecute(位图结果){
/*这是主线*/
}
}

UI线程的行为类似于执行代码的任何其他线程,但正如您所指出的,主要区别在于用户与之交互的线程

想象一下这个场景: 您需要加载带有图标的列表项,该图标位于URL处,必须通过网络请求检索

下面是一些示例代码,每当屏幕上出现列表项时,都会获取位于URL的图像:

public View getView(int position) {
    final URL imageURL = getURL(position);
    final Bitmap image = getImage(imageURL); //this will take a while
    ...
    view.setIcon(imageIcon);
    return view;
}
在Android上使用列表视图时,每当用户浏览列表时,就会调用getView,因为每个列表项都会出现在屏幕上

如果这一切都在UI线程上完成,那么用户将看到一个列表,加载每个列表项需要非常长的时间。主线程不够聪明,无法缩短长时间运行的任务(如果长时间运行的任务碰巧正在将重要的用户数据保存到某个外部数据库中怎么办?),因此它必须让它们完全完成。同样重要的是要注意,从系统的角度来看,UI线程与任何其他线程没有太大区别。这很重要,因为它是用户体验发生的地方

这就是为什么在任何平台上编写任何应用程序的最佳实践是将长时间运行的任务卸载到后台线程。操作系统可以处理线程之间的切换,这会产生一个对用户操作(如浏览列表)响应非常灵敏的UI

正如ABFORCE演示的,您应该在Android上使用异步任务获取图像。在后台,将向URL请求图像,并且只有当最终图像准备好显示在页面上时,才会调用UI线程来显示它。用户将看到一个他们可以快速浏览的列表,图像将在获取时弹出到每个列表项中。我们不想阻止用户做一些事情,所以我们要确保他们可以尽可能快地浏览列表

更新: 如果要执行新的AsyncTask().execute.get(),该方法确实会在UI线程上阻塞,从而抵消了后台线程的好处。在扩展AsyncTask的类中,您可能需要做的是保存一个对希望图像作为其一部分的视图的引用(我假设每个列表项都有一个新的AsyncTask),并在onPostExecute中,设置需要在该视图上设置的任何字段

例如:

class async extends AsyncTask<Void,Void,Bitmap>{

    private View view;

    @Override
    public Bitmap doInBackGround(Void...Params) {
        /* Here is background thread */
        return new Bitmap(); ----+
    }                            |
                                 | //this object will be passed here
    @Override                    V
    public void onPostExecute(Bitmap result) {
        /* Here is main thread */
        view.setIcon(result);
    }
}

显然有很多方法可以做到这一点,这是我脑子里想不到的。

如果主线程执行
Result Result=new AsyncTask().doInBackground()
(即直接调用
doInBackground
方法),它将不得不等待结果。但事实并非如此。结果神奇地出现在
onPostExecute
方法中。
async.execute().get()
不要在ui线程上调用它。实际上,它否定了使用异步任务的全部意义。发生的情况是,您的bg线程完成了工作,而您的ui线程却在等待(阻止)bg线程完成其工作。这正是我的意思,为什么我在完成后将imageView传递给异步任务以设置位图时,我的行位置会混淆呢!当您使用async.execute()执行它时,它并不总是这样。get,这就是在不混合行的情况下填充listView的方式是的,您需要在视图上设置标记,以便不会发生混淆。如果您是指ViewHolder,但是:(就像我上面提到的,我的意思是当您调用async.execute()时).get,否则ListView项将混合,至少在我的情况下,当我将imageView传递给async以设置Drawable时是这样的。我一直在尝试这样做,但我在列表中的行被混合,并且当我将imageView引用传递给async任务时,它们的位置将错误,因此他在PostExecute时将位图设置到其中,
public View getView(int position) {
    new async(view).execute();

    return view; //View will return now, and the image will populate later
}