I have made a foreground service that work with a runnable object that is sheduled after some time. It runs for a long time. At first I had this code into the service:
private final Handler handler = new Handler();
...
...
public int onStartCommand(...
handler.postDelayed(sendUpdatesToUI, 50);
...
private Runnable sendUpdatesToUI = new Runnable() {
public void run() {
try {
DBAdapter DB = new DBAdapter(MyService.this);
DB.open();
DB.insertData(System.currentTimeMillis(), "",
cronometro.tiempo_original, 0) ;
DB.close();
} catch (Exception e) {
Toast.makeText(MyService.this, e.toString(),Toast.LENGTH_LONG).show();
}
SendInfo();
handler.postDelayed(this, 300000);
}
};
But the above code has a problem: Handler runs in the UI thread, so if the UI activity is not in memory the postdelayed function does not work as the working thread is no longer active. So I changed the code to use a system sheduler in between activity specific scheduler (handler). Now the code look like this:
private final ScheduledExecutorService schedulerService = Executors.newScheduledThreadPool(1);
private ScheduledFuture scheduleFuture;
...
...
public int onStartCommand(...
scheduleFuture = schedulerService.schedule(sendUpdatesToUI,50, TimeUnit.MILLISECONDS);
...
private Runnable sendUpdatesToUI = new Runnable() {
public void run() {
try {
DBAdapter DB = new DBAdapter(MyService.this);
DB.open();
DB.insertData(System.currentTimeMillis(), "",
cronometro.tiempo_original, 0) ;
DB.close();
} catch (Exception e) {
Toast.makeText(MyService.this, e.toString(),Toast.LENGTH_LONG).show();
}
SendInfo();
scheduleFuture = schedulerService.schedule(sendUpdatesToUI,300000,TimeUnit.MILLISECONDS);
}
};
This second code is the correct way to do it for a long service which activity is not going to be in the front.
As you can see the code do the same, but it avoid the problem of using the handler, which depent on the main UI thread.
If I comment the SQL code for both, they run perfect as long as main activity is on top. But if I dont comment sql code, the second sample hang and not run. It stops at sqlcode because DatabaseHelper need a valid context, and as executors is a system context, it does not run. Any context I put in DBAdapter(context) (like getApplicationContext(), getBaseContext() or MyService.this) does not work.
Does anyone know how can I provide a valid context to the second example?
Services share the same main application thread (“UI thread”) as activities. AFAIK, this should “work” regardless of UI state. It is a poor implementation for other reasons (see below).
This is incrementally better than the first implementation. Both are poor. Please use
AlarmManagerand anIntentService, so that you are not wasting the user’s resources while your service is not doing anything for five minutes.Your claim makes no sense. You are using the
Serviceobject, which is a validContext, so long as theServiceis running. AScheduledExecutorServiceis an ordinary Java object and cannot supply aContext. I have no idea what you think a “system context” is.Then you have some other problem, such as a synchronization/deadlock problem with multiple threads accessing the database, or perhaps your
Serviceis destroyed (and, hence, you leaked this background thread).If you use
AlarmManagerand anIntentService, and you do your database I/O inonHandleIntent(), using aSQLiteOpenHelperthat is shared between all components (this service and your activities), you should have better results.