Android: Implication of using AsyncTask to make repeated Ajax Calls

∥☆過路亽.° 提交于 2019-12-04 04:50:20

问题


I need my Android app to periodically fetch data from a server using AJAX calls, and update the UI accordingly (just a bunch of TextViews that need to be updated with setText()). Note that this involves 2 tasks:

  1. Making an AJAX call, and updating the UI once I receive a response - I use a simple AsyncTask for this.
  2. Doing the above repeatedly, at regular intervals.

I haven't figured out an elegant way to achieve Point 2 above. Currently, I am simply executing the task itself from OnPostExecute(). I read on this thread at SO that I need not worry about garbage collection as far as the AsyncTask objects are concerned.

But I'm still unsure as to how I set up a timer that will fire my AsyncTask after it expires. Any pointers will be appreciated. Here is my code:

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

    }

}

Thanks in advance

EDIT: I tried posting an answer but couldn't do it since I don't have the reputation to answer within 8 hours.

Well, so I found a solution. I'm not convinced however.

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. Are there any flip sides that I should be aware of when I use this? In particular, I am seeing lots of GCs happening in the LogCat. Also, I am wondering how an AsyncTask can be candidate for GC unless the onPostExecute() completes?

  2. How can I "stop" the updates? One way I thought of was to make the very first AsyncTask instance as a member variable of the Activity. That way, I can invoke cancel(true) on it and hope that this will "stop" the tasks.


SOLUTION:

In case anyone is looking for something similar - none of the solutions I mentioned here work satisfactorily. They all suffer from OutOfMemory issues. I did not debug into the details of the OOM, but I suspect it could either be because of the recursion, or because of having HTTP-related objects as member variables in the AsyncTask rather than as members of the Activity (basically because of NOT reusing HTTP and other objects).

I discarded this approach for a different one - making my Ajax Calls endlessly in the doInBackground() of my AsyncTask; and updating the UI in onProgressUpdate(). That way I also avoid the overhead of maintaining too many threads or Handlers for updating the UI (remember UI can be updated in onProgressUpdate() ).

This approach also eliminates the need for Timers and TimerTasks, favoring the use of Thread.sleep() instead. This thread on SO has more details and a code snippet too.


回答1:


Call postDelayed() on any View to schedule a hunk of code to be run on the main application thread after a certain delay. Do this in onPostExecute() of the AsyncTask to create and execute another AsyncTask.

You could use AlarmManager, as others have cited, but I would agree with you that it feels a bit like overkill for timing that occurs purely within an activity.

That being said, if the AJAX calls should be occurring regardless of whether the activity exists, definitely consider switching to AlarmManager and an IntentService.




回答2:


I think the android way to do this is using AlarmManager. Or you can user a basic java Timer as well. I'd recommend AlarmManager.

Set it up to send some intent with a custom Action, and register a broadcastreceiver for it.




回答3:


If the ajax calls are only executed in the activity you can just use a timer in the activity which starts the tasks.

Otherwise use a service which uses the AlarmManager and which connects to the gui via a broadcast.




回答4:


The recommended way to do a repeated task, is via AlarmManager, as alluded to by Scythe. Basically it involves setting up a broadcast listener, and having AlarmManager fire off an intent to that listener at whatever interval you choose. You then would have your broadcast listener call out to the activity to run the AsyncTask. If you need a very tight timer (less than 5s calls I'd say), then you're better off using a Timer within a Service, and using AIDL to call back to the activity.

Instead of talking directly from the broadcast intent, you could also setup an IntentService which you can poke, and use AIDL to update the activity.




回答5:


This is how I achieved it finally. Note that the AsyncTask cancel(true) method is useless in my scenario because of the recursion. I used what @CommonsWare suggested - used a flag to indicate whether any more tasks should be executed.

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

            }

        }

    }
}


来源:https://stackoverflow.com/questions/6534824/android-implication-of-using-asynctask-to-make-repeated-ajax-calls

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!