Job Scheduler service not work when app is killed - android

I'm working on an android app which requires a background task to be performed every hour(Job Scheduler or Service). Task gets executed when the app is running but as soon as I kill the app from foreground, service not work. Is there another way to achieve this?
1. Service
public class NotificationService extends JobService {
private void PrintLog()
{
Log.d("DGVCL", "PrintLog()");
}
#Override
public boolean onStartJob(JobParameters jobParameters) {
Log.d("DGVCL", "onStartJob()");
PrintLog();
jobFinished(jobParameters, false);
return true;
}
#Override
public boolean onStopJob(JobParameters jobParameters) {
Log.d("DGVCL", "onStopJob()");
return true;
}
}
2. Main Activity
JobScheduler jobScheduler = (JobScheduler)getApplicationContext().getSystemService(JOB_SCHEDULER_SERVICE);
ComponentName componentName = new ComponentName(this, NotificationService.class);
JobInfo jobInfo = new JobInfo.Builder(1, componentName)
.setPeriodic(Global.NOTIFICATION_TIME_PERIOD)
.setBackoffCriteria(Global.NOTIFICATION_TIME_PERIOD, JobInfo.BACKOFF_POLICY_LINEAR)
.setPersisted(true).build();
jobScheduler.schedule(jobInfo);
3. manifest
<service android:name="com.hopesndreams.hiren.hd.service.NotificationService" android:permission="android.permission.BIND_JOB_SERVICE" >
</service>

Use WorkManager it is build on top of JobScheduler and it is specifically build to take on all background services both foreground and background functionalities. https://developer.android.com/topic/libraries/architecture/workmanager

* Using AlarmManager*
Step 1:Create a Service
Do your Logic here in the service
public class AService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//do something
}
#Override
public void onCreate() {
//do somrthing
}
}
Step 2: Create a BroadCast receiver
Start your service with this.
public class AReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
try {
Intent intent = new Intent(context, AService.class);
context.startService(intent);
} catch (Exception e) {
}
}
}
in MainActivity
Intent liveIntent = new Intent(getApplicationContext(), AReceiver.class);
PendingIntent recurring = PendingIntent.getBroadcast(getApplicationContext(), 0, liveIntent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Calendar updateTime = Calendar.getInstance();
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, updateTime.getTimeInMillis(), 16 * 60 * 1000, recurring);
//wakeup and starts service in every 16 minutes.
This is the method working for me. Works fine even if you close the app. Works in Xiaomi devices.
Don't forget to add the service inside the manifest

Indeed, WorkManager is the way to go.
You can read up more on other work primitives to suit your task here, but the below implementation uses Worker for threading in WorkManager, which performs work synchronously on a background thread.
public class BackgroundWorker extends Worker {
public BackgroundWorker
(#NonNull Context context,
#NonNull WorkerParameters params) {
super(context, params);
}
#NonNull
#Override
public Worker.Result doWork() {
yourBackgroundTask(); // yourBackgroundTask() implementation
return Result.success();
}
public static void schedulePeriodicWork(Data data) {
// When multiple constraints are specified like below,
// your task will run only when all the constraints are met.
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.setRequiresCharging(true)
.build();
PeriodicWorkRequest taskWork = new PeriodicWorkRequest.Builder(BackgroundWorker.class, 60,
TimeUnit.MINUTES)
.setConstraints(constraints)
.setInputData(data)
.build();
WorkManager.getInstance().enqueue(taskWork);
}
}
Later in your MainActivity file, inside onCreate():
Data data = workData();
BackgroundWorker.schedulePeriodicWork(data);
Then outside the onCreate() method,
private Data workData() {
return new Data.Builder() // to build Data objects
.build();
}
One small thing to note, is that though we set the above task to execute every 60 minutes, each iteration may not be executed at the same time interval.
According to Android documentation, WorkManager is meant for deferrable work, and some drift must be tolerated. However, you can check your log console for the update, "WM-WorkerWrapper: Worker result SUCCESS for Work".
Hope this is helpful.

Related

Periodic task killed when app is stopped on Android 8+

