如何将OnPostExecute()的结果传递给主活动,因为AsyncTask是一个单独的类?

379
我有这两个类,我的主Activity和继承AsyncTask的一个类。现在在我的主Activity中,我需要从AsyncTask的OnPostExecute()方法中获取结果。我如何将结果传递或获取到我的主Activity中?
以下是示例代码。
我的主Activity。
public class MainActivity extends Activity{

    AasyncTask asyncTask = new AasyncTask();

    @Override
    public void onCreate(Bundle aBundle) {
        super.onCreate(aBundle);            

        //Calling the AsyncTask class to start to execute.  
        asyncTask.execute(a.targetServer); 

        //Creating a TextView.
        TextView displayUI = asyncTask.dataDisplay;
        displayUI = new TextView(this);
        this.setContentView(tTextView); 
    }

}

这是AsyncTask类。

public class AasyncTask extends AsyncTask<String, Void, String> {

TextView dataDisplay; //store the data  
String soapAction = "http://sample.com"; //SOAPAction header line. 
String targetServer = "https://sampletargeturl.com"; //Target Server.

//SOAP Request.
String soapRequest = "<sample XML request>";    



@Override
protected String doInBackground(String... string) {

String responseStorage = null; //storage of the response

try {


    //Uses URL and HttpURLConnection for server connection. 
    URL targetURL = new URL(targetServer);
    HttpURLConnection httpCon = (HttpURLConnection) targetURL.openConnection();
    httpCon.setDoOutput(true);
    httpCon.setDoInput(true);
    httpCon.setUseCaches(false); 
    httpCon.setChunkedStreamingMode(0);

    //properties of SOAPAction header
    httpCon.addRequestProperty("SOAPAction", soapAction);
    httpCon.addRequestProperty("Content-Type", "text/xml; charset=utf-8"); 
    httpCon.addRequestProperty("Content-Length", "" + soapRequest.length());
    httpCon.setRequestMethod(HttpPost.METHOD_NAME);


    //sending request to the server.
    OutputStream outputStream = httpCon.getOutputStream(); 
    Writer writer = new OutputStreamWriter(outputStream);
    writer.write(soapRequest);
    writer.flush();
    writer.close();


    //getting the response from the server
    InputStream inputStream = httpCon.getInputStream(); 
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    ByteArrayBuffer byteArrayBuffer = new ByteArrayBuffer(50);

    int intResponse = httpCon.getResponseCode();

    while ((intResponse = bufferedReader.read()) != -1) {
        byteArrayBuffer.append(intResponse);
    }

    responseStorage = new String(byteArrayBuffer.toByteArray()); 

    } catch (Exception aException) {
    responseStorage = aException.getMessage(); 
    }
    return responseStorage;
}

protected void onPostExecute(String result) {

    aTextView.setText(result);

}       

}   

你可以像HelmiB所解释的那样创建一个接口,或者你可以创建一个本地广播接收器。你可以在这里查看示例广播接收器的实现:http://wiki.workassis.com/android-local-broadcast-receiver/ - Bikesh M
为什么不使用观察者模式? - JAAAY
17个回答

779

简单:

  1. 创建一个 interface 接口类,其中 String output 是可选的,或者可以是任何你想要返回的变量。

 public interface AsyncResponse {
     void processFinish(String output);
 }
去你的AsyncTask类,并将接口AsyncResponse声明为一个字段:
 public class MyAsyncTask extends AsyncTask<Void, Void, String> {
   public AsyncResponse delegate = null;

     @Override
     protected void onPostExecute(String result) {
       delegate.processFinish(result);
     }
  }
在你的主活动(Activity)中,你需要实现(implement) AsyncResponse 接口。
 public class MainActivity implements AsyncResponse{
   MyAsyncTask asyncTask =new MyAsyncTask();

   @Override
   public void onCreate(Bundle savedInstanceState) {

      //this to set delegate/listener back to this class
      asyncTask.delegate = this;

      //execute the async task 
      asyncTask.execute();
   }

   //this override the implemented method from asyncTask
   @Override
   void processFinish(String output){
      //Here you will receive the result fired from async class 
      //of onPostExecute(result) method.
    }
  }

更新

我不知道这对你们中的许多人来说是这样的一个喜爱。因此,这里是使用interface的简单方便方法。

