Android: использование AsyncTask для повторных вызовов Ajax - PullRequest
2 голосов
/ 30 июня 2011

Мне нужно, чтобы мое приложение для Android периодически извлекало данные с сервера с помощью вызовов AJAX и соответственно обновляло пользовательский интерфейс (просто набор TextView с, которые необходимо обновить с помощью setText()).Обратите внимание, что это включает в себя 2 задачи:

  1. Выполнение вызова AJAX и обновление пользовательского интерфейса после получения ответа - для этого я использую простой AsyncTask.
  2. Выполнение вышеуказанногонеоднократно, с регулярными интервалами.

Я не нашел элегантного способа достичь пункта 2 выше.В настоящее время я просто выполняю саму задачу из OnPostExecute().Я прочитал в этой теме на SO , что мне не нужно беспокоиться о сборке мусора в том, что касается объектов AsyncTask.

Но я все еще не уверен в том, как настроить таймерэто вызовет мой AsyncTask после истечения срока его действия.Любые указатели будут оценены.Вот мой код:

public class MyActivity extends Activity {


    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        new AjaxRequestTask().execute(MY_REST_API_URL);

    }

    private void updateReadings(String newReadings) {
           //Update the UI
        }

    class AjaxRequestTask extends AsyncTask<String, Integer, String> {

        @Override
        protected String doInBackground(String... restApiUrl) {
                    //Do AJAX Request
                }

        @Override
        protected void onPostExecute(String result) {
            updateReadings(result);
                     /*Is there a more elegant way to achieve this than create a new AsyncTask object every 10 seconds?  Also, How can I update the UI if I create a timer here? */
            new AjaxRequestTask().execute(MY_REST_API_URL);
        }

    }

}

Заранее спасибо

РЕДАКТИРОВАТЬ: я пытался опубликовать ответ, но не смог сделать это, так как у меня нетрепутация ответить в течении 8 часов.

Ну вот и я нашел решение.Я не уверен, однако.

protected void onPostExecute(String result) {

            updateReadings(result);
            // super.onPostExecute(result);
            new Timer().schedule(
                    new TimerTask() {
                        @Override
                        public void run() {
                            new AjaxRequestTask().execute(MY_REST_API_URL);
                        }
                    }, 
                    TIMER_ONE_TIME_EXECUTION_DELAY
            );
        }
  1. Есть ли какие-то оборотные стороны, о которых я должен знать, когда я использую это?В частности, я вижу много GC, происходящих в LogCat.Кроме того, мне интересно, как AsyncTask может быть кандидатом на сборщик мусора, если onPostExecute() не завершится?

  2. Как я могу "остановить" обновления?Один из способов, о котором я подумал, - создать самый первый экземпляр AsyncTask в качестве переменной-члена Activity.Таким образом, я могу вызвать cancel(true) и надеяться, что это «остановит» задачи.


РЕШЕНИЕ :

В случае, если кто-то ищет что-то подобное - ни одно из упомянутых здесь решений не работает удовлетворительно.Все они страдают от OutOfMemory проблем.Я не отлаживал детали OOM, но подозреваю, что это может быть либо из-за рекурсии, либо из-за того, что связанные с HTTP объекты являются переменными-членами в AsyncTask, а не членами Activity (в основномиз-за НЕ повторного использования HTTP и других объектов).

Я отбросил этот подход для другого - бесконечно совершать мои Ajax-вызовы в doInBackground() моего AsyncTask;и обновление пользовательского интерфейса в onProgressUpdate().Таким образом, я также избегаю лишних затрат на поддержание слишком большого количества потоков или Handler с для обновления пользовательского интерфейса (помните, что пользовательский интерфейс может быть обновлен в onProgressUpdate()).

Этот подход также устраняет необходимость в Timer си TimerTask с, предпочитая использовать вместо Thread.sleep(). Этот поток в SO содержит больше деталей и фрагмент кода.

Ответы [ 5 ]

1 голос
/ 30 июня 2011

Вызовите postDelayed() на любом View, чтобы запланировать кусок кода, который будет запускаться в главном потоке приложения после определенной задержки.Сделайте это в onPostExecute() из AsyncTask, чтобы создать и выполнить еще один AsyncTask.