Hello everyone i'm trying to implement a periodic task on Android but i'm stuck on some devices.
I need to run a task in background every 15 or 30 minutes. This works well on Android pre 8.0. But on 8+, it works only when app is in background or foreground. When app is swiped out of recent , scheduled task are killed on real devices (Ulefone note 7(Android 8.1), Tecno LC7(Android 10), itel A56 (Abdroid 9)) but works well on emulators(Android 10). I've tried several ways:
1.Workmanager (works only when app is in background or foreground)
build.gradle
implementation "androidx.work:work-runtime:2.4.0"
MainActivity
PeriodicWorkRequest periodicSyncDataWork =
new PeriodicWorkRequest.Builder(NotificationWorker.class, 15,TimeUnit.MINUTES)
.addTag("TAG_SYNC_DATA")
.setBackoffCriteria(BackoffPolicy.LINEAR,PeriodicWorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS)
.build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"NOTIFICATION_WORKER",
ExistingPeriodicWorkPolicy.REPLACE, //Existing Periodic Work policy
periodicSyncDataWork //work request
);
NotificationWorker
public class NotificationWorker extends Worker {
public NotificationWorker(#NonNull Context context, #NonNull WorkerParameters workerParams)
{
super(context, workerParams);
}
#NonNull
#Override
public Result doWork() {
Log.d("MYWORKER", "LLLLLLLLLLL");
//My code here
return Result.success();
}
}
2.JobScheduler (works only when app is in background or foreground)
ComponentName serviceComponent = new ComponentName(context, NotifJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(1880, serviceComponent);
builder.setPersisted(true);
builder.setPeriodic(16*60*1000, 20*60 *1000);
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
jobScheduler.schedule(builder.build());
3.Alarm Manager (Doesn't fire the BroadcastReceiver)
The main code
Intent intent = new Intent(getApplicationContext(), MyIntentService.class);
final PendingIntent pIntent = PendingIntent.getBroadcast(this, 100,intent, PendingIntent.FLAG_UPDATE_CURRENT);
long firstMillis = System.currentTimeMillis();
AlarmManager alarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
alarm.setInexactRepeating(AlarmManager.RTC_WAKEUP, firstMillis, AlarmManager.INTERVAL_FIFTEEN_MINUTES, pIntent);
The BroadcastReceiver
public class NotificationBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent in = new Intent(context, MyIntentService.class);
context.startService(in);
}
}
The IntentService
public class MyIntentService extends IntentService {
public MyIntentService(String name) {
super(name);
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
Log.d("NotifIntentService", "Starting");
//My task here
}
}
I can't figure out what i'm doing wrong here. Please help
I have been stuck on the same issue for days and eventually found out that there is no proper way to do it yet but to ask users to give some permissions (auto start, batter saver optimizations etc..)
you can find more information here: https://dontkillmyapp.com
This question is asked in many different ways with no good answers, but here you can probably find some answers which it's abstract will be what I just told you.
Work Manager on chinese ROMs like Xiaomi and oppo, when under battery optimization, increase the scheduled delay of work by several hours

How to popup an Alertdialog every 2 hours?

After login i want to show dialog using AlertDialog for every 2 hours.
Tried below code :
private void ShowDialog() {
new SweetAlertDialog(getContext())
.setTitleText("Attention!")
.setContentText("Have You Checked Followup list? Check Here !!")
.setConfirmText("Followup List")
.setConfirmClickListener(new SweetAlertDialog.OnSweetClickListener() {
#Override
public void onClick(SweetAlertDialog sweetAlertDialog) {
ShowFollowupList();
sweetAlertDialog.dismissWithAnimation();
}
})
.setCancelButton("Cancel", new SweetAlertDialog.OnSweetClickListener() {
#Override
public void onClick(SweetAlertDialog sDialog) {
sDialog.dismissWithAnimation();
}
})
.show();
}
By using Work Manager you can display it.
Refer link: https://developer.android.com/topic/libraries/architecture/workmanager
Schedule tasks with WorkManager
API makes it easy to schedule deferrable, asynchronous tasks that are expected to run even if the app exits or device restarts.
You can use AlarmManager to achieve it.
First, create your Receiver class that extends BroadcastReceiver as below.
public class YourReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// show your dialog here
}
}
After that, create an AlarmManager and PendingIntent instance to set the alarm, as below.
public void setAlarm(Context context, int requestCode) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, YourReceiver.class);
// different request code enable you to set alarm more than one
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
final long everyTwoHours = 2 * 60 * 60 * 1000; // in milliseconds
alarmManager.setInexactRepeating(
AlarmManager.RTC, calendar.getTimeInMillis(), everyTwoHours, pendingIntent);
}
Use WorkManager for Scheduling task in background and foreground
Example for Periodic Request
PeriodicWorkRequest request= new PeriodicWorkRequest.Builder(WorkerClass.class,
2, TimeUnit.HOURS).addTag("TAG").build()
WorkManager.getInstance().enqueueUniquePeriodicWork("TAG", ExistingPeriodicWorkPolicy.KEEP, request);
Create a worker class
public class WorkerClass extends Worker {
#Override
public Worker.WorkerResult doWork() {
// Do the work here
// Indicate success or failure with your return value:
return WorkerResult.SUCCESS;
// (Returning RETRY tells WorkManager to try this task again
// later; FAILURE says not to try again.)
}
}
add this in build.gradle
implementation 'android.arch.work:work-runtime:2.1.0-alpha01'
check the latest release doc https://developer.android.com/jetpack/androidx/releases/work

