Android:如果AsyncTask在单独的类中,如何从AsyncTask更新UI?

40

我讨厌内部类。

我有一个主活动,启动一个“短期”AsyncTask。

AsyncTask在一个单独的文件中,不是主活动的内部类。

我需要AsyncTask更新来自主Activity的textView。

我知道如果AsyncTask是一个内部类,我可以从onProgressUpdate更新TextView。

但是如何从一个外部、独立的异步任务更新呢?

更新:这似乎有效:

在Activity中调用该任务

backgroundTask = new BackgroundTask(this);
backgroundTask.execute();
在构造函数中,我已经
public BackgroundTask(Activity myContext)
{
    debug = (TextView) myContext.findViewById(R.id.debugText);
}

其中debug是AsyncTask的私有字段。

所以在onProgressUpdate方法中,我可以

debug.append(text);

感谢大家的建议。


你的意思是在另一个类中,你想要访问UI界面? - Hossein Mobasher
是的,我需要从AsyncTask更新TextView。 - realtebo
您可以在构造函数中传递Context,并将其转换为ActivityClass,然后使用runOnUIThread方法更改textView。 - Hossein Mobasher
7个回答

41

AsyncTask通常是从Activity中分离出来的一个单独的类,但我猜测您的意思是它在不同的文件中,因此您无法受益于它成为活动内部类。只需将活动上下文作为参数传递给您的异步任务(即传递给其构造函数)。

class MyAsyncTask extends AsyncTask<URL, Integer, Long> {

    WeakReference<Activity> mWeakActivity;

    public MyAsyncTask(Activity activity) {
       mWeakActivity = new WeakReference<Activity>(activity);
    }

 ...

当需要使用它时使用(记得在 doInBackground() 中不要使用)例如,当您通常调用时

int id = findViewById(...)

在AsyncTask中,您调用例如:

Activity activity = mWeakActivity.get();
if (activity != null) {
   int id = activity.findViewById(...);
}
请注意,我们的ActivitydoInBackground()进行时可能会消失(因此返回的引用可能会变为null),但是使用WeakReference并不会阻止GC对其进行回收(并且泄漏内存),并且当Activity消失时,通常甚至尝试更新它的状态也没有意义(不过根据您的逻辑,您可能需要执行类似于更改内部状态或更新DB之类的操作,但必须跳过触及UI的操作)。

2
如果您选择这条路线,最好将活动引用保持为WeakReference<Activity>而不仅仅是Activity。在我看来。 - newbyca
WebnetMobile.com,啊好的,我不确定,知道就好了。@realtebo,http://developer.android.com/reference/java/lang/ref/WeakReference.html - newbyca
1
在AsyncTask中持有UI元素的引用是一个不好的主意。WeakReference更好,但我推荐@Ams的答案,它使用接口进行通信。它描绘了松耦合,而不需要在AsyncTask本身中保留任何引用。同样的内容也在Android开发者视频中讲解 - https://www.youtube.com/watch?v=jtlRNNhane0&index=4&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE。 - Shubham A.

26

使用接口 1)创建一个接口

public interface OnDataSendToActivity {
    public void sendData(String str);
}

2) 在您的Activity中实现它

public class MainActivity extends Activity implements OnDataSendToActivity{

     @Override
     protected void onCreate(Bundle savedInstanceState) {
          new AsyncTest(this).execute(new String[]{"AnyData"}); // start your task
     }

     @Override
     public void sendData(String str) {
         // TODO Auto-generated method stub

     }

}

3) 在AsyncTask类中创建构造函数 AsyncTask(Activity activity){} 在AsyncTask文件中注册您的接口 并像下面这样调用接口方法。

public class AsyncTest extends AsyncTask<String, Integer, String> {

    OnDataSendToActivity dataSendToActivity;
    public AsyncTest(Activity activity){
        dataSendToActivity = (OnDataSendToActivity)activity;
    }

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

}
在AsyncTask完成所有任务后,您的OnPostExecute将被调用并将获得由doInBackground(){ return "";}返回的"result"参数。
当"dataSendToActivity.sendData(result);"时,它将调用活动的重写方法 "public void sendData(String str) {}"。
要记住的一个边缘情况是:确保将您当前活动的上下文传递给AsyncTask,而不是创建另一个活动实例,否则您的Activity将被销毁并创建新的实例。