. Вы можете использовать AlarmManager, как цитировали другие, но я согласен с вами, что это немного похоже наИзбыточное время для синхронизации, которая происходит исключительно внутри действия.

При этом, если вызовы AJAX должны выполняться независимо от того, существует ли действие, определенно рассмотрите возможность переключения на AlarmManager и IntentService.

0 голосов
/ 01 июля 2011

Вот как я наконец-то достиг этого.Обратите внимание, что метод AsyncTask cancel (true) в моем сценарии бесполезен из-за рекурсии.Я использовал то, что предложил @CommonsWare - использовал флаг, чтобы указать, нужно ли выполнять еще какие-либо задачи.

public class MyActivity extends Activity {

    /*Flag which indicates whether the execution should be halted or not.*/
    private boolean mCancelFlag = false;

    private AjaxRequestTask mAjaxTask;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        if(mAjaxTask == null){
            mAjaxTask = new AjaxRequestTask();
        }
        mAjaxTask.execute(MY_REST_API_URL);

    }
@Override
    protected void onResume() {
        super.onResume();
        mCancelFlag = false; /*when we resume, we want the tasks to restart. Unset cancel flag*/

        /* If the main task is Finished, create a new task and execute it.*/

        if(mAjaxTask == null || mAjaxTask.getStatus().equals(AsyncTask.Status.FINISHED)){
            new AjaxRequestTask().execute(TLS_REST_API_URL);
        }

    }       
    @Override
    protected void onPause() {
        mCancelFlag = true; /*We want the execution to stop on pause. Set the cancel flag to true*/
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        mCancelFlag = true;/*We want the execution to stop on destroy. Set the cancel flag to true*/
        super.onDestroy();
    }

    private void updateReadings(String result) {
          //Update the UI using the new readings.
    }

    class AjaxRequestTask extends AsyncTask<String, Integer, String> {

        private AjaxRequestTask mChainAjaxRequest;
        private Timer mTimer;
        private TimerTask mTimerTask;


        @Override
        protected String doInBackground(String... restApiUrl) {
            //Do AJAX call and get the response
            return ajaxResponse;
        }

        @Override
        protected void onPostExecute(String result) {
            Log.d(TAG, "Updating readings");
            updateReadings(result);
            // super.onPostExecute(result);
            if(mTimer == null){
                mTimer = new Timer();
            }

            if(!mCancelFlag){/*Check if the task has been cancelled prior to creating a new TimerTask*/
                if(mTimerTask == null){
                    mTimerTask = new TimerTask() {
                        @Override
                        public void run() {
                            if(!mCancelFlag){/*One additional level of checking*/
                                if(mChainAjaxRequest == null){
                                    mChainAjaxRequest = new AjaxRequestTask();
                                }
                                    mChainAjaxRequest.execute(MY_REST_API_URL);
                            }

                        }
                    };
                }
                mTimer.schedule(mTimerTask,TIMER_ONE_TIME_EXECUTION_DELAY);

            }

        }

    }
}
0 голосов
/ 30 июня 2011

Если вызовы ajax выполняются только в упражнении, вы можете просто использовать таймер в упражнении, которое запускает задачи.

В противном случае используйте службу, которая использует AlarmManager и которая подключается к графическому интерфейсу через широковещательную рассылку..

0 голосов
/ 30 июня 2011

Рекомендованный способ выполнить повторяющуюся задачу - через AlarmManager , на что ссылается Scythe. По сути, это включает настройку широковещательного прослушивателя и запуск AlarmManager намерением этого слушателя с любым интервалом, который вы выберете. Затем ваш слушатель широковещания должен вызвать действие, чтобы запустить AsyncTask. Если вам нужен очень жесткий таймер (я бы сказал, менее 5 секунд вызовов), вам лучше использовать Таймер в Сервисе и использовать AIDL для обратного вызова к действию.

Вместо того, чтобы говорить напрямую с целью трансляции, вы также можете настроить IntentService, который вы можете использовать, и использовать AIDL для обновления действия.

0 голосов
/ 30 июня 2011

Я думаю, что Android способ сделать это с помощью AlarmManager . Или вы можете использовать базовый таймер Java. Я бы порекомендовал AlarmManager.

Настройте его для отправки какого-либо намерения с помощью специального действия и зарегистрируйте для него широковещательный приемник.

...