Unable to start Service via JobScheduler in Android Oreo - android

I created an app that uses firebase services. Whenever there's a change in value, a notification is generated. So for this,my service must be continuously running,else there will be no notification. I did created and successfully get it running in pre-oreo Android OS. My service runs perfectly even after clearing from recents and force closing but not in Oreo.
Upon googling i came to know about automatic start of service in Oreo is prohibited. We need JobScheduler for this purpose.
This is my JobService class-
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Intent;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;
#RequiresApi(api = Build.VERSION_CODES.O)
public class Serviceo extends JobService {
private JobParameters params;
#Override
public boolean onStartJob(JobParameters jobParameters) {
this.params = jobParameters;
Log.d("HEYY","i'm here");
Intent service = new Intent(getApplicationContext(), NotificationService.class);
getApplicationContext().startService(service);
return true;
}
#Override
public boolean onStopJob(JobParameters jobParameters) {
return false;
}
}
PS- Extending JobService gave error that it needs min api=21, so i rectified it using #RequiresApi(api = Build.VERSION_CODES.O)
And in my Service's onDestroy method, i declared my JobService like this-
#Override
public void onDestroy() {
super.onDestroy();
Log.i("EXIT", "ondestroy!");
Intent broadcastIntent = new Intent("com.mukesh.mu.RestarterBroadcastReceiver");
sendBroadcast(broadcastIntent);
stoptimertask();
Log.i("BRUHHH","hi");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Log.i("BRUHHH","hi2");
jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(1, new ComponentName(getPackageName(),
Serviceo.class.getName()));
//run job service after every 5 seconds
builder.setPeriodic(5000);
jobScheduler.schedule(builder.build());
}
This method is executed but service ain't getting started. I can see in logcat "hi2" executed.
I also declared it in manifest
<service
android:name="com.mukesh.mu.Serviceoreo"
android:permission="android.permission.BIND_JOB_SERVICE"
/>
Everything work's fine and normal in pre-Oreo builds via Intent and broadcast but not working in Oreo.

You can try Alternatives of Job Service- Firebase JobDispatcher

Related

Start service with JobScheduler on Android O