仍然使用相同的interface。FYI,您可以将此组合到AsyncTask类中。

AsyncTask类中:

public class MyAsyncTask extends AsyncTask<Void, Void, String> {

  // you may separate this or combined to caller class.
  public interface AsyncResponse {
        void processFinish(String output);
  }

  public AsyncResponse delegate = null;

    public MyAsyncTask(AsyncResponse delegate){
        this.delegate = delegate;
    }

    @Override
    protected void onPostExecute(String result) {
      delegate.processFinish(result);
    }
}

在你的Activity类中执行此操作。

public class MainActivity extends Activity {
  
   MyAsyncTask asyncTask = new MyAsyncTask(new AsyncResponse(){
    
     @Override
     void processFinish(String output){
     //Here you will receive the result fired from async class 
     //of onPostExecute(result) method.
     }
  }).execute();

 }

或者,在活动上再次实现接口

public class MainActivity extends Activity 
    implements AsyncResponse{
      
    @Override
    public void onCreate(Bundle savedInstanceState) {

        //execute the async task 
        new MyAsyncTask(this).execute();
    }
      
    //this override the implemented method from AsyncResponse
    @Override
    void processFinish(String output){
        //Here you will receive the result fired from async class 
        //of onPostExecute(result) method.
    }
}

如你所见,上面的两个解决方案中,第一个和第三个需要创建processFinish方法,而另一个则将该方法放在了调用者参数中。第三个更加简洁,因为没有嵌套匿名类。

提示: 将String outputString responseString result更改为不同的匹配类型,以获取不同的对象。


20
完全写了所有的东西,然后看到你的答案 :) 这就是想法相似的好处!+1。此外,这样多个事物就可以听取AsyncTask的结果了! - Roloc
8
我遇到了空指针异常,因为委托对象被设置为null,请说明一下。 - Reyjohn
2
只是提醒一下 .net 开发人员,可以使用 AutoResetEvents 来实现这一点,而且也有一个 Java 实现的 autoresetevents,但这更加清晰。顺便问一下,ProcessFinish 是线程安全的吗? - Syler
13
如果你遇到了空指针异常,可以把接口传递给AsyncTask类的构造函数,然后将其分配给一个变量,并在该变量上调用processfinish()方法。参考已接受的答案:https://dev59.com/_Wkw5IYBdhLWcg3wV5ED - Sunny
2
只是一点小提示,在AsyncTask类中创建listener(Activity)的弱引用,并在post execute时检查是否为null,以避免内存泄漏。 - Saqib
显示剩余28条评论

25

有几种选择:

  • AsyncTask类嵌套在您的Activity类中。假设您不会在多个活动中使用相同的任务,这是最简单的方法。所有代码保持不变,只需将现有任务类移动为内部类即可。

public class MyActivity extends Activity {
    // existing Activity code
    ...

    private class MyAsyncTask extends AsyncTask<String, Void, String> {
        // existing AsyncTask code
        ...
    }
}
创建一个自定义的构造函数,为你的AsyncTask传递对你的Activity的引用。你可以使用以下方式实例化任务:new MyAsyncTask(this).execute(param1, param2)
public class MyAsyncTask extends AsyncTask<String, Void, String> {
    private Activity activity;

    public MyAsyncTask(Activity activity) {
        this.activity = activity;
    }

    // existing AsyncTask code
    ...
}

在第二个选项中:如果类不是static,为什么要经历构造函数和字段的痛苦呢?只需使用MyActivityMyActivity.this.fieldOrMethod中的任何fieldOrMethod即可,如果它被遮蔽了。 - TWiStErRob
@TWiStErRob 第二个假设 MyAsyncTask 不是内部类。 - quietmint
1
哦,抱歉,不允许在顶层类中使用 private/protected ,因此我认为它必须是一个非 static 内部类。 - TWiStErRob
2
请注意,内部类AsyncTask可能会导致内存泄漏,如此处所述:http://garena.github.io/blog/2014/09/10/android-memory-leaks/。 - Mark Pazon
2
持有Activity的引用也会导致内存泄漏。回调方法比这种方式要好得多。 - OneCricketeer

