Android异步任务:发送回调到用户界面

157

我有一个AsyncTask类,它并不在Activity中。在Activity中,我正在初始化AsyncTask,并且我希望AsyncTask向我的Activity报告回调。 这可能吗?或者AsyncTask必须与Activity在同一个类文件中吗?

protected void onProgressUpdate(Integer... values) 
{
    super.onProgressUpdate(values);
    caller.sometextfield.setText("bla");
}

像这样吗?


我不知道这个。有时候,这可能会对你有所帮助AsyncTask示例 - Athira Reddy
4个回答

411
你可以创建一个interface,将其传递给AsyncTask(在构造函数中),然后在onPostExecute()中调用方法。
例如:
您的接口:
public interface OnTaskCompleted{
    void onTaskCompleted();
}

您的活动:

public class YourActivity implements OnTaskCompleted{
    // your Activity
}

还有你的AsyncTask:

public class YourTask extends AsyncTask<Object,Object,Object>{ //change Object to required type
    private OnTaskCompleted listener;

    public YourTask(OnTaskCompleted listener){
        this.listener=listener;
    }

    // required methods

    protected void onPostExecute(Object o){
        // your stuff
        listener.onTaskCompleted();
    }
}

编辑

由于这个答案越来越受欢迎,我想添加一些内容。

如果您是Android开发的新手,AsyncTask是一种快速的方法,可以在不阻塞UI线程的情况下使事情工作。它确实解决了一些问题,它本身的工作方式没有什么错。然而,它也带来了一些影响,例如:

  • 可能会出现内存泄漏的情况。如果您保留对Activity的引用,则即使用户离开屏幕(或旋转设备),它仍将保留在内存中。
  • 如果Activity已经被销毁,AsyncTask不会向Activity提供结果。您必须添加额外的代码来管理所有这些东西,或者执行两次操作。
  • Activity中编写的复杂代码

当您感到足够成熟,准备继续进行Android开发时,请查看这篇文章,我认为这是使用异步操作开发Android应用程序的更好方式。


3
@DmitryZaitsev提出的建议非常好,我认为这是针对非私有内部类AsyncTask问题的最佳解决方案。 - WiZarD
2
@DmitryZaitsev 很好的回答。但请告诉我如何根据您的代码在我的活动中调用Asyntask? - Qadir Hussain
1
我正在尝试使用新的HttpCallsAsyncTask(context, null, null, // 接口对象).execute(); 如何在这种情况下传递接口对象?建议我传递接口对象,因为你已经声明了默认构造函数并且需要接口对象。请帮忙。 - Qadir Hussain
2
异步任务在这里不应该使用弱引用吗?否则任务可能会泄漏活动。 - JacksOnF1re
1
@JacksOnF1re 一方面,当然应该这样做。另一方面,我认为这会显著复杂化答案,因为我假设它主要是由 Android 新手阅读的。最后,我不认为 AsyncTask 是一个好选择。使用它存在相当多的问题,所以我不认为它值得任何“改进”。即使你添加了 WeakReferece,你仍然会遇到其他生命周期问题。 - Dmitry Zaytsev
显示剩余15条评论

49

我觉得以下方法非常简单。

我已经声明了一个回调接口。

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);
    }

}

在Activity类中点击按钮时调用异步任务。

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[] { "Youe request to aynchronous task class is giving here.." });

            }
        });
    }
}

谢谢


再次出现了内存泄漏的情况。 - user1530779
我正在寻找确切的解决方案,并找到了这个线程,但我不理解为什么要做上述所有事情的原因。在这种情况下,asynctask在MainActivity中执行,其中访问UI不是问题。同样可以通过在asynctask的onPostExecute()方法中更新UI来实现,而无需使用特殊接口。希望解决的问题是从另一个非活动类触发异步任务并从那里更新UI。 - user_noname_00
和你一样,这是像我这样的初学者所能得到的最好答案。 - Aesthetic
@user1530779 为什么这是一个内存泄漏的情况? - user3764893
如果一个Activity实现了接口并在AsyncTask完成之前被销毁(例如由于方向更改),则AsyncTask会对该Activity保持强引用。这会导致垃圾回收器无法回收该Activity对象。我建议的解决方案非常简单:在Activity的onDestroy()方法中将委托引用设置为null。 - Chris

6

除了上面的答案,您还可以为每个异步调用自定义回退,这样对通用异步方法的每次调用都会填充不同的数据,这取决于您在onTaskDone中放置的内容。

  Main.FragmentCallback FC= new  Main.FragmentCallback(){
            @Override
            public void onTaskDone(String results) {

                localText.setText(results); //example TextView
            }
        };

new API_CALL(this.getApplicationContext(), "GET",FC).execute("&Books=" + Main.Books + "&args=" + profile_id);

提醒:我在主活动上使用了接口,这就是为什么“Main”出现的地方,像这样:
public interface FragmentCallback {
    public void onTaskDone(String results);


}

我的API执行后像这样:
  @Override
    protected void onPostExecute(String results) {

        Log.i("TASK Result", results);
        mFragmentCallback.onTaskDone(results);

    }

API构造函数如下所示:
 class  API_CALL extends AsyncTask<String,Void,String>  {

    private Main.FragmentCallback mFragmentCallback;
    private Context act;
    private String method;


    public API_CALL(Context ctx, String api_method,Main.FragmentCallback fragmentCallback) {
        act=ctx;
        method=api_method;
        mFragmentCallback = fragmentCallback;


    }

3
这个想法很好,但是要小心使用匿名实现的 Main.FragmentCallback - 这可能会导致 Activity 泄漏。考虑使用 WeakReference 来解决这个问题。 - Dmitry Zaytsev
1
这个问题被问到的原因是,如果在Asynctask正在运行时销毁了Activity,那么上面两个答案都会泄漏Activity。因为Asynctask是一个匿名内部类,它引用了你的Activity,这是一个隐式引用。 - user1530779

3
我会重复其他人说的话,但会尽量简化...
首先,只需创建接口类。
public interface PostTaskListener<K> {
    // K is the type of the result object of the async task 
    void onPostTask(K result);
}

其次,创建AsyncTask(可以是活动或片段的内部静态类),使用接口,包括一个具体类。在示例中,PostTaskListener被参数化为String,这意味着它期望异步任务的结果是String类。

public static class LoadData extends AsyncTask<Void, Void, String> {

    private PostTaskListener<String> postTaskListener;

    protected LoadData(PostTaskListener<String> postTaskListener){
        this.postTaskListener = postTaskListener;
    }

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

        if (result != null && postTaskListener != null)
            postTaskListener.onPostTask(result);
    }
}

最后,是将你的逻辑组合在一起的部分。在你的Activity/Fragment中,创建PostTaskListener并将其传递给异步任务。以下是一个示例:

...
PostTaskListener<String> postTaskListener = new PostTaskListener<String>() {
    @Override
    public void onPostTask(String result) {
        //Your post execution task code
    }
}

// Create the async task and pass it the post task listener.
new LoadData(postTaskListener);

完成了!


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