您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - zionpi
这是一个好的解决方案。在这个Android开发者视频中,实现接口可以减少耦合,因此建议使用 - https://www.youtube.com/watch?v=jtlRNNhane0&index=4&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE。 - Shubham A.
这样做是否仍然保留对Activity的引用?dataSendToActivity不就是实现了OnDataSendToActivity接口的一个Activity吗? - zundi

5

在你的Activity类中创建一个静态函数,并将上下文传递给它以更新你的TextView,然后在你的AsynkTask类中调用此函数进行更新。

在Activity类中:

public static void updateTextView(){
    //在这里写你的代码
}

在AsynckTask类中调用此函数。


您的TextView需要是静态的,否则会造成内存泄漏。 - Trevor

3

只需将上下文(活动或其他内容)传递给构造函数中的AsyncTask,然后在onSuccess或onProgressUpdate中调用您需要在上下文中进行的任何操作。


1
那会导致内存泄漏。 - Trevor

3

我为这种情况编写了一个小的AsyncTask扩展。它允许您将AsyncTask保留在单独的类中,但也为您提供了方便的访问任务完成的方式:

public abstract class ListenableAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result>{

    @Override
    protected final void onPostExecute(Result result) {
        notifyListenerOnPostExecute(result);
    }

    private AsyncTaskListener<Result> mListener;
    public interface AsyncTaskListener<Result>{
        public void onPostExecute(Result result);
    }
    public void listenWith(AsyncTaskListener<Result> l){
        mListener = l;
    }
    private void notifyListenerOnPostExecute(Result result){
        if(mListener != null)
            mListener.onPostExecute(result);
    }

}

所以首先您需要扩展ListenableAsyncTask而不是AsyncTask。然后在您的UI代码中,创建一个具体的实例并设置listenWith(...)。

2

这个问题已经有了答案,但我还是要说一下应该怎么做。

Mainactivity类

  public class MainActivity extends Activity implements OnClickListener
    {

        TextView Ctemp;

        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Ctemp = (TextView) findViewById(R.id.Ctemp);
            doConv = (Button) findViewById(R.id.doConv);
            doConv.setOnClickListener(this);
        }

        @Override
        public void onClick(View arg0) // The conversion to do
        {
            new asyncConvert(this).execute();
        }
    }

现在在异步类中

public class asyncConvert extends AsyncTask<Void, Void, String>
{
    SoapPrimitive response = null;
    Context context;

    public asyncConvert(Context callerclass)
    {
        contextGUI = callerclass;
    }
.
.
.
.
protected void onPostExecute(String result)
    {
        ((MainActivity) contextGUI).Ctemp.setText(result); // changing TextView
    }
}

1
    /**
     * Background Async Task to Load all product by making HTTP Request
     * */
     public  static class updateTExtviewAsyncTask extends AsyncTask<String, String, String> {

        Context context;
        ProgressDialog pDialog;
        String id, name;

        String state_id;

        //--- Constructor for getting network id from asking method

        public  updateTExtviewAsyncTask(Context context,String id,String city) 
        {
            context   = context;
            state_id  = id;
            city_name = city;
        }       
        /* *
         * Before starting background thread Show Progress Dialog
         * */
        @Override
        protected void onPreExecute() 
        {
            super.onPreExecute();
            pDialog = ProgressDialog.show(context, "","Please wait...", true, true);
            pDialog.show();

        }

        /**
         * getting All products from url
         * */
        protected String doInBackground(String... args) 
        {
            return null;
        }

        /**
         * After completing background task Dismiss the progress dialog
         * **/
            protected void onPostExecute(String file_url)  {

                     YourClass.UpdateTextViewData("Textview data");
        }
    }

// 将此代码放置在您的活动类中,并声明更新文本视图为静态

    public static void  UpdateTextViewData(String tvData) 
{
   tv.setText(tvData);
}

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