I have a Firebase JobDispatcher in my app that is set up to run an AsyncTask. The JobDispatcher is created:
dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(getContext())) ;
monitorEggJob = dispatcher.newJobBuilder()
.setService(EggMonitorJobService.class)
.setTag(JOB_TAG)
.setLifetime(Lifetime.UNTIL_NEXT_BOOT)
.setRecurring(false)
.setReplaceCurrent(true)
.setConstraints(Constraint.ON_ANY_NETWORK)
.build() ;
dispatcher.mustSchedule(monitorEggJob);
When the "Stop" event of the fragment that started this JobDispatcher is called it cancels the JobService:
int result ;
if ( dispatcher != null )
{
result = dispatcher.cancel(JOB_TAG) ;
Log.d(TAG, "Dispatcher cancelled Egg Monitor Task (Result = " + result + "(");
}
The result is "0", meaning success.
However when the app has been closed, after a period of time the JobService runs the AsyncTask again and a exception occurs:
Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
Also, if I look at the "App Info" after the app has been closed completely, not just on the back stack, the "force Stop" option is available, indicating the Dispatcher is still running I think.
My question is, how can I ensure the dispatcher is killed in the "onStop" event. The service is only required while the app is running in foreground.
*********** Add EggMonitorService code ************
public class EggMonitorJobService extends JobService {
private EggMonitorTask eggMonitorTask ;
private boolean keepGoing ;
private JobParameters jobParameters ;
@Override
public boolean onStartJob(JobParameters job) {
jobParameters = job ;
keepGoing = true ;
eggMonitorTask = new EggMonitorTask();
eggMonitorTask.execute() ;
return true;
}
@Override
public boolean onStopJob(JobParameters job) {
//keepGoing = false ;
if ( eggMonitorTask != null ) {
eggMonitorTask.cancel(true) ;
}
return false;
}
private class EggMonitorTask extends AsyncTask<Void, Void, Void> {
public static final String TAG = "EggMonitorTask" ;
@Override
protected void onPreExecute() {
Log.d(TAG, "Starting ...");
super.onPreExecute();
}
@Override
protected Void doInBackground(Void... voids) {
final DatabaseReference eggsReference = FirebaseDatabase.getInstance().getReference().child("eggs") ;
Query query = eggsReference.orderByChild("userKey") ;
final ValueEventListener listener = new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
//
List<EggModel> eggs = new ArrayList<>() ;
for ( DataSnapshot snapshot : dataSnapshot.getChildren() ) {
EggModel egg = snapshot.getValue(EggModel.class) ;
eggs.add(egg) ;
}
if ( eggs.size() > 0 ) {
for ( EggModel item : eggs ) {
//
// Run the hatch date and write
// any updated data to database
//
if ( item.updateHatchSoon() ) {
//
// Update database
//
eggsReference.child(item.getDbKey()).setValue(item) ;
}
}
}
//
// All done
//
keepGoing = false ;
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
} ;
query.addValueEventListener(listener) ;
//
// Sleep until all the work has been completed,
// check for completion periodically.
//
while(keepGoing) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
query.removeEventListener(listener);
return null;
}
//
// There is no return data, this is here for
// debug only
//
@Override
protected void onPostExecute(Void aVoid) {
Log.d(TAG, "Ending ...");
jobFinished(jobParameters, keepGoing) ;
}
}
}
Thanks. Sid
If
AsyncTask
is cancelled,onCancelled()
is called instead ofonPostExecute()
. CalljobFinished()
from there:Also, handle errors in
ValueEventListener
: