ChatGPT解决这个技术问题 Extra ChatGPT

从Android中的AsyncTask返回一个值[重复]

这个问题在这里已经有了答案:How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class? (17 个回答) 5 年前关闭。

一个简单的问题:是否可以在 AsyncTask 中返回一个值?

//AsyncTask is a member class
private class MyTask extends AsyncTask<Void, Void, Void>{

    protected Void doInBackground(Void... params) {
         //do stuff
         return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        //do stuff
        //how to return a value to the calling method?
    }
}

然后在我的 Activity/Fragment 中:

// The task is started from activity
myTask.execute()
// something like this?
myvalue = myTask.getvalue() 

编辑:很久以前有人问过我不熟悉Java的地方,现在我对它更好,我将做一个快速总结:

异步任务的重点是任务是 asynchronous,这意味着在任务上调用 execute() 后,任务开始在自己的线程上运行。从 asynctask 返回一个值是没有意义的,因为原来的调用线程已经在做其他事情(因此任务是异步的)。

想想时间:在某个时间点,您启动了一项将与主线程并行运行的任务。当并行运行的任务完成时,主线程上的时间也过去了。并行任务无法及时返回向主线程返回值。

我来自C,所以我对此知之甚少。但似乎很多人都有同样的问题,所以我想我会澄清一下。

关于您的编辑;一组密切相关的基于线程的对象确实具有返回某些东西的概念;线程池和期货。当您将任务提交到线程池时,您将获得未来。在未来某个时间(希望在 runnable 完成之后),您向未来询问它的返回值。如果runnable已经完成,你会立即得到返回值,如果没有,线程会一直等到它完成。我经常用它把东西分成几个部分,同时运行它们,然后得到结果(通常只在几行之后)
@RichardTingle 你能给我一些关于你在说什么的链接......示例代码会更好。在此先感谢,请不要忘记在您的评论中提及我。
我可以得到像听众这样的结果吗?
是的,但注意不要泄露它,使用 WeakReference 或类似的东西

T
Ted Hopp

这就是 onPostExecute() 的用途。它在 UI 线程上运行,您可以将结果从那里传送到屏幕(或您需要的任何其他地方)。在最终结果可用之前不会调用它。如果您想提供中间结果,请查看 onProgressUpdate()


是的,我知道 doInBackground() 返回数据并将其放在那里,但是如何以变量形式将数据传输到我的主要活动?
@Tom91136 - 只需覆盖 onPostExecute() 即可将结果存储在您选择的变量中。请注意,您的原始代码没有意义,因为(假设的)方法 myTask.getValue() 将在之前调用结果可用。您也可以调用 AsyncTask 的 get() 方法来获取结果,但在您确定结果可用之前,您不应从 UI 线程执行此操作。否则,它会阻塞 UI 线程,首先破坏了使用 AsyncTask 的目的。
@TedHopp 您说将结果存储在您在 onPostExecute() 中选择的变量中,但是如何将该变量返回到您的活动中?例如,如果您希望您的任务连接到 Internet 并下载一些信息,然后您想对这些信息执行一些操作...您如何.execute() AsyncTask 然后在接下来的情况下使用该信息执行某些操作在 AsyncTask 完成之前运行的代码行?
@Jakobud - 你不能那样做。您需要将“下一行代码”移到其他地方(例如,onPostExecute())。我使用这个类比:您不会编写等待用户输入的代码(例如,按下按钮);相反,您编写的事件处理程序会在用户提供一些输入时做出反应。将 onPostExecute() 视为 AsyncTask 的结果可用时的事件处理程序。这就是您放置代码(或调用方法)的地方,除非结果确实可用,否则这些代码将不起作用。
所以 AsyncTask 真的不应该被认为是一个简单地获取和返回一些信息的实用函数,而是一个更大的东西,它可以获取信息并在获取该信息后操纵 UI(或其他)?
M
Meneer Venus

为什么不调用处理值的方法?

public class MyClass extends Activity {

    private class myTask extends AsyncTask<Void, Void, Void> {

        //initiate vars
        public myTask() {
            super();
            //my params here
        }

        protected Void doInBackground(Void... params) {
            //do stuff
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            //do stuff
            myMethod(myValue);
        }
    }

    private myHandledValueType myMethod(Value myValue) {
        //handle value 
        return myHandledValueType;
    }
}

以及如何访问 myMethod?
它不起作用,例如 myTask mTask = new myTask(); mTask.execute();不会返回 myHandledValueType。
格式很差,但基本上 AsyncTask 调用活动方法:带有 myValue 的 myMethod(据说是在 doInBackground 中设置的类变量)。在 myMethod 中,您处理 AsyncTask 的结果(这里没有返回值的原因,只需在此方法中处理结果即可)。更好的实现是扩展 AsyncTask 并让 doInBackground 返回 Value 并让 OnPostExecute 接受 Value 并将其传递给 myMethod - 这将避免任何混乱的类变量,这也是 AsyncTask 的设计方式。
我使用带有接口的 asynctask。当 asynctask 完成时会触发该界面(在我的情况下是成功的,在上传成功的意义上是成功的)。在接口的成功函数中,我从 asynctask 中调用了该方法,并得到了值
这种方法的一个问题是,如果活动中存在配置更改并且任务是从生命周期方法之一启动的,则将(重复)触发 AsyncTask ......这很难让多个任务做同样的事情更改屏幕方向时
S
Sheamus O'Halloran

最简单的方法是将调用对象传递给异步任务(如果您愿意,可以在构造它时):

public class AsyncGetUserImagesTask extends AsyncTask<Void, Void, Void> {

    private MyImagesPagerFragment mimagesPagerFragment;
    private ArrayList<ImageData> mImages = new ArrayList<ImageData>();

    public AsyncGetUserImagesTask(MyImagesPagerFragment imagesPagerFragment) {
        this.mimagesPagerFragment = imagesPagerFragment;
    }

    @Override
    public Void doInBackground(Void... records) {
        // do work here
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        mimagesPagerFragment.updateAdapter(mImages);
    }
}

并且在调用类(您的活动或片段)中执行任务:

public class MyImagesPagerFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        AsyncGetUserImagesTask mGetImagesTask = new AsyncGetUserImagesTask(this);
        mGetImagesTask.execute();
    }

然后 onPostExecuteMethod 将调用您喜欢的原始类的任何方法,例如:

    public void updateAdapter(List<ImageData> images) {
        mImageAdapter.setImages(images);
        mImageAdapter.notifyDataSetChanged();
    }
}

这本质上是依赖注入
此解决方案有效。除非这将被其他片段重用,否则将异步任务设置为私有内部类(或匿名)而不传递片段会更简单。此外,您可以指定图像数组,而不是使用 Void 作为结果。 doInBackground 将返回图像数组,并在执行后将其作为参数,这将消除对类变量传递数据的需要。
@jt-gilkeson 是正确的,但这也不能重用,因为您必须在您拥有的每个 Activity 类中包含该内部类。怎样才能做到这一点?制作一个可重用的 AsyncTask 类?
@DIEGOF.G。正确 - 因此“除非这将是可重复使用的”资格。如果您想要一个可重用的解决方案 - 您可以创建一个嵌入了内部 asynctask 类的自定义片段类 - 然后从该类扩展需要此功能的片段。
或者你可以让 AsyncGetUserImagesTask 构造函数接受一个接口而不是一个具体的片段——然后任何实现接口的东西都可以使用这个类(即依赖注入)。
j
jt-gilkeson

代码示例:Activity使用AsyncTask在后台线程中获取值,然后AsyncTask通过调用processValue将结果返回给Activity:

public class MyClass extends Activity {
  private void getValue() {
      new MyTask().execute();
  }

  void processValue(Value myValue) {
     //handle value 
     //Update GUI, show toast, etc..
  }

  private class MyTask extends AsyncTask<Void, Void, Value> {
    @Override
    protected Value doInBackground(Void... params) {
      //do stuff and return the value you want 
      return Value;
    }

    @Override
    protected void onPostExecute(Value result) {
      // Call activity method with results
      processValue(result);
    }
  }
}

T
Tarsem Singh

你可以试试这个:myvalue = new myTask().execute().get();减号是它会冻结进程,直到异步不会完成;


避免此冻结 UI 的任何其他方式
不鼓励在 UI 线程中执行繁重的任务,这可能会导致 ANR。此外,在主线程上执行网络操作会导致 NetworkOnMainThreadException 并按设计立即崩溃。查看那篇不错的文章:developer.android.com/training/articles/perf-anr#java
I
IgniteCoders

您需要使用 "protocols"AsynTask 委派或提供数据。

代表和数据源

委托是一个对象,当该对象遇到程序中的事件时,该对象代表另一个对象或与该对象协作。 (Apple definition)

协议是定义一些方法来委托某些行为的接口。

DELEGATE:在后台线程中从对象中捕获事件

异步任务:

public final class TaskWithDelegate extends AsyncTask<..., ..., ...> {
    //declare a delegate with type of protocol declared in this task
    private TaskDelegate delegate;

    //here is the task protocol to can delegate on other object
    public interface TaskDelegate {
        //define you method headers to override
        void onTaskEndWithResult(int success);
        void onTaskFinishGettingData(Data result);
    }

    @Override
    protected Integer doInBackground(Object... params) {
        //do something in background and get result
        if (delegate != null) {
            //return result to activity
            delegate.onTaskFinishGettingData(result);
        }   
    }

    @Override
    protected void onPostExecute(Integer result) {
        if (delegate != null) {
            //return success or fail to activity
            delegate.onTaskEndWithResult(result);
        }   
    }
}

活动:

public class DelegateActivity extends Activity implements TaskDelegate {
    void callTask () {
            TaskWithDelegate task = new TaskWithDelegate;
        //set the delegate of the task as this activity
        task.setDelegate(this);
    }

    //handle success or fail to show an alert...
    @Override
    void onTaskEndWithResult(int success) {

    }