19
您可以在Main类中尝试此代码。 这对我有用,但我已以其他方式实现了方法
try {
    String receivedData = new AsyncTask().execute("http://yourdomain.com/yourscript.php").get();
} 
catch (ExecutionException | InterruptedException ei) {
    ei.printStackTrace();
}

2
你能否解释一下为什么这会帮助OP解决他们的问题? - John Odom
3
那对我很有帮助。我试了@HelmiB的答案所提供的方法,但没有得到结果。 - Nicu P
抱歉我的回答有误。她需要调用AsyncTask类,然后向该调用传递一个参数。AsyncTask由3个泛型类型定义:1个参数(服务器域或其他参数),2个进度(可以是void),3个结果。所有这些类型都可以是JSONObject、字符串或任何数据类型。当您拥有一个AsyncTask类时,应该定义要将数据发送到哪里。例如:asyncTask.execute("https://sampletargeturl.com").get(); - Nicu P
当使用此方法时,我在Logcat上收到了一个警告:“I / Choreographer﹕跳过50帧!应用程序可能在其主线程上执行太多工作。”如何处理?等待从AsyncTask返回时,UI是否会冻结? - Huy Do
@NicuP 你可能忘记调用execute(),请看看我的回答,我在那里加了注释。你可能想要检查一下我的更新答案。希望这能帮到你。 - HelmiB

19

我觉得下面的方法非常简单。

我为回调声明了一个接口。

public interface AsyncResponse {
    void processFinish(Object output);
}

然后创建了异步任务以响应所有类型的并行请求

 public class MyAsyncTask extends AsyncTask<Object, Object, Object> {

    public AsyncResponse delegate = null;//Call back interface

    public MyAsyncTask(AsyncResponse asyncResponse) {
        delegate = asyncResponse;//Assigning call back interfacethrough constructor
    }

    @Override
    protected Object doInBackground(Object... params) {

      //My Background tasks are written here

      return {resutl Object}

    }

    @Override
    protected void onPostExecute(Object result) {
        delegate.processFinish(result);
    }

}

在活动类中点击按钮时,调用异步任务。

public class MainActivity extends Activity{

    @Override
    public void onCreate(Bundle savedInstanceState) {

    Button mbtnPress = (Button) findViewById(R.id.btnPress);

    mbtnPress.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {

                MyAsyncTask asyncTask =new MyAsyncTask(new AsyncResponse() {

                    @Override
                    public void processFinish(Object output) {
                        Log.d("Response From Asynchronous task:", (String) output);

                        mbtnPress.setText((String) output);
                   }
                });

                asyncTask.execute(new Object[] { "Your request to aynchronous task class is giving here.." });


            }
        });

    }



}

感谢


17
这个回答可能有点晚了,但是当你的Activity依赖于AsyncTask时,我想提到一些东西。这将有助于您防止崩溃和内存管理。如上面的答案中已经提到的,使用interface,我们也称为回调函数。它们将作为通知者,但永远不要发送Activityinterfacestrong引用,在这些情况下始终使用weak引用。

请参考下面的截图,查找可能导致问题的原因。

enter image description here

如您所见,如果我们使用strong引用启动AsyncTask,那么我们的Activity/Fragment将无法保证在我们获得数据之前还活着,因此最好在这些情况下使用WeakReference,这也有助于内存管理,因为我们永远不会持有我们的Activity的强引用,那么在其销毁后就可以进行垃圾收集。

请检查下面的代码片段,了解如何使用强大的WeakReference -

MyTaskInformer.java接口将作为通知器。

public interface MyTaskInformer {

    void onTaskDone(String output);

}

MySmallAsyncTask.java是一个异步任务,用于执行长时间运行的任务,该任务将使用WeakReference

public class MySmallAsyncTask extends AsyncTask<String, Void, String> {

    // ***** Hold weak reference *****
    private WeakReference<MyTaskInformer> mCallBack;

    public MySmallAsyncTask(MyTaskInformer callback) {
        this.mCallBack = new WeakReference<>(callback);
    }

    @Override
    protected String doInBackground(String... params) {

        // Here do whatever your task is like reading/writing file
        // or read data from your server or any other heavy task

        // Let us suppose here you get response, just return it
        final String output = "Any out, mine is just demo output";

        // Return it from here to post execute
        return output;
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);

        // Here you can't guarantee that Activity/Fragment is alive who started this AsyncTask

        // Make sure your caller is active

        final MyTaskInformer callBack = mCallBack.get();

        if(callBack != null) {
            callBack.onTaskDone(s);
        }
    }
}

MainActivity.java这个类用于启动我的AsyncTask,并在这个类上实现interface,重写这个必需的方法。

public class MainActivity extends Activity implements MyTaskInformer {

    private TextView mMyTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mMyTextView = (TextView) findViewById(R.id.tv_text_view);

        // Start your AsyncTask and pass reference of MyTaskInformer in constructor
        new MySmallAsyncTask(this).execute();
    }

    @Override
    public void onTaskDone(String output) {

        // Here you will receive output only if your Activity is alive.
        // no need to add checks like if(!isFinishing())

        mMyTextView.setText(output);
    }
}

7

你只需要在调用AsyncTask时覆盖onPostExecute方法即可用几行代码完成。这里有一个例子:

new AasyncTask()
{
    @Override public void onPostExecute(String result)
    {
       // do whatever you want with result 
    }
}.execute(a.targetServer);

我希望这对你有所帮助,愉快地编码 :)

它对我不起作用。它只是没有进入“onPostExecute”方法。 - Francisco Romero

6
在你的OnCreate()方法中:
`
myTask.execute("url");
String result = "";
try {
      result = myTask.get().toString();
} catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
}catch (ExecutionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();

}`


4

为什么人们把它搞得这么难。

这应该足够了。

不要在异步任务上实现onPostExecute,而是在Activity上实现:

public class MainActivity extends Activity 
{

@Override
public void onCreate(Bundle savedInstanceState) {

    //execute the async task 
    MyAsyncTask task = new MyAsyncTask(){
            protected void onPostExecute(String result) {
                //Do your thing
            }       

    }

    task.execute("Param");

}


}

这对我很有效。它是最简单和最易于理解的。我认为它利用了Java设计的功能。谢谢。附注:添加@Override是否必要? - Old Geezer
1
不,@Override只是一个注解。这样你就告诉Java编译器你打算重写这个方法,并在你没有这样做时得到通知。 - bugdayci

4
你可以调用 AsyncTaskget() 方法(或重载的 get(long, TimeUnit) 方法)。该方法将会阻塞直到 AsyncTask 完成其工作,然后它将返回 Result
在创建/启动异步任务和调用 get 方法之间进行其他工作是明智的,否则你无法有效地利用异步任务。

3
因为get()会阻塞主线程,所以被踩了。如果它会导致阻塞,那就没有使用AsyncTask的理由。 - GabrielBB

3
你可以编写自己的监听器。与HelmiB的答案相同,但看起来更自然:
创建监听器接口:
public interface myAsyncTaskCompletedListener {
    void onMyAsynTaskCompleted(int responseCode, String result);
}

然后编写您的异步任务:
public class myAsyncTask extends AsyncTask<String, Void, String> {

    private myAsyncTaskCompletedListener listener;
    private int responseCode = 0;

    public myAsyncTask() {
    }

    public myAsyncTask(myAsyncTaskCompletedListener listener, int responseCode) {
        this.listener = listener;
        this.responseCode = responseCode;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }


    @Override
    protected String doInBackground(String... params) {
        String result;
        String param = (params.length == 0) ? null : params[0];
        if (param != null) {
            // Do some background jobs, like httprequest...
            return result;
        }
        return null;
    }

    @Override
    protected void onPostExecute(String finalResult) {
        super.onPostExecute(finalResult);
        if (!isCancelled()) {
            if (listener != null) {
                listener.onMyAsynTaskCompleted(responseCode, finalResult);
            }
        }
    }
}

最后在活动中实现监听器:
public class MainActivity extends AppCompatActivity implements myAsyncTaskCompletedListener {

    @Override
    public void onMyAsynTaskCompleted(int responseCode, String result) {

        switch (responseCode) {
            case TASK_CODE_ONE: 
                // Do something for CODE_ONE
                break;
            case TASK_CODE_TWO:
                // Do something for CODE_TWO
                break;
            default: 
                // Show some error code
        }        
    }

这是调用asyncTask的方法:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Some other codes...
        new myAsyncTask(this,TASK_CODE_ONE).execute("Data for background job");
        // And some another codes...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接