I have a JobIntentService
that is launched every time a push notification comes in to tell the service to go fetch more data. While the app is in the foreground everything works as it should.
When the app is in the background and multiple push notifications come in, the intent gets queued up and it executes the same intent multiple times putting unneeded stress on the server because the first call to the server will get all the information it needs making the other queued intent's from the push notifications unnecessary.
Is there anyway to cancel or not add the same intent to the queue or some other way to prevent the extra calls to the server?
This class is deprecated. This class has been deprecated in favor of the Android Jetpack WorkManagerlibrary, which makes it easy to schedule deferrable, asynchronous tasks that are expected to run even if the app exits or the device restarts.
This class was deprecated in API level 30. IntentService is subject to all the background execution limits imposed with Android 8.0 (API level 26). Consider using WorkManager or JobIntentService , which uses jobs instead of services when running on Android 8.0 or higher.
IntentService has never been designed to be cancelled, hence the lack of any API to cancel a JobIntentService . Therefore, JobIntenService should only be used for work that don't need to be cancelled. Note that JobIntentService can still be used for work that can fail, and consequently, be aborted.
IntentService in Android is a base class for Services to handle asynchronous requests that are expressed in the form of Intents when there is a demand to do so. The requests are sent by the clients through Context. startService(Intent) calls to start the service.
We learned that the JobIntentService is not only deprecated but can also be a danger for your app stability because of an unsolved bug related to this component. As we saw, the migrated version of JobIntentService as Worker is not that different from its origin and therefore quite easy to do.
The JobIntentService combines 2 different types of services: the IntentService and the JobService. Since Android Oreo (API 26), background services can’t keep running while the app itself is in the background.
Work is sent to an IntentService by instantiating an Intent and then calling the StartService method with that Intent as a parameter. The Intent will be passed to the service as a parameter in the OnHandleIntent method. This code snippet is an example of sending a work request to an Intent:
The same as in the IntentService’s onHandleIntent method, all the incoming intents are executed sequentially on a background thread in the JobIntentService’s onHandleWork method, and we can send data to it in form of intent extras.
So first a couple of already similar questions with lots of good knowledge: question1 and question2. Maybe your question even duplicates those similar.
The cleanest would be to get your hands on JobIntentService's jobs queue in your MyOwnJobIntentService extends JobIntentService
.
In androidx.core.app.JobIntentService.java
there is:
final ArrayList<CompatWorkItem> mCompatQueue;
And in your MyOwnJobIntentService extends JobIntentService
:
if(mCompatQueue.isEmpty()){
//only in this case enqueue new job
}
But unfortunately mCompatQueue
is not public field.
And 10 minutes later we're getting with working solution -> SingleJobIntentService
a JobIntentService which will not queue jobs if it's already working.
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v4.app.JobIntentService;
import android.util.Log;
public class SingleJobIntentService extends JobIntentService {
private static final String TAG = "SingleJobIntentService";
private Intent theOnlyJobIhave = null;
public static void enqueue(Context context, Intent work) {
Log.d(TAG, "enqueue: someone tries to add me work " + work.hashCode());
JobIntentService.enqueueWork(
context,
SingleJobIntentService.class,
SingleJobIntentService.class.hashCode(),
work);
}
@Override
protected void onHandleWork(@NonNull final Intent theWorkIgot) {
Log.d(TAG, "onHandleWork: " + this.hashCode());
if (theOnlyJobIhave == null) {
theOnlyJobIhave = theWorkIgot;
final int extraValue = theOnlyJobIhave.getIntExtra(MainActivity.KEY, -500);
Log.d(TAG, "onHandleWork: " + extraValue);
try {
Thread.sleep(7000); //this simulates fetch to server
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
Log.d(TAG, "onHandleWork I'm already busy, refuse to work >:(");
}
Log.d(TAG, "onHandleWork end");
}
}
You may want test it with simple activity with a button:
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
public class MainActivity extends AppCompatActivity {
public static final String KEY = "KEYKEY";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
final Intent theIntent = new Intent();
theIntent.putExtra(KEY, 666);
SingleJobIntentService.enqueue(MainActivity.this, theIntent);
}
});
}
}
Notice: you must be careful with threading as the solution I gave is not thread-safe. For example in EDIT: after thinking about it -> since androidx.core.app.JobIntentService.java
touching mCompatQueue
is synchronized.onHandleWork
is called from JobIntentService from single thread there is no thread issue and the solution is thread safe!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With