    //handle data to show them in activity...
    @Override
    void onTaskFinishGettingData(Data result) {

    }
}

编辑:如果您在 doInBackground 中调用委托,并且委托尝试编辑某些视图,则会崩溃,因为视图只能由主线程操作。

// this refers to Activity
this.runOnUiThread(new Runnable() {
    @Override
    public void run() {
    // Here you can edit views when task notify some changes from background thread
        textView.setText(someValue);
    }
});

额外的

DATASOURCE:在后台线程中向对象提供数据

异步任务:

public final class TaskWithDataSource extends AsyncTask<..., ..., ...> {
    //declare a datasource with type of protocol declared in this task
    private TaskDataSource dataSource;
    private Object data;

    //here is the task protocol to can provide data from other object
    public interface TaskDataSource {
        //define you method headers to override
        int indexOfObject(Object object);
        Object objectAtIndex(int index);
    }

    @Override
    protected void onPreExecute(Integer result) {
        if (dataSource != null) {
            //ask for some data
            this.data = dataSource.objectAtIndex(0);
        }   
    }

    @Override
    protected Integer doInBackground(Object... params) {
        //do something in background and get result
        int index;
        if (dataSource != null) {
            //ask for something more
            index = dataSource.indexOfObject(this.data);
        }   
    }
}

活动:

public class DataSourceActivity extends Activity implements TaskDataSource {
    void callTask () {
            TaskWithDataSource task = new TaskWithDataSource;
        //set the datasource of the task as this activity
        task.setDataSource(this);
    }

    //send some data to the async task when it is needed...
    @Override
    Object objectAtIndex(int index) {
        return new Data(index);
    }

    //send more information...
    @Override
    int indexOfObject(Object object) {
        return new object.getIndex();
    }
}

因此,换句话说,只需使用一个接口,需要通知的人只需注册为侦听器即可。
是的,简而言之就是这个想法。
我发现这是最干净的答案。 Task Class 不应该知道 Activity 的实现细节。另一个重要的好处是代码重用,您可以为任何其他活动/片段调用此类
N
Nikita Bosik

使用泛型参数

AsyncTask<Params, Progress, Result>

Params — 任务的输入数据类型

进展——如何让世界了解进展

Result — 任务的输出数据类型

想像

Result = task(Params)

例子

按字符串 URL 加载 YourObject

new AsyncTask<String, Void, YourObject>()
{
    @Override
    protected void onPreExecute()
    {
        /* Called before task execution; from UI thread, so you can access your widgets */

        // Optionally do some stuff like showing progress bar
    };

    @Override
    protected YourObject doInBackground(String... url)
    {
        /* Called from background thread, so you're NOT allowed to interact with UI */

        // Perform heavy task to get YourObject by string
        // Stay clear & functional, just convert input to output and return it
        YourObject result = callTheServer(url);
        return result;
    }

    @Override
    protected void onPostExecute(YourObject result)
    {
        /* Called once task is done; from UI thread, so you can access your widgets */

        // Process result as you like
    }
}.execute("http://www.example.com/");

S
Sunil Garg

对于您从 AsyncTask 获得的结果,它需要在 super.onPostExcecute(result) 之后执行;因为这意味着后台和AsyncTask也已经完成了。例如:

... into your async task

@Override
protected void onPostExecute(MyBeanResult result) {
    if (dialog.isShowing()) {
        dialog.dismiss();
    }
    if (mWakeLock.isHeld()) {
        mWakeLock.release();
    }
    super.onPostExecute(result);
    MyClassName.this.checkResponseForIntent(result);
}

方法器 checkResponseForIntent 可能是这样的:

private void checkResponseForIntent(MyBeanResult response) {
    if (response == null || response.fault != null) {
        noServiceAvailable();
        return;
    }
 ... or do what ever you want, even call an other async task...

我在使用 AsyncTask 中的 .get() 时遇到了这个问题,而 ProgressBar 根本不适用于 get(),实际上,它仅在 doInBackground 完成后才有效。

我希望它可以帮助你。


没错,进度对话无法与 get() 方法一起使用
H
Hamid

将 MainActivity 传递给 Async 类,因此您将在内部类中访问 MainActivity 函数,这对我来说很好:

public class MainActivity extends ActionBarActivity {

   void callAsync()
   {
      Async as = new Async(this,12,"Test");
      as.execute();
   }

   public void ReturnThreadResult(YourObject result)
   {
     // TO DO:
     // print(result.toString());

   }
}


public class Async extends AsyncTask<String, String, Boolean> {
    MainActivity parent;
    int param1;
    String param2;

    public Async(MainActivity parent,int param1,String param2){
       this.parent = parent;
       this.param1 = param1;
       this.param2 = param2;
    }

    @Override
    protected void onPreExecute(){};

    @Override
    protected YourObject doInBackground(String... url)
    {
        return result;
    }

    @Override
    protected void onPostExecute(YourObject result)
    {
       // call an external function as a result 
       parent.ReturnThreadResult(result);
    }
  }