I have recently replaced all my service to foreground services and JobIntentService since there are some background execution limits (https://developer.android.com/about/versions/oreo/background) in oreo and above. As per documentation, JobIntentService acts like Intent Service for Android 7 & below and acts like JobScheduler for Android 8 & above. I have noticed there is an issue in new JobIntentService provided by Google.
Android 8 & above:
There is a crash happening continuously in android 8 and above. There was a ticket raised here mentioning about the same issue https://issuetracker.google.com/issues/63622293 and I have added a temp fix suggested by few geeks.
Android 7 & below:
JobIntentService which acts like Intent Service is not getting stopped once the work is done.
I have implemented JobIntentService within a service which triggers whenever some action is performed by a user.
Code
public class SampleJobIntentService extends FixedJobIntentService {
public static void postData(Context context, String data) {
Intent intent = new Intent(context, SampleJobIntentService.class);
intent.setAction(INITIAL_ACTION);
intent.putExtra(SAMPLE_ID, data);
SampleJobIntentService.enqueueWork(context,intent);
}
public static void enqueueWork(Context context, Intent work) {
SampleJobIntentService.enqueueWork(context, SampleJobIntentService.class, JOB_ID, work);
@Override
protected void onHandleWork(@NonNull Intent intent) {
if (intent != null) {
SampleRequest sampleRequest = requests.get(intent.getAction());
if (sampleRequest != null) {
try {
// perform some networking operations
} catch (Exception ex) {
Log.d("Error for intent ");
}
Log.i("send action ");
} else
Log.e("action not found for ");
}
}
}
To avoid the crash with JobIntentService, I took few references from https://issuetracker.google.com/issues/63622293
public abstract class FixedJobIntentService extends JobIntentService {
@Override
GenericWorkItem dequeueWork() {
try {
return new FixedGenericWorkItem(super.dequeueWork());
} catch (SecurityException ignored) {
doStopCurrentWork();
}
return null;
}
private class FixedGenericWorkItem implements GenericWorkItem {
final GenericWorkItem mGenericWorkItem;
FixedGenericWorkItem(GenericWorkItem genericWorkItem) {
mGenericWorkItem = genericWorkItem;
}
@Override
public Intent getIntent() {
if (mGenericWorkItem != null) {
return mGenericWorkItem.getIntent();
}
return null;
}
@Override
public void complete() {
try {
if (mGenericWorkItem != null) {
mGenericWorkItem.complete();
}
} catch (IllegalArgumentException ignored) {
doStopCurrentWork();
}
}
}
}
Well..., Its a lot big theory...!! It would not be able to put it all here. I will try my best which will make some your concepts clear.
I have already lost my 2 complete years in reading google documentations... Which are use-less
... With no proper documentation
and with no proper sample codes for its developers
..!! So i mention this in every of my posts on stack-overflow
, As it will help to save time of others..!!
It looks you are a good programmer; just need some hints to your posted question
:
Hint-1 :
YOU :- I have recently replaced all my service to foreground services and
JobIntentService
foreground service :
If you need ALL THE TIME RUNNING PROCESS; WHICH WILL NEVER END... ONCE IT IS STARTED
it is used in service which returns START_STICKY
from its OnStartCommand
. Which is again not advised to use as if you want to implement it at any cost ... then you will have to use a notification with setOngoing(true)
Which end user would not be able to swipe away your notification, it will remain there forever....
Use of the foreground service :
There has been restrictions on receivers too; above Oreo
onwards and you can not use all the receivers and intent actions by declaring it in manifest and by just making a receiver... I advice to just use BootComplete
permission and use a single receiver
which receives the boot_completed
intent and calls a service
if below O and calls a foreground service above O. Now from that foreground service you implement the runtime receivers for all and unregister it in Ondestroy methods. I have never found an official sample code for implementing runtime receiver and finally i have implemented it successfully by many months hard-work... Yes it was not a smart work due to google
When to use foreground service :
Only if you want to implement broadcast receivers.... If you do not want to implement any broadcast receivers; STAY AWAY.......
Hint-2 :
YOU :- I have recently replaced all my service to foreground services and
JobIntentService
** service has its quality of :**
Just doing a very tiny work... and just exit... it has to be exited by StopSelf()
... Again, Services can cause data-loss
if called multiple times... As same service thread can be run more than once... Again if you want a service to do a lot of work... Use START_STICKY
... But again it is not recommended and i have suggested already, when to use it in Hint 1.
** Intentservice has its quality of :**
Doing a relatively long running tasks and it has property of execution serially only
If you again and again calls the same intentService
, then all calls will be kept in a queue
and will be executed one by one
after finishing one by one
. Which is not the case in service as depicted above. It ends on its own... no need to end it by a developer..!!
** Unique Quality of all :**
Once they are crashed
android can stop them calling in future without notifying you as it crashes the app. Need to be handled them with try-catch-exception
to avoid crash
. Again... If you are implementing threads within services
then try-catch-exception
will not save your application from being crashing
...
** THEN WHAT THE HELL & HOW TO IMPLEMENT IT THEN :**
Use FireBaseJobScedular
:-
- Easy to use
- Uses simple JobService
- Can run longer or smaller time tasks...
EVEN ALL THE TIME RUNNING TASK
EVEN SUPPORTED BY NON STANDARD COMPANIES
like vivo, mi, oppo, one+3, ... which takes stock-android
makes changes to it and gives names like FunTouchOs, ColorOs, OxygenOs
- Just need to Do change battery settings to "Do not optimise this app"
- Yes google supports it officially and recommends to use it
- It Creates the instance of
GooglePlyService
and runs within it, And obviously non-standards companies too would not restrict google apps from being doing its tasks.
- Works on
Oreo
.., Even i have tested it on Android P
and works below Android version 5.0 as AlarmManager
tasks.
- Still i recommends to use
minsdk above 16
, target sdk 26
as if in case you wants to upload your app to google play
it is compulsory now and that news would have been heard you. and compile sdk 26
.
- Just Bind Your
JobService
in manifest and use a single line permission of receive_boot_complete
- Just schedule it ... And it will be started on every device in market from every manufacturer... even on
cold boot
and hot boot
- It minimises a lot, lot and lot of code and
you can focus on actual tasks
.
- Once task is finished you can
return false
to indicate task has been finished and it will end the JobService.
Why i am suggesting because i am CTO
of a well-UNKNOwn
company and has been experienced the problems caused by foreground service
across the many types of android phone manufacturers... It is not the Apple and ios
so we had to experienced it. Remain developer since past 18 years and i mostly codes today too... in all of the development projects and its development strategies are thought by me only.
Correct me ... too... As you have not mentioned what your tasks
and
project
is related to... and what you exactly wants to be done in a
foreground service and intent-service
... Let me know..., It would be my pleasure to help you. It is a general theoretical answer rather than what you wants.... But for giving you actual answer i will need exact your project scope..
I think you just need this much of a code. Create a new Class MyJobIntentService and write this much of a code and call postData() to start your service.
public class MyJobIntentService extends JobIntentService {
public static void postData(Context context, String data) {
final Intent intent = new Intent(context, MyJobIntentService.class);
intent.setAction(INITIAL_ACTION);
intent.putExtra(SAMPLE_ID, data);
enqueueWork(context, MyJobIntentService.class, 1000, intent);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Ln.d("Cancelled service");
super.onDestroy();
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
if (intent != null) {
final SampleRequest sampleRequest = requests.get(intent.getAction());
if (sampleRequest != null) {
try {
// perform some networking operations
} catch (Exception ex) {
Log.d("Error for intent ");
}
Log.i("send action ");
} else {
Log.e("action not found for ");
}
}
}
}
And make sure to add your service in manifest file
<service
android:name="service.MyJobIntentService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />
JobIntentService which acts like Intent Service is not getting stopped once the work is done
The issue is in your extended class FixedJobIntentService dequeueWork method.
Try changing it to something like below
GenericWorkItem superValue = super.dequeueWork();
if (superValue != null) {
return new FixedGenericWorkItem(superValue);
}
return null;
Looking at the JobIntentSerivce code, Work Items processor logic is below, i.e until there are no work items left in the queue all items are processed (i.e onHandleWork is called for each item)
while ((work = dequeueWork()) != null) {
if (DEBUG) Log.d(TAG, "Processing next work: " + work);
onHandleWork(work.getIntent());
if (DEBUG) Log.d(TAG, "Completing work: " + work);
work.complete();
}
Issue in your implementation is after processing the first work item, the super.dequeueWork() returns null, which you are not taking care of and just sending a new FixedGenericWorkItem object passing null value. You might observe that a null value is passed to your onHandleWork in your subsequent calls.
Hope this helps resolve your issue.