Start service with JobScheduler on Android O - android

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.

Related

Starting WorkManager task from a BroadcastReceiver

I have a BroadcastReceiver here:
NotificationServiceReceiver:
public class NotificationServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(RestService.ACTION_PENDING_REMINDERS_UPDATED)) {
//Reminders updated
NotificationServer.startNotificationWorkRequest(context);
}
}
A Notification Server:
public class NotificationServer extends IntentService {
private static final String LOG_TAG = "NotificationService";
public static final String ACTION_SHOW_NOTIFICATION = "com.android.actions.SHOW_NOTIFICATION";
// this is a bypass used for unit testing - we don't want to trigger this service when the calendar updates during
// the intergration tests
public static boolean sIgnoreIntents = false;
private WorkManager mWorkManager;
private LiveData<List<WorkStatus>> mSavedWorkStatus;
public NotificationServer() {
super(NotificationServer.class.getName());
mWorkManager = WorkManager.getInstance();
}
/**
* Handles all intents for the update services. Intents are available to display a particular notification, clear all
* notifications, refresh the data backing the notification service and initializing our timer. The latter is safe to
* call always, it will check the current state of on-device notifications and update its timers appropriately.
*
* #param intent - the intent to handle. One of ACTION_SHOW_NOTIFICATION,
* ACTION_REFRESH_DATA or ACTION_INIT_TIMER.
*/
#Override
protected void onHandleIntent(Intent intent) {
startNotificationWorkRequest(this);
}
public void startNotificationWorkRequest(Context context) {
WorkContinuation continuation = mWorkManager
.beginUniqueWork(IMAGE_MANIPULATION_WORK_NAME,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequest.from(CleanupWorker.class));
}
}
I want to start a WorkManager task onReceive of the Broadcast Receiver. The problem is I can't do this statically as I need access to the current WorkManager object. The example code that Google provides here: https://github.com/googlecodelabs/android-workmanager/blob/master/app/src/main/java/com/example/background/BlurActivity.java
Grabs the ViewModel like this: ViewModelProviders.of(this).get(BlurViewModel.class);
I can't do this obviously because my notification server class is not a view model. How should I approach this problem?
For anyone that sees this, you can use WorkManager.getInstance() to get the WorkManager object statically. There is only one instance of WorkManager, just make sure you initialize it like this on the start of your application: WorkManager.initialize(this, new Configuration.Builder().build());
Android Custom Work Manager Config official documentation

Unable to start Service via JobScheduler in Android Oreo

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

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

How to replace sendStickyBroadcast in Android?

As you know,
sendStickyBroadcast method is now deprecated. How to replace it?
Of course I can use sendBroadcast but then it will be not sticky.
You could use an event bus, the following are some of the most used libraries.
- https://github.com/greenrobot/EventBus
- http://square.github.io/otto/
- https://blog.kaush.co/2014/12/24/implementing-an-event-bus-with-rxjava-rxbus/ (how to use Rx as an event bus)
Another approach would be to create a class that listens to the broadcast and then stores the last state that it retrieved. In my opinion, this approach would not be ideal though.
Maybe one can use a JobScheduler to
schedule a periodic job,
which will send broadcasts.
The "keep alive" service, which will send periodoc broadcasts.
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Intent;
import static my.UtilsLocation.PACKAGE_NAME;
/**
* JobService to be scheduled by the JobScheduler.
* start another service
*/
public class KeepAliveBroadcastJobService extends JobService {
public static final String INTENT_ACTION_KEEP_ALIVE = PACKAGE_NAME + ".action.KEEPALIVE";
#Override
public boolean onStartJob(JobParameters params) {
// send recurring broadcast
final Intent intent = new Intent(getApplicationContext());
intent.setAction(INTENT_ACTION_KEEP_ALIVE);
sendBroadcast(intent);
return false;
}
#Override
public boolean onStopJob(JobParameters params) {
return true;
}
}
A util, to periodically schedule the keep alive job.
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.util.Log;
import java.util.concurrent.atomic.AtomicBoolean;
public class UtilsKeepAlive {
private static final String TAG = UtilsKeepAlive.class.toString();
private static AtomicBoolean isKeepAliveOn = new AtomicBoolean(false);
private static final int INTERVAL_MILLIS = 600000; // 10 min
private static final int FLEX_MILLIS = 60000; // 1 min
public static void enableKeepAlive(Context context) {
// if already on
if (isKeepAliveOn.get()) return;
Log.i(TAG, "Keep alive job scheduled");
ComponentName serviceComponent = new ComponentName(context, KeepAliveBroadcastJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(0, serviceComponent);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); //Require any network
builder.setRequiresCharging(false);
builder.setPeriodic(INTERVAL_MILLIS, FLEX_MILLIS);
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
jobScheduler.schedule(builder.build());
//we have scheduled the keep alive
isKeepAliveOn.set(true);
}
}
The periodic "keep alive" job - can be e.g. scheduled in a broadcast, on BOOT_COMPLETED.
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "BroadCastReceiver got the location.");
final String action = intent.getAction();
switch (action) {
case INTENT_ACTION_BOOT_COMPLETED:
Log.i(TAG, "Received a BootCompleted");
UtilsKeepAlive.enableKeepAlive(context);
break;
I have used this tutorial explaining the JobScheduler:
https://www.vogella.com/tutorials/AndroidTaskScheduling/article.html
This is Google's explanation to why Sticky Broadcasts was deprecated.
Sticky broadcasts should not be used. They provide no security (anyone can access them), no protection (anyone can modify them), and many other problems. The recommended pattern is to use a non-sticky broadcast to report that something has changed, with another mechanism for apps to retrieve the current value whenever desired.
Hope this helps.

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