Perform REST call from background in android after fixed interval

I am new to Android, I want to know the most efficient way to perform a background operation which is a simple POST request with JSON Body which will be hit after a fixed interval. Which is best way, Intent Service Or Async Task.
Create a JobScheduler as below
public static void scheduleJob(Context context) {
ComponentName serviceComponent = new ComponentName(context, TestJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(0, serviceComponent);
builder.setMinimumLatency(1 * 1000); // wait at least
builder.setOverrideDeadline(3 * 1000); // maximum delay
charging or not
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
jobScheduler.schedule(builder.build());
}
Create the following receiver
public class MyStartServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Util.scheduleJob(context);
}
}
Register the receiver in the Android manifest for the BOOT_COMPLETED event.
<receiver android:name="MyStartServiceReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Create a JobService and add your code in to onStartJob
public class TestJobService extends JobService {
private static final String TAG = "SyncService";
#Override
public boolean onStartJob(JobParameters params) {
Intent service = new Intent(getApplicationContext(), LocalWordService.class);
getApplicationContext().startService(service);
Util.scheduleJob(getApplicationContext()); // reschedule the job
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
return true;
}
}
For more details refer : linkhere
Please refer this link: https://developer.android.com/reference/java/util/concurrent/ScheduledExecutorService
Please check my custom class example below. This will execute every 2 seconds.
class CustomThreadExecutor {
private lateinit var scheduledExecutorService: ScheduledExecutorService
private lateinit var scheduledFuture: ScheduledFuture<*>
init {
//Start Scheduler as required
startScheduler()
}
fun startScheduler() {
scheduledExecutorService = Executors.newScheduledThreadPool(2)
scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(
{ tempImageFetch() }, 0, 2, TimeUnit.SECONDS)
}
fun shutdownScheduler() {
//Stop before exit the app or when necessary
scheduledExecutorService.shutdownNow()
}
private fun tempImageFetch() {
//TODO call API
}
}
You can use FirebaseJobDispatcher for API level below and above Lollipop. Here is the github link:
https://github.com/firebase/firebase-jobdispatcher-android
How to implement:
Add the following to your build.gradle's dependencies section:
implementation 'com.firebase:firebase-jobdispatcher:0.8.5'
Make a class for your job service:
public class MyJobService extends JobService {
#Override
public boolean onStartJob(JobParameters job) {
// Do some work here
return false; // Answers the question: "Is there still work going on?"
}
#Override
public boolean onStopJob(JobParameters job) {
return false; // Answers the question: "Should this job be retried?"
}
}
Add this on Manifest:
<service
android:exported="false"
android:name=".MyJobService">
<intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
</intent-filter>
</service>
Add this on your main activity onCreate method:
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
Bundle myExtrasBundle = new Bundle();
myExtrasBundle.putString("some_key", "some_value");
Job myJob = dispatcher.newJobBuilder()
// the JobService that will be called
.setService(MyJobService.class)
// uniquely identifies the job
.setTag("my-unique-tag")
// one-off job
.setRecurring(false)
// don't persist past a device reboot
.setLifetime(Lifetime.UNTIL_NEXT_BOOT)
// start between 0 and 60 seconds from now
.setTrigger(Trigger.executionWindow(0, 60))
// don't overwrite an existing job with the same tag
.setReplaceCurrent(false)
// retry with exponential backoff
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
// constraints that need to be satisfied for the job to run
.setConstraints(
// only run on an unmetered network
Constraint.ON_UNMETERED_NETWORK,
// only run when the device is charging
Constraint.DEVICE_CHARGING
)
.setExtras(myExtrasBundle)
.build();
dispatcher.mustSchedule(myJob);
If your application is for API level Lollipop and above, then you should use JobScheduler or WorkManager
For workmanager:
https://codelabs.developers.google.com/codelabs/android-workmanager/
For JobScheduler:
http://www.vogella.com/tutorials/AndroidTaskScheduling/article.html