I'm trying to start a IntentService to register to a firebase cloud messaging on Android O.
On Android O it's not allowed to start a Intent Service "in a situation when it isn't permitted" and every one tells me to use a JobService but not how to use it.
What constraints should the JobInfo.Builder have in order to have a "situation where it's permitted", i keep getting the same IllegalStateException
Here's my JobService
#Override
public boolean onStartJob(JobParameters params) {
Intent intent = new Intent(this, RegistrationIntentService.class);
getApplicationContext().startService(intent);
return false;
}
#Override
public boolean onStopJob(JobParameters params) {
return false;
}
public static void scheduleJob(Context context) {
ComponentName serviceComponent = new ComponentName(context, MyJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(MyJobService.JOB_ID, serviceComponent);
builder.setMinimumLatency(1 * 1000); // wait at least
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
if(jobScheduler != null) jobScheduler.schedule(builder.build());
}
If you are using support library version 26.1.0 or higher you have access to the JobIntentService which is similar to an Intent Service with the added benefits of the job scheduler, you do not need to manage anything other than starting it.
According to the docs
Helper for processing work that has been enqueued for a job/service. When running on Android O or later, the work will be dispatched as a job via JobScheduler.enqueue. When running on older versions of the platform, it will use Context.startService.
You can find out more details here JobIntentService.
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v4.app.JobIntentService;
public class JobIntentNotificationService extends JobIntentService {
public static void start(Context context) {
Intent starter = new Intent(context, JobIntentNotificationService.class);
JobIntentNotificationService.enqueueWork(context, starter);
}
/**
* Unique job ID for this service.
*/
static final int JOB_ID = 1000;
/**
* Convenience method for enqueuing work in to this service.
*/
private static void enqueueWork(Context context, Intent intent) {
enqueueWork(context, JobIntentNotificationService.class, JOB_ID, intent);
}
#Override
protected void onHandleWork(#NonNull Intent intent) {
// do your work here
}
}
And the way you call it is
JobIntentNotificationService.start(getApplicationContext());
You will need to add this permission for pre Oreo devices
<!-- used for job scheduler pre Oreo -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
Firebase actually has a dedicated service for receiving messages called FirebaseMessagingService. This Firebase page should contain all the info to get you started in that regard.
Aside from that, you're trying to access the application context from the Service, while you should be using the base context of the parent service:
getApplicationContext().startService(intent);
to
startService(intent);
If you want to launch certain jobs from the FirebaseMessagingService, look into their JobDispatcher library which is pretty great.

Android 8.0: java.lang.IllegalStateException: Not allowed to start service Intent

On application launch, app starts the service that should to do some network task.
After targeting API level 26, my application fails to start service on Android 8.0 on background.
Caused by: java.lang.IllegalStateException: Not allowed to start
service Intent {
cmp=my.app.tt/com.my.service
}: app is in background uid UidRecord{90372b1 u0a136 CEM idle procs:1
seq(0,0,0)}
as I understand it related to:
Background execution limits
The startService() method now throws an IllegalStateException if an
app targeting Android 8.0 tries to use that method in a situation when
it isn't permitted to create background services.
"in a situation when it isn't permitted" - what it's actually mean?? And how to fix it. I don't want to set my service as "foreground"
I got solution. For pre-8.0 devices, you have to just use startService(), but for post-7.0 devices, you have to use startForgroundService(). Here is sample for code to start service.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context, ServedService.class));
} else {
context.startService(new Intent(context, ServedService.class));
}
And in service class, please add the code below for notification:
#Override
public void onCreate() {
super.onCreate();
startForeground(1,new Notification());
}
Where O is Android version 26.
If you don't want your service to run in Foreground and want it to run in background instead, post Android O you must bind the service to a connection like below:
Intent serviceIntent = new Intent(context, ServedService.class);
context.startService(serviceIntent);
context.bindService(serviceIntent, new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
//retrieve an instance of the service here from the IBinder returned
//from the onBind method to communicate with
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
The permitted situations are a temporary whitelist where the background service behaves the same as before Android O.
Under certain circumstances, a background app is placed on a temporary whitelist for several minutes. While an app is on the whitelist, it can launch services without limitation, and its background services are permitted to run. An app is placed on the whitelist when it handles a task that's visible to the user, such as:
Handling a high-priority Firebase Cloud Messaging (FCM) message.
Receiving a broadcast, such as an SMS/MMS message.
Executing a PendingIntent from a notification.
Starting a VpnService before the VPN app promotes itself to the foreground.
Source: https://developer.android.com/about/versions/oreo/background.html
So in other words if your background service does not meet the whitelist requirements you have to use the new JobScheduler. It's basically the same as a background service, but it gets called periodically instead of running in the background continuously.
If you're using an IntentService, you can change to a JobIntentService. See #kosev's answer below.
The best way is to use JobIntentService which uses the new JobScheduler for Oreo or the old services if not available.
Declare in your manifest:
<service android:name=".YourService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
And in your service you have to replace onHandleIntent with onHandleWork:
public class YourService extends JobIntentService {
public static final int JOB_ID = 1;
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, YourService.class, JOB_ID, work);
}
#Override
protected void onHandleWork(#NonNull Intent intent) {
// your code
}
}
Then you start your service with:
YourService.enqueueWork(context, new Intent());
If the service is running in a background thread by extending IntentService, you can replace IntentService with JobIntentService which is provided as part of Android Support Library
The advantage of using JobIntentService is, it behaves as an IntentService on pre-O devices and on O and higher, it dispatches it as a job
JobScheduler can also be used for periodic/on demand jobs. But, ensure to handle backward compatibility as JobScheduler API is available only from API 21
Yeah, that's because you can't start services in the background anymore on API 26. So you can start ForegroundService above API 26.
You'll have to use
ContextCompat.startForegroundService(...)
and post a notification while processing the leak.
As #kosev said in his answer you can use JobIntentService.
But I use an alternative solution - I catch IllegalStateException and start the service as foreground.
For example, this function starts my service:
#JvmStatic
protected fun startService(intentAction: String, serviceType: Class<*>, intentExtraSetup: (Intent) -> Unit) {
val context = App.context
val intent = Intent(context, serviceType)
intent.action = intentAction
intentExtraSetup(intent)
intent.putExtra(NEED_FOREGROUND_KEY, false)
try {
context.startService(intent)
}
catch (ex: IllegalStateException) {
intent.putExtra(NEED_FOREGROUND_KEY, true)
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
}
else {
context.startService(intent)
}
}
}
and when I process Intent I do such thing:
override fun onHandleIntent(intent: Intent?) {
val needToMoveToForeground = intent?.getBooleanExtra(NEED_FOREGROUND_KEY, false) ?: false
if(needToMoveToForeground) {
val notification = notificationService.createSyncServiceNotification()
startForeground(notification.second, notification.first)
isInForeground = true
}
intent?.let {
getTask(it)?.process()
}
}
I see a lot of responses that recommend just using a ForegroundService. In order to use a ForegroundService there has to be a notification associated with it. Users will see this notification. Depending on the situation, they may become annoyed with your app and uninstall it.
The easiest solution is to use the new Architecture Component called WorkManager. You can check out the documentation here: https://developer.android.com/topic/libraries/architecture/workmanager/
You just define your worker class that extends Worker.
public class CompressWorker extends Worker {
public CompressWorker(
#NonNull Context context,
#NonNull WorkerParameters params) {
super(context, params);
}
#Override
public Worker.Result doWork() {
// Do the work here--in this case, compress the stored images.
// In this example no parameters are passed; the task is
// assumed to be "compress the whole library."
myCompress();
// Indicate success or failure with your return value:
return Result.SUCCESS;
// (Returning RETRY tells WorkManager to try this task again
// later; FAILURE says not to try again.)
}
}
Then you schedule when you want to run it.
OneTimeWorkRequest compressionWork =
new OneTimeWorkRequest.Builder(CompressWorker.class)
.build();
WorkManager.getInstance().enqueue(compressionWork);
Easy! There are a lot of ways you can configure workers. It supports recurring jobs and you can even do complex stuff like chaining if you need it. Hope this helps.
Alternate solution by using JobScheduler, it can start service in background in regular interval of time.
Firstly make class named as Util.java
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
public class Util {
// schedule the start of the service every 10 - 30 seconds
public static void schedulerJob(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); //delay time
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); // require unmetered network
builder.setRequiresCharging(false); // we don't care if the device is charging or not
builder.setRequiresDeviceIdle(true); // device should be idle
System.out.println("(scheduler Job");
JobScheduler jobScheduler = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
jobScheduler = context.getSystemService(JobScheduler.class);
}
jobScheduler.schedule(builder.build());
}
}
Then, make JobService class named as TestJobService.java
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.widget.Toast;
/**
* JobService to be scheduled by the JobScheduler.
* start another service
*/
public class TestJobService extends JobService {
#Override
public boolean onStartJob(JobParameters params) {
Util.schedulerJob(getApplicationContext()); // reschedule the job
Toast.makeText(this, "Bg Service", Toast.LENGTH_SHORT).show();
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
return true;
}
}
After that BroadCast Receiver class named ServiceReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class ServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Util.schedulerJob(context);
}
}
Update Manifest file with service and receiver class code
<receiver android:name=".ServiceReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service
android:name=".TestJobService"
android:label="Word service"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
Left main_intent launcher to mainActivity.java file which is created by default, and changes in MainActivity.java file are
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Util.schedulerJob(getApplicationContext());
}
}
WOOAAH!! Background Service starts without Foreground service
[Edit]: You can use Work Manager for any type of background tasks in Android.
From the firebase release notes, they state that support for Android O was first released in 10.2.1 (although I'd recommend using the most recent version).
please add new firebase messaging dependencies for android O
compile 'com.google.firebase:firebase-messaging:11.6.2'
upgrade google play services and google repositories if needed.
If any intent was previously working fine when the app is in the background, it won't be the case any more from Android 8 and above. Only referring to intent which has to do some processing when app is in the background.
The below steps have to be followed:
Above mentioned intent should be using JobIntentService instead of
IntentService.
The class which extends JobIntentService should implement the - onHandleWork(#NonNull Intent intent) method and should have below the
method, which will invoke the onHandleWork method:
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, xyz.class, 123, work);
}
Call enqueueWork(Context, intent) from the class where your intent is defined.
Sample code:
Public class A {
...
...
Intent intent = new Intent(Context, B.class);
//startService(intent);
B.enqueueWork(Context, intent);
}
The below class was previously extending the Service class
Public Class B extends JobIntentService{
...
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, B.class, JobId, work);
}
protected void onHandleWork(#NonNull Intent intent) {
...
...
}
}
com.android.support:support-compat is needed for JobIntentService - I use 26.1.0 V.
Most important is to ensure the Firebase libraries version is on at least 10.2.1, I had issues with 10.2.0 - if you have any!
Your manifest should have the below permission for the Service class:
service android:name=".B"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE"
Hope this helps.
If you are running your code on 8.0 then application will crash. So start the service in the foreground. If below 8.0 use this :
Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
context.startService(serviceIntent);
If above or 8.0 then use this :
Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
ContextCompat.startForegroundService(context, serviceIntent );
Due to controversial votes on this answer (+4/-4 as of this edit), PLEASE LOOK AT THE OTHER ANSWERS FIRST AND USE THIS ONLY AS A LAST RESORT. I only used this once for a networking app that runs as root and I agree with the general opinion that this solution should not be used under normal circumstances.
Original answer below:
The other answers are all correct, but I'd like to point out that another way to get around this is to ask user to disable battery optimizations for your app (this isn't usually a good idea unless your app is system related). See this answer for how to request to opt out of battery optimizations without getting your app banned in Google Play.
You should also check whether battery optimizations are turned off in your receiver to prevent crashes via:
if (Build.VERSION.SDK_INT < 26 || getSystemService<PowerManager>()
?.isIgnoringBatteryOptimizations(packageName) != false) {
startService(Intent(context, MyService::class.java))
} // else calling startService will result in crash
if you have integrated firebase messaging push notification then,
Add new/update firebase messaging dependencies for android O (Android 8.0), due to Background Execution Limits.
compile 'com.google.firebase:firebase-messaging:11.4.0'
upgrade google play services and google repositories if needed.
Update:
compile 'com.google.firebase:firebase-messaging:11.4.2'
Use startForegroundService() instead of startService()
and don't forget to create startForeground(1,new Notification()); in your service within 5 seconds of starting service.
it's actually happening because the phone is on offscreen, or you pressed the power button while starting the service. solution for this which worked for me is to
start an activity and when it will go in onResume then start the service.
in my case, it was booting up and starting a service.
I am very dissatisfied with the answers here. What if foreground service nor WorkManager fit the use case?
I've come to a solution, where I use process scope and make sure to not include scope cancellation exception in the logging logic.
Like so:
with(ProcessLifecycleOwner.get()) {
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
try {
context.startService(context, Service::class.java)
} catch (ex: CancellationException) {
// app minimized, scope cancelled, do not log as error
} catch (ex: IllegalStateException) {
logToFirebase(ex)
}
}
}
}
More detailed in this article https://medium.com/#lepicekmichal/android-background-service-without-hiccup-501e4479110f
You may try this code to avoid crash. As google developers said in issue tracker.
private val activityManager by lazy { getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager }
//due to https://issuetracker.google.com/issues/113122354
private fun isInForegroundByImportance(): Boolean {
val importanceState = activityManager.runningAppProcesses.find {
it.pid == android.os.Process.myPid()
}?.importance ?: return false
return importanceState >= RunningAppProcessInfo.IMPORTANCE_FOREGROUND
}
and usage
override fun onResume() {
super.onResume()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || isInForegroundByImportance()) {
val intent = Intent(this, BluetoothScannerService::class.java)
this.startService(intent)
}
}
i had this problem too
added this library
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
and reinstalled the app solved this for me

