Reputation: 185
I am trying to create simple Android program that pulls web server every 30*1000 milliseconds. I have dedicated service in my application - PollerService
Android Manifest.xml:
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".PullServerAndListenActivivty"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".PollerService" />
</application>
The class creates scheduled executor and registers its timer task to start calling the Engine which in order pulls the server.
PollerService.java:
public class PollerService extends Service {
public static final int INTERVAL = 20*1000;
private ScheduledExecutorService executor;
public void onCreate() {
super.onCreate();
executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(new TimerTask() {
public void run() {
Engine.tick ();
}
}, 0, INTERVAL, TimeUnit.MILLISECONDS);
}
public void onDestroy() {
executor.shutdownNow();
}
public IBinder onBind(Intent arg0) {
return null;
};
}
The Engine just creates AsyncTask subclass instance in GUI thread and executes it. onPostExecute writes a line through a console which was assigned in Activity and calls UI components.
public class Engine {
private static Console console; //Assigned in Activity onCreate
private static Activity context;//Assigned in Activity onCreate
...
public static void tick() {
final String requestURL = String.format(urlFormat, serverURL);
try {
context.runOnUiThread(
new Runnable () {
public void run() {
new RequestTask().execute(requestURL);
};
}
);
}
catch (Throwable e) {
Log.e("SBOX", e.getMessage(), e);
}
}
public static void processCommand(String result) {
try {
final Command command = fromJSONToCommand(result);
context.runOnUiThread(new Runnable() {
public void run() {
console.writeLine("Receieved command: " + command);
}
});
} catch (Exception e) {
Log.e ("SBOX", e.getMessage(), e);
}
}
Yak, I started from simple RequestTask instance creation, I was under impression that it will run on UI thread by itself but trying to fix the problem I finally came to this approach, still useless. Just want to say that I tried a lot here.
RequestTask looks like this, simple http call:
class RequestTask extends AsyncTask<String, Void, String>{
@Override
protected String doInBackground(String... uri) {
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response;
String responseString = null;
try {
response = httpclient.execute(new HttpGet(uri[0]));
StatusLine statusLine = response.getStatusLine();
if(statusLine.getStatusCode() == HttpStatus.SC_OK){
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.getEntity().writeTo(out);
out.close();
responseString = out.toString();
} else{
response.getEntity().getContent().close();
throw new IOException(statusLine.getReasonPhrase());
}
} catch (Exception e) {
Log.e("SBOX", e.getMessage(), e);
}
return responseString;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
Engine.processCommand (result);
}
}
So the problem is that it runs properly only once, timer schedules, calls first time the Engine, it runs RequestTask, it pulls server, gets the responce, prints it in the console. But second time it gets into the timer method and then no signs of life from the service. No exceptions in LogCat, logging in RequestTask is not printed.
Of course its time to get to debug, but probably someone can save me from this pain and show errors in the code? The executor itself works perfectly continuous if to comment Engine.tick() call.
Will be very greatful for clues on resolution.
Upvotes: 1
Views: 2421
Reputation: 1006914
I am trying to create simple Android program that pulls web server every 30*1000 milliseconds.
You sure are going about it the hard way.
Step #1: Set up a real communications channel between your activity and your service. Do not use a static data member, as you are creating a possible memory leak. There are plenty of candidates (createPendingResult()
, Messenger
, etc.). For the sake of this answer, I will assume that you are going to use a broadcast Intent
, so choose some unique action string and set up a BroadcastReceiver
in onResume()
in your activity to listen for such a broadcast (and remove that receiver in onPause()
).
Step #2: Create a pollServer()
method in your service that has the code from your doInBackground()
method from your RequestTask
. However, rather than returning a string, have it send the broadcast Intent
, with your string as an extra.
Step #3: Call pollServer()
from your ScheduledExecutorService
, instead of Engine.tick()
.
Step #4: Delete Engine
and RequestTask
entirely.
Upvotes: 6
Reputation: 26981
In the documentation on AsyncTask it gives the following as a rule related to threading:
The task can be executed only once (an exception will be thrown if a second execution is attempted.)
All this means is that you have to create a new instance of the class every time you want to use it. In other words, it must be done like this:
new DownloadFilesTask().execute(url1, url2, url3);
new DownloadFilesTask().execute(url4, url5, url6);
Or conversely, you can NOT do the following:
DownloadFilesTask dfTask = new DownloadFilesTask();
dfTask().execute(url1, url2, url3);
dfTask().execute(url4, url5, url6);
Upvotes: 2