How to run a Jobscheduler or a Service every minute wihout stopping?

I'm doing an Android app that requires sending its location frequently, every 1 minute or 2 minutes at the most. For this, I use a JobSchedulerService. I've already managed to make it run more than once every 15 minutes on devices with Android N version by replacing the .setPeriodic() with a .setMinimumLatency(). The fact is that at the beginning it is executed periodically in the established time, but after a while it runs every 7 or 9 minutes approximately.
I have already included the application in the battery saving white list, but didn't work. Is there any way to execute it or a similar service every minute with no restrictions? Doesn't matter how much battery the app spends.
EDIT:
This is what I've tried:
ReceiverService:
public class ReceiverService extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context ctx, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
if (!isMyServiceRunning(ServiceBackground.class, ctx))
startWakefulService(ctx, new Intent(ctx, ServiceBackground.class));
new ServiceAlarmManager(ctx).register();
}
}
private boolean isMyServiceRunning(Class<?> serviceClass,Context context) {
ActivityManager manager = (ActivityManager)context. getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.i("Service already","running");
return true;
}
}
Log.i("Service not","running");
return false;
}
}
The ServiceAlarmManager is exactly the same as #madking said.
You can put your code that sends location in a Service and implement an AlarmManager that periodically checks if your Service is running and restarts it if the Service has been killed by OS. You'll have to implement the AlarmManager using a WakefulBroadcastReceiver.
ReceiverService.java
public class ReceiverService extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context ctx, Intent intent) {
if (!YourService.isRunning()) {
startWakefulService(ctx, new Intent(ctx, YourService.class));
}
new ServiceAlarmManager(ctx).register();
}
}
ServiceAlarmManager.java
public class ServiceAlarmManager {
private Context ctx;
private static final int TIME_INTERVAL = 300 * 1000;
public ServiceAlarmManager(Context context) {
ctx = context;
}
public void register() {
Intent serviceRestarter = new Intent();
serviceRestarter.setAction("someString");
PendingIntent pendingIntentServiceRestarter = PendingIntent.getBroadcast(ctx, 0, serviceRestarter, 0);
AlarmManager alarmManager = (AlarmManager) ctx.getSystemService(ctx.ALARM_SERVICE);
Date now = new Date();
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, now.getTime() + TIME_INTERVAL, pendingIntentServiceRestarter);
}
}
Also register your BroadcastReceiver in your Manifest.xml file
<receiver android:name=".ReceiverService">
<intent-filter>
<action android:name="someString" />
</intent-filter>
</receiver>
The register() method does two things.
1- Issues a broadcast which is caught by WakefulBroadcastReceiver and restarts the Service if required
2- Sets the next alarm to be invoked to check if the Service has been killed.
This way the service keeps running even if the OS kills it and you'll be able to send location updates periodically.
Note: Though this practice is not recommended as your application will use more battery but you don't seem to care about it as I did not either as some business requirements don't leave us a choice.
I tried this and it works: in the onCreate() of your activity you schedule an Alarm for every minute (setAlarm). Everytime the alarm is triggered, WakefulBroadcastReceiver is called, and that's where we launch our service(s):
private static long INTERVAL_ALARM = 1 * 60 * 1000;
public static void setAlarm(Context context) {
long current_time = Calendar.getInstance().getTimeInMillis();
Intent myAlarm = new Intent(context.getApplicationContext(), AlarmReceiver.class);
PendingIntent recurringAlarm = PendingIntent.getBroadcast(context.getApplicationContext(), 0, myAlarm, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.RTC_WAKEUP, current_time, INTERVAL_ALARM, recurringAlarm);
}
And in the receiver:
public class AlarmReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent myService = new Intent(context, MyService.class);
context.startService(myService);
}
}
In your service, you should stopSeflf() in the end of your treatment.
Don't forget to register your BroadcastReceiver in your Manifest.xml file
NB: WakefulBroadcastReceiver is deprecated in API level 26.1.0. JobSchedulerService does the work

Android service not restarting in lollipop