Job scheduler stops working if app killed android

Hi did a sample of JobScheduler in my app.
This is how I initiate it
jobScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
ComponentName jobService = new ComponentName(getPackageName(),
MyJobService.class.getName());
JobInfo jobInfo = new JobInfo.Builder(MYJOBID, jobService)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setExtras(bundle).build();
jobScheduler.schedule(jobInfo);
And I showed a toast in the JobService:
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends JobService {
public MyJobService() {
}
#Override
public boolean onStartJob(JobParameters params) {
// UtilityMethods.showToast(this,params.getExtras().getString("json"));
Toast.makeText(this,"test",Toast.LENGTH_SHORT).show();
return false;
}
#Override
public boolean onStopJob(JobParameters params) {
UtilityMethods.showToast(this,"onStop()");
return false;
}
}
And this was working perfectly fine even I tried turning off the internet and killing app from background.
I then tried building a similar thing in one of my libraries. I wrote the same code in the library and I am calling it from my app's MainActivity. But this time, When I kill app from background, it stops working. Can anyone tell me why?
My MainActivity where I initialize it
JobScheduler jobScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
ComponentName jobService = new ComponentName(getPackageName(),
MyJobService.class.getName());
JobInfo jobInfo = new JobInfo.Builder(MYJOBID, jobService)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build();
jobScheduler.schedule(jobInfo);
It is working when I start it from onCreate and not working if I start it from a callback funtion().
Any help is really appreciated.
Android> 7 automatically saves battery power. You must enable the app's battery saver stop
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Intent intent = new Intent();
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
startActivity(intent);
}
}
add this to AndroidManifest.xml
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
Make this return true
#Override
public boolean onStartJob(JobParameters params) {
// UtilityMethods.showToast(this,params.getExtras().getString("json"));
Toast.makeText(this,"test",Toast.LENGTH_SHORT).show();
return true;
}
and start a new thread from here(as this is executed on mainthread only).
onStartJob
added in API level 21
boolean onStartJob (JobParameters params)
Override this method with the callback logic for your job. Any such logic needs to be performed on a separate thread, as this function is executed on your application's main thread.
Parameters
params JobParameters: Parameters specifying info about this job, including the extras bundle you optionally provided at job-creation time.
Returns
boolean True if your service needs to process the work (on a separate thread). False if there's no more work to be done for this job.

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.

