我需要我的Android应用程序使用AJAX调用定期从服务器获取数据,并相应地更新UI(只需要使用TextView
更新一堆setText()
)。请注意,这涉及2个任务:
AsyncTask
。我还没有想出一个优雅的方法来实现上面的第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
);
}
当我使用它时,我应该注意哪些反面?特别是,我在LogCat中看到很多GC正在发生。另外,我想知道除非AsyncTask
完成,否则onPostExecute()
如何成为GC的候选者?
如何“停止”更新?我想到的一种方法是将第一个AsyncTask
实例作为Activity
的成员变量。这样,我可以在其上调用cancel(true)
并希望这将“停止”任务。
如果有人在寻找类似的东西 - 我在这里提到的解决方案都没有令人满意的效果。他们都遇到OutOfMemory
个问题。我没有调试OOM的细节,但我怀疑它可能是因为递归,或者是因为在AsyncTask
中将HTTP相关对象作为成员变量而不是{{1}的成员(基本上是因为没有重用HTTP和其他对象)。
我放弃了另一种方法 - 在我Activity
的{{1}}中无休止地调用我的Ajax;并更新doInBackground()
中的用户界面。这样我也可以避免维护太多线程或AsyncTask
来更新UI的开销(请记住UI可以在onProgressUpdate()
中更新)。
这种方法也消除了对Handler
和onProgressUpdate()
的需求,而更倾向于使用Timer
。 This thread on SO还有更多详细信息和代码段。
答案 0 :(得分:1)
在任何postDelayed()
上调用View
以安排在一段延迟后在主应用程序线程上运行的代码块。在onPostExecute()
的{{1}}中执行此操作,以创建并执行另一个AsyncTask
。
你可以使用AsyncTask
,正如其他人所引用的那样,但我同意你的看法,就像在一项活动中纯粹出现的时间感觉有点过分。
话虽如此,如果AJAX调用应该发生而不管活动是否存在,那么一定要考虑切换到AlarmManager
和AlarmManager
。
答案 1 :(得分:0)
我认为这样做的方法是使用AlarmManager。或者您也可以使用基本的Java Timer。我推荐使用AlarmManager。
将其设置为使用自定义Action发送一些意图,并为其注册广播接收器。
答案 2 :(得分:0)
如果ajax调用仅在活动中执行,您可以在启动任务的活动中使用计时器。
否则使用一个使用AlarmManager并通过广播连接到gui的服务。
答案 3 :(得分:0)
执行重复任务的推荐方法是通过AlarmManager,正如Scythe所暗示的那样。基本上它涉及设置广播监听器,并让AlarmManager以您选择的任何间隔触发对该监听器的意图。然后,您可以让广播侦听器调用该活动来运行AsyncTask。如果你需要一个非常紧张的计时器(我会说不到5秒的电话),那么你最好在服务中使用一个计时器,并使用AIDL回叫活动。
您可以设置一个可以戳戳的IntentService,并使用AIDL来更新活动,而不是直接与广播意图交谈。
答案 4 :(得分:0)
这就是我最终实现的目标。请注意,由于递归,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);
}
}
}
}