In my application, I use location based service in background. So I need to restart my service when it gets destroyed.
But I got this message in logcat
Spurious death for ProcessRecord{320afaf6 20614:com.odoo.crm:my_odoo_gps_service/u0a391}, curProc for 20614: null
My service onTaskRemoved
#Override
public void onTaskRemoved(Intent rootIntent) {
System.out.println("onTaskRemoved called");
Intent restartServiceIntent = new Intent(App.getAppContext(), this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent =
PendingIntent.getService(App.getAppContext(), 1, restartServiceIntent,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService =
(AlarmManager) App.getAppContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartServicePendingIntent);
}
My service onDestroy
#Override
public void onDestroy() {
System.out.println("destroy service");
super.onDestroy();
wakeLock.release();
}
My service onStartCommand
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
I don`t know what is the error. I searched both in google & stackoverflow.
All of them refer Service.START_STICKY. but I already used it.
Same service restart works in KitKat, but with some delay(~5 mins).
Any help is appreciated.
You can restart it by using a BroadcasteReceiver which handles the broadcast sent from onDestroy() of your service.
How to do this:
StickyService.java
public class StickyService extends Service
{
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
sendBroadcast(new Intent("IWillStartAuto"));
}
#Override
public void onDestroy() {
super.onDestroy();
sendBroadcast(new Intent("IWillStartAuto"));
}
}
RestartServiceReceiver.java
public class RestartServiceReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context.getApplicationContext(), StickyService.class));
}
}
Declare the components in manifest file:
<service android:name=".StickyService" >
</service>
<receiver android:name=".RestartServiceReceiver" >
<intent-filter>
<action android:name="IWillStartAuto" >
</action>
</intent-filter>
</receiver>
Hope this will help you.
Your code in onTaskRemoved is preventing the system to run the killProcess commands. The delay on Kitkat is caused by using alarmService.set, which is inexact from API 19. Use setExact instead.
If you have a service that you want to keep alive, it is recommended that you attach a notification to it and make it foreground. That way the likeliness of it being killed would be lowered.
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Environment;
import android.os.IBinder;
import android.support.v7.app.NotificationCompat;
import java.io.File;
import java.io.IOException;
import activity.MainActivity;
import activity.R;
import fragment.MainFragment;
public class MyService extends Service {
public static final int NOTIFICATION_CODE = 1;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(NOTIFICATION_CODE, getNotification());
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
stopForeground(true);
super.onDestroy();
}
#Override
public boolean stopService(Intent name) {
return super.stopService(name);
}
/**
* Create and return a simple notification.
*/
private Notification getNotification() {
Notification notification;
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setColor(getResources()
.getColor(R.color.material_deep_teal_500))
.setAutoCancel(true);
notification = builder.build();
notification.flags = Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTO_CANCEL;
return notification;
}
}
You can modify this code to accomodate your needs but this is the basic structure to start foreground service. Which restarts if gets killed.
how you check issocketalive that socket is connected or not ?
if sockettimeoutexception is generated then try to on set getinputstream and getoutputstream.
other issue that may be socket not closed properly.
So if possible then put your socket code here
this worked for me
Add this attribute in android:allowBackup="false" in manifest file in application tag.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="false"
tools:replace="android:allowBackup">
</application>
</manifest>
The idea of having a service ALWAYS running in background in Android is just wrong 99% of the times.
The system need to "shut down" CPU, and switch to a low battery usage profile.
You are saying you have a location based service. I assume you are using Google Play Services FusedLocationProvider, if not you should.
The FusedLocationProvider allow you to register for location changes using a PendingIntent. Meaning your services doesn't need to run all the time, it just need to register for location changes and then react when a new location come and do its stuff.
See the FusedLocationProviderApi official documentation.
To start listening for location updates
connect to the GoogleClient using the LocationServices.API API
Build your LocationRequest according to your needs (see the doc)
Call requestLocationUpdates() using the PendingIntent version
To stop listening
connect to the GoogleClient using the LocationServices.API API
Call removeLocationUpdates() using the same PendingIntent
Your PendingIntent can launch another service to handle the new location.
For example doing this from a service:
public void startMonitoringLocation(Context context) {
GoogleApiClient client = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.build()
ConnectionResult connectionResult = mApiClient.blockingConnect();
if (connectionResult.isSuccess()) {
LocationServices.FusedLocationApi
.requestLocationUpdates(client, buildLocationRequest(), buildPendingIntent(context));
} else {
handleConnectionFailed(context);
}
}
Then the service can immediately stop.
The first time this code run it WILL fail. The connection to the google client usually require the user to take some actions. The ConnectionResult.hasResolution() method will return true if this is the case. Otherwise the reason is something else and you can't recover from it. Meaning the only thing you can do is inform the user the feature will not work or have a nice fallback.
The ConnectionResult.getResolution() give you a PendingIntent you need to use an Activity and startIntentSenderForResult() method on the Activity to resolve this intent. So you would create a Notification starting your Activity to resolve that, and in the end call your Service again.
I usually just start an Activity dedicated to do all the work. It's lot easier but you don't want to call connectBlocking() in it. Check out this on how to do it.
You may ask why not requesting location updates directly in the Activity. That's actually perfectly fine, unless you need the location monitor to automatically start with the device, even if the user didn't explicitly opened the App.
<receiver android:name=".BootCompletedBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
This way you can just run your service to connect and request location updates when the device is rebooted.
Example on how you can build your location request:
public LocationRequest buildLocationRequest() {
LocationRequest locRequest = LocationRequest.create();
// Use high accuracy
locRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// how often do you need to check for the location
// (this is an indication, it's not exact)
locRequest.setInterval(REQUIRED_INTERVAL_SEC * 1000);
// if others services requires the location more often
// you can still receive those updates, if you do not want
// too many consider setting this lower limit
locRequest.setFastestInterval(FASTEST_INTERVAL_SEC * 1000);
// do you care if the user moved 1 meter? or if he move 50? 1000?
// this is, again, an indication
locRequest.setSmallestDisplacement(SMALLEST_DISPLACEMENT_METERS);
return locRequest;
}
And your pending intent:
public PendingIntent buildPendingIntent(Context context) {
Intent intent = new Intent(context, LocationUpdateHandlerService.class);
intent.setAction(ACTION_LOCATION_UPDATE);
intent.setPackage(context.getPackageName());
return PendingIntent.getService(context, REQUEST_CODE, intent, PendingIntent.FLAG_CANCEL_CURRENT);
}
Your LocationUpdateHandlerService can be an IntentService if you need to do work in background:
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(FusedLocationProviderApi.KEY_LOCATION_CHANGED)) {
Location location = extras.getParcelable(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
handleLocationChanged(location);
} else {
Log.w(TAG, "Didn't receive any location update in the receiver");
}
}
}
But can also be a Broadcast or anything that suits you.
Finally I achieved with help of Evernote JobService
Github link - https://github.com/evernote/android-job
Step 1: Add evernote jobservice dependency
implementation 'com.evernote:android-job:1.3.0-alpha03'
Step 2: Create DemoJobCreator.java class
public class DemoJobCreator implements JobCreator {
#Override
#Nullable
public Job create(#NonNull String tag) {
switch (tag) {
case DemoSyncJob.TAG:
return new DemoSyncJob();
default:
return null;
}
}
}
Step 3: Create DemoSyncJob.java class
public class DemoSyncJob extends Job {
public static final String TAG = ">>>> job_demo_tag";
#Override
#NonNull
protected Result onRunJob(Params params) {
// run your job here
Log.d(TAG, "onRunJob: ");
if(!isMyServiceRunning(this.getContext(), TestService.class)){
Intent intent=new Intent(context,TestService.class);
context.startService(intent);
}
scheduleJob();
return Job.Result.SUCCESS;
}
public static void scheduleJob() {
new JobRequest.Builder(DemoSyncJob.TAG)
.setExecutionWindow(2_000L, 2_000L)
//.setPeriodic(900000) -> recommended. but it will work after 15 min (if you used this no need scheduleJob(); inside onRunJob();)
.build()
.schedule();
}
public static boolean isMyServiceRunning(Context context, Class<?> serviceClass) {
try {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
}catch (Exception e){
Log.e(TAG, "isMyServiceRunning: ",e );
}
return false;
}
}
Step 4: In your Application file (If not available create it) add following line in onCreate()
JobManager.create(this).addJobCreator(new DemoJobCreator());
Step 5: Finally start JobService in your Activity
DemoSyncJob.scheduleJob();
This JobService will check service running or not (every 2 second) If service not running it will restart the service.
Disclaimer : This may be not right solution. But it will 100% working.
I hope it helps atleast anyone in future.

Categories

Resources