【问题标题】:Android: Implication of using AsyncTask to make repeated Ajax CallsAndroid:使用 AsyncTask 进行重复 Ajax 调用的含义
【发布时间】:2011-07-13 11:48:59
【问题描述】:

我需要我的 Android 应用定期使用 AJAX 调用从服务器获取数据,并相应地更新 UI(只是一堆需要使用 setText() 更新的 TextViews)。请注意,这涉及 2 个任务:

  1. 进行 AJAX 调用,并在收到响应后更新 UI - 为此我使用了一个简单的 AsyncTask
  2. 定期重复上述操作。

我还没有找到实现上述第 2 点的优雅方法。目前,我只是从OnPostExecute() 执行任务本身。我在this thread at 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. 当我使用它时,是否有任何反面需要注意?特别是,我看到 LogCat 中发生了很多 GC。另外,我想知道除非onPostExecute() 完成,否则AsyncTask 如何成为GC 的候选人?

  2. 如何“停止”更新?我想到的一种方法是将第一个AsyncTask 实例作为Activity 的成员变量。这样,我可以在其上调用 cancel(true) 并希望这将“停止”任务。


解决方案

如果有人正在寻找类似的东西 - 我在这里提到的解决方案都不能令人满意。他们都遭受OutOfMemory 问题的困扰。我没有调试 OOM 的细节,但我怀疑这可能是因为递归,或者因为 HTTP 相关对象作为 AsyncTask 中的成员变量而不是 Activity 的成员(基本上因为没有重用 HTTP 和其他对象)。

我放弃了这种方法,换了一种方法——在我的AsyncTaskdoInBackground() 中无休止地进行Ajax 调用;并更新onProgressUpdate() 中的用户界面。这样我也避免了维护太多线程或Handlers 来更新 UI 的开销(记住 UI 可以在 onProgressUpdate() 中更新)。

这种方法还消除了对Timers 和TimerTasks 的需要,而倾向于使用Thread.sleep()This thread on SO 有更多细节和代码 sn-p。

【问题讨论】:

  • 我在上面的编辑中提到的“解决方案”有严重的缺陷——正如怀疑的那样,它会导致“无限循环”。在onPauseonDestroy() 中调用cancel(true) 并没有成功——它从不调用我在AsyncTask 中覆盖的onCancelled()。我想不出一种方法来使用isCancelled() 方法来影响doInBackground() 内部的行为。我想我将不得不采用 AlarmManager 的方式。我试试看。

标签: android android-asynctask timertask


【解决方案1】:

在任何View 上调用postDelayed() 以安排一大块代码在一定延迟后在主应用程序线程上运行。在AsyncTaskonPostExecute() 中执行此操作以创建并执行另一个AsyncTask

您可以使用 AlarmManager,正如其他人所引用的那样,但我同意您的看法,对于纯粹发生在活动中的时间安排感觉有点矫枉过正。

话虽如此,如果无论活动是否存在都应该发生 AJAX 调用,请务必考虑切换到 AlarmManagerIntentService

【讨论】:

  • 感谢您指出postDelayed()。它可能比我在已编辑问题中发布的TimerTask 解决方案效果更好。我试试看。
  • 这仍然意味着我必须从当前任务中开始执行新的 AsyncTask,不是吗?它不会和我尝试的解决方案有同样的“无限循环”陷阱吗? (请参阅对原始问题的评论)。
  • @techie.curious:在 Java 中取消线程从未如此简单或可靠,恕我直言。只需为自己设置一个标志,完成后不要调用postDelayed() 或执行下一个AsyncTask
  • 感谢@CommonsWare 成功了。我最终不得不使用最原始的线程间通信形式——在onPause()onDestroy() 上设置一个标志并更改其值。在我的onPostExecute() 中启动一个新的 AyncTask 实例之前,我正在检查这个标志的值。
【解决方案2】:

我认为执行此操作的 android 方法是使用 AlarmManager。或者您也可以使用基本的 java Timer。我会推荐 AlarmManager。

将其设置为使用自定义操作发送一些意图,并为其注册广播接收器。

【讨论】:

  • 感谢您的指点。我不相信AlarmManager 对我来说是正确的解决方案,因为我不希望从服务中进行 Ajax 调用。我将查看Handler,并尽量避免从处理程序更新 UI。相反,我将尝试从处理程序执行 AsyncTask。
  • 我没有说你必须使用服务。正确设置警报管理器->警报管理器定期发送意图->您在处理这些意图的活动中动态注册广播接收器->在此接收器中启动异步任务。这里不需要服务。不要忘记在 onStop 或 onPause 中取消注册接收器,如果不需要,请禁用警报管理器。
【解决方案3】:

如果仅在 Activity 中执行 ajax 调用,您可以在启动任务的 Activity 中使用计时器。

否则使用使用 AlarmManager 并通过广播连接到 gui 的服务。

【讨论】:

  • 在我发布这个问题之后,我意识到以“定期”间隔执行任务不是一个好主意 - 这是因为 AJAX 的性质。到下一个请求发出时,我什至可能还没有得到回应!我倾向于从OnPostExecute 触发AsyncTask 的想法,但要经过一个固定的延迟。我查看了AlarmManager,但这似乎有点过头了,因为我需要仅在活动运行时才进行 Ajax 调用。我可能会看看Handler
【解决方案4】:

推荐的重复任务方法是通过AlarmManager,正如 Scythe 所暗示的那样。基本上,它涉及设置广播侦听器,并让 AlarmManager 以您选择的任何时间间隔向该侦听器触发意图。然后,您将让广播侦听器调用活动以运行 AsyncTask。如果您需要一个非常紧凑的计时器(我会说少于 5 秒的调用),那么您最好在服务中使用计时器,并使用 AIDL 回调活动。

除了直接从广播 Intent 对话之外,您还可以设置一个可以戳的 IntentService,并使用 AIDL 来更新 Activity。

【讨论】:

    【解决方案5】:

    这就是我最终实现它的方式。请注意,由于递归,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);
    
                }
    
            }
    
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-03
      • 1970-01-01
      • 2011-10-22
      • 1970-01-01
      • 2014-09-02
      • 1970-01-01
      相关资源
      最近更新 更多