Android - Periodic Background Service - Advice

I am working on an app that will relay information about its location to a remote server. I am intending to do it by doing a simple HTTP post to the web-server and all is simple and fine.
But according to the spec, the app needs to execute itself from time to time, lets say once in every 30 mins. Be independent of the interface, meaning which it needs to run even if the app is closed.
I looked around and found out that Android Services is what needs to be used. What could I use to implement such a system. Will the service (or other mechanism) restart when the phone restarts?
Thanks in advance.
Create a Service to send your information to your server. Presumably, you've got that under control.
Your Service should be started by an alarm triggered by the AlarmManager, where you can specify an interval. Unless you have to report your data exactly every 30 minutes, you probably want the inexact alarm so you can save some battery life.
Finally, you can register your app to get the bootup broadcast by setting up a BroadcastReceiver like so:
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
// Register your reporting alarms here.
}
}
}
You'll need to add the following permission to your AndroidManifest.xml for that to work. Don't forget to register your alarms when you run the app normally, or they'll only be registered when the device boots up.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
Here is a semi-different way to keep the service going forever. There is ways to kill it in code if you'd wish
Background Service:
package com.ex.ample;
import android.app.Service;
import android.content.*;
import android.os.*;
import android.widget.Toast;
public class BackgroundService extends Service {
public Context context = this;
public Handler handler = null;
public static Runnable runnable = null;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Toast.makeText(this, "Service created!", Toast.LENGTH_LONG).show();
handler = new Handler();
runnable = new Runnable() {
public void run() {
Toast.makeText(context, "Service is still running", Toast.LENGTH_LONG).show();
handler.postDelayed(runnable, 10000);
}
};
handler.postDelayed(runnable, 15000);
}
#Override
public void onDestroy() {
/* IF YOU WANT THIS SERVICE KILLED WITH THE APP THEN UNCOMMENT THE FOLLOWING LINE */
//handler.removeCallbacks(runnable);
Toast.makeText(this, "Service stopped", Toast.LENGTH_LONG).show();
}
#Override
public void onStart(Intent intent, int startid) {
Toast.makeText(this, "Service started by user.", Toast.LENGTH_LONG).show();
}
}
Here is how you start it from your main activity or wherever you wish:
startService(new Intent(this, BackgroundService.class));
onDestroy() will get called when the application gets closed or killed but the runnable just starts it right back up.
I hope this helps someone out.
The reason why some people do this is because of corporate applications where in some instances the users/employees must not be able to stop certain things :)
http://i.imgur.com/1vCnYJW.png
EDIT
Since Android O (8.0) you have to use JobManager for scheduled tasks. There is a library called Android-Job by Evernote which will make periodic background work a breeze on all Android versions. I have also made a Xamarin Binding of this library.
Then all you need to do is the following:
In your application class:
public class MyApp extends Application {
#Override
public void onCreate() {
super.onCreate();
JobManager.create(this).addJobCreator(new MyJobCreator());
}
}
Create the following two classes YourJobCreator and YourSyncJob(Where all the work will be done. Android allocates time for all the background jobs to be run. For android versions < 8.0 it will still run with an Alarm manager and background service as per normal)
public class MyJobCreator implements JobCreator {
#Override
#Nullable
public Job create(#NonNull String tag) {
switch (tag) {
case MySyncJob.TAG:
return new MySyncJob();
default:
return null;
}
}
}
public class MySyncJob extends Job {
public static final String TAG = "my_job_tag";
#Override
#NonNull
protected Result onRunJob(Params params) {
//
// run your job here
//
//
return Result.SUCCESS;
}
public static void scheduleJob() {
new JobRequest.Builder(MySyncJob.TAG)
.setExecutionWindow(30_000L, 40_000L) //Every 30 seconds for 40 seconds
.build()
.schedule();
}
}
You should schedule your service with alarm manager, first create the pending intent of service:
Intent ii = new Intent(getApplicationContext(), MyService.class);
PendingIntent pii = PendingIntent.getService(getApplicationContext(), 2222, ii,
PendingIntent.FLAG_CANCEL_CURRENT);
Then schedule it using alarm manager:
//getting current time and add 5 seconds to it
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 5);
//registering our pending intent with alarmmanager
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP,cal.getTimeInMillis(), pi);
this will launch your service after 5 seconds of current time. You can make your alarm repeating.
You can use Alarm Manager to start Service at specified time and then repeat alarm in specified interval. When alarm goes on you can start service and connect to server and make what you want

Categories

Resources