Android: Как обновить пользовательский интерфейс из AsyncTask, если AsyncTask находится в отдельном классе?

Я ненавижу внутренний класс.

У меня есть основной вид деятельности, который запускает AsyncTask с коротким сроком службы.

AsyncTask находится в отдельном файле, не является внутренним классом основной активности

Мне нужно асинхронное задание обновляет текстовое представление из основной деятельности.

Я знаю, что могу обновить TextView из onProgressUpdate, если AsyncTask является внутренним классом

Но как из внешней, независимой, асинхронной задачи?

ОБНОВЛЕНИЕ: Это похоже на работу:

В деятельности я называю задачу

backgroundTask = new BackgroundTask(this);
backgroundTask.execute();

В конструкторе я

public BackgroundTask(Activity myContext)
{
    debug = (TextView) myContext.findViewById(R.id.debugText);
}

где отладка была частным полем AsyncTask.

Так что на ProgressUpdate я могу

debug.append(text);

Спасибо за все ваши предложения

7 ответов

Решение

РЕДАКТИРОВАТЬ Я отредактировал ответ, чтобы использовать WeakReference


AsyncTask всегда отдельный класс от Activity, но я подозреваю, что вы имеете в виду, что он находится в другом файле, чем ваш файл класса активности, поэтому вы не можете извлечь выгоду из того, что он является внутренним классом деятельности. Просто передайте контекст 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(...);
}

Обратите внимание, что наш Activity может быть в то время как doInBackground() выполняется (поэтому возвращаемая ссылка может стать null), но с помощью WeakReference мы не запрещаем GC собирать его (и тратить память), и когда активность исчезает, обычно бессмысленно даже пытаться обновить его состояние (тем не менее, в зависимости от вашей логики вы можете захотеть сделать что-то вроде изменения внутреннего состояния или обновления БД, но трогательный пользовательский интерфейс должен быть пропущен).

Использование интерфейса 1) Создайте один интерфейс

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

2) Реализует это в своей деятельности

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

}

Здесь ваш OnPostExecute будет вызывать после всей задачи, выполненной AsyncTask, и получит "result" в качестве параметра, возвращаемого doInBackground(){ return "";}.

Хотя "dataSendToActivity.sendData(result);" он будет вызывать переопределенный метод действия "public void sendData(String str) {}".

Крайний случай, чтобы помнить: обязательно пройти thisт. е. контекст вашей текущей деятельности AsyncTask а не создавать другой экземпляр вашей деятельности, иначе ваш Activity будет уничтожен и создан новый.

Создайте статическую функцию в вашем классе активности, передавая ему контекст, чтобы обновить текстовое представление, а затем вызовите эту функцию в вашем классе AsynkTask для обновления.

В классе Activity: открытая статическая void updateTextView () {

// ваш код здесь}

В классе AynckTask вызывайте эту функцию.

Просто передайте контекст (действие или что-то еще) вашей AsyncTask в конструкторе, а затем в onSuccess или onProgressUpdate вызовите все, что вам нужно в контексте.

Я написал небольшое расширение для 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. Затем в своем коде пользовательского интерфейса создайте конкретный экземпляр и установите listenWith(...).

Вопрос уже был дан ответ, я все еще пишу, как это должно быть сделано, я думаю..

Класс основной деятельности

  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
    }
}
    /**
     * 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");
        }
    }

// помещаем этот код в ваш класс активности, а также объявляем обновление статического textview

    public static void  UpdateTextViewData(String tvData) 
{
   tv.setText(tvData);
}
Другие вопросы по тегам