I am creating a GcmTaskService which I call with a OneoffTask.
I am not sure the windowEndDelaySeconds value I pass to the setExecution() method of OneoffTask.Builder is being utilised correctly. Or maybe I just don't understand the documentation for OneoffTask.Builder's setExecution():
public OneoffTask.Builder setExecutionWindow (long windowStartDelaySeconds, long windowEndDelaySeconds)
Mandatory setter for creating a one-off task. You specify the earliest
point in time in the future from which your task might start
executing, as well as the latest point in time in the future at which
your task must have executed.
Parameters
windowStartDelaySeconds - Earliest point from which your task is eligible to run. windowEndDelaySeconds - Latest point at
which your task must be run.
When I do the following:
Disable connectivity on my device
Schedule my task with an end time of 1 second - using .setExecutionWindow(0, 1)
Wait 10 seconds
Enable connectivity on my device
...my task executes - despite it being much later than the 1 second windowEndDelaySeconds value I specified in the OneoffTask.Builder. Why is this?
My code
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gms.gcm.GcmNetworkManager;
import com.google.android.gms.gcm.GcmTaskService;
import com.google.android.gms.gcm.OneoffTask;
import com.google.android.gms.gcm.Task;
import com.google.android.gms.gcm.TaskParams;
public class TestTaskService extends GcmTaskService {
private final static String LOG_TAG = TestTaskService.class.getSimpleName();
private static final String ACTION_TEST = "ACTION_TEST";
private static final String EXTRA_ACTION = "EXTRA_ACTION";
private static final String EXTRA_MESSAGE = "EXTRA_MESSAGE";
public static void startActionTest(Context context, String message) {
Bundle extras = new Bundle();
extras.putString(EXTRA_ACTION, ACTION_TEST);
extras.putString(EXTRA_MESSAGE, message);
OneoffTask task = new OneoffTask.Builder()
.setService(TestTaskService.class)
.setExtras(extras)
.setTag(ACTION_TEST)
.setExecutionWindow(0, 1) // seconds
.setRequiredNetwork(Task.NETWORK_STATE_CONNECTED)
.setPersisted(true)
.setUpdateCurrent(true)
.build();
GcmNetworkManager gcmNetworkManager = GcmNetworkManager.getInstance(context);
gcmNetworkManager.schedule(task);
}
#Override
public int onRunTask(TaskParams taskParams) {
Context context = getApplicationContext();
Bundle extras = taskParams.getExtras();
String action = extras.getString(EXTRA_ACTION);
if (ACTION_TEST.equals(action)) {
String message = extras.getString(EXTRA_MESSAGE);
if (message == null) {
message = "No message.";
}
Utilities.makeToast(context, message);
return GcmNetworkManager.RESULT_SUCCESS;
}
else {
Log.e(LOG_TAG, "Unknown action: " + action);
Utilities.makeToast(context, "Unknown action: " + action);
return GcmNetworkManager.RESULT_FAILURE;
}
}
}
...called with TestTaskService.startActionTest(context, "My message");
Given start=600 and end=3600 the job will run between 10 minutes and 1 hour from now as soon as the conditions are met (e.g. required connectivity) or 1 hour from now if the conditions aren't met. It is up to you to determine which is it or check the conditions when the job is run.
The opposing idea that the job shouldn't run at all if the conditions aren't met until end is therefore false.
Schedule a one-off task when network is connected
Your app can schedule a one-off task to execute only when the user is connected to an unmetered (non-cellular) network, in order to conserve your users' data. The example below shows a task that will execute as soon as the user has an unmetered connection, or at latest one hour after scheduling:
OneoffTask task = new OneoffTask.Builder()
.setService(MyTaskService.class)
.setTag(TASK_TAG_WIFI)
.setExecutionWindow(0L, 3600L)
.setRequiredNetwork(Task.NETWORK_STATE_UNMETERED)
.build();
mGcmNetworkManager.schedule(task);
Source: https://developers.google.com/cloud-messaging/network-manager
Related
I'm using the latest Xamarin Forms 3.1.0 and I'm trying to schedule a daily send of information back to the server.
I'm using the JobSchedulerService with SetPeriodic(24 * 3600 * 1000)
which references the JobService below.
This starts and runs ok but keeps on rescheduling and runs at ever decreasing intervals (as you might expect for a failing task being rescheduled at ever longer intervals).
However, there is nothing in the logcat which indicates an error occurring.
The Logger statements are just an attempt to track what's happening, but I am always seeing the "FINISHED:" statement and never the "FAULTED" message so the task does not seem to be failing
What am I doing wrong?
public class AdherenceReportingJob : JobService
{
static readonly string logClassName = typeof(AdherenceReportingJob).ToString();
public override bool OnStartJob(JobParameters jobParams)
{
Logger.Audit(logClassName, "START");
Task.Factory.StartNew(() =>
{
bool updateRan = false;
updateRan = AdherenceCalculator.AdherenceReporting();
// Have to tell the JobScheduler the work is done.
JobFinished(jobParams, false);
Logger.Audit(logClassName, "FINISHED:" + updateRan);
}).ContinueWith(t =>
{
Logger.Error(logClassName, "FAULTED");
Logger.Error(logClassName, t.Exception.InnerException);
JobFinished(jobParams, true);
}, TaskContinuationOptions.OnlyOnFaulted);
// Return true because of the asynchronous work
return true;
}
public override bool OnStopJob(JobParameters jobParams)
{
// we don't want to reschedule the job if it is stopped or cancelled.
Logger.Audit(logClassName, "START");
return false;
}
}
The job is scheduled using:
var javaClass = Java.Lang.Class.FromType(typeof(AdherenceReportingJob));
var componentName = new ComponentName(Application.Context, javaClass);
var jobBuilder = new JobInfo.Builder(ADHERENCE_REPORT_JOB, componentName);
jobBuilder.SetPeriodic(App.OneDay);
jobBuilder.SetRequiredNetworkType(NetworkType.Any); // Need network to report back
var jobInfo = jobBuilder.Build();
var jobScheduler = (JobScheduler) Application.Context.GetSystemService(JobSchedulerService);
var scheduleResult = jobScheduler.Schedule(jobInfo);
if (JobScheduler.ResultSuccess == scheduleResult)
{
Logger.Debug(logClassName, "Scheduled OK");
}
else
{
Logger.Error(logClassName, "Scheduled FAILED");
}
This always reports "Scheduled OK"
Android Oreo has imposed many restrictions on running background service. Services now don't behave like normal in Oreo as they used to before.
But what if I have to run a service in background for long periods of time.
I am developing an application to launch the flashlight when user shakes the phone. To achieve this I will have to put the Sensor listener code inside a service.
How do I prevent android system to not kill the service.
PS: I don't want to start a foreground service with a notification.
How do I prevent android system to not kill the service.
To summarize the comments: Use a foreground service, with a notification on a dedicated channel, with the channel set to IMPORTANCE_DEFAULT. Advise the user that they can mute that channel (e.g., long-press on the Notification in the notification shade). Using a dedicated channel means that you can still raise notifications on other channels. Your notification should also be useful:
Have a "stop" action to stop your service, if the user wants to shut it down for a while
Tapping on the notification itself would lead to your activity for configuring your app's behavior
I don't want to start a foreground service with a notification.
Then most likely you cannot write your app.
I cannot rule out the possibility of some bug in Android 8.x that could be exploited to have an indefinite-duration service. In fact, I'd consider it to be fairly likely that there's something floating around out there. However, this is clearly against Google intentions, meaning:
Exploiting that technique, without what Google would consider to be valid justification, might get your app banned from the Play Store, if that was how you planned to distribute it
The bug might be fixed in a future version of Android, and getting in an arms race with Google tends to be a losing proposition
There are enough "air gesture" apps floating about (i.e., do things based on a shake) that, ideally, Google would add some dedicated low-power API for it. For example, they could add functionality to JobScheduler to allow you to register for a shake event and have your JobService be invoked in that circumstance, just as they allow you to register for changes in a ContentProvider. I have no idea whether they will ever offer such an API, but you could file a feature request for it, if you wanted.
Make a service unstoppable on Oreo or later without shown notification is possible (Yes We Can).
Let me to explain how make a service stoppable ONLY BY USER and not by system (or better to say THE ONLY WAY TO STOP THEM IS UNINSTALLING YOUR APP).
Note that even I make a service unstoppable in my point of view is not a good technique and I’m CONTRARY on that for different reasons (like battery consuming, clear user experience etc.)
First of all you need to declare the service in manifest file.
The separate name “:serviceNonStoppable” make the service running in a separate process and not in main app process. Is better for background processes which need to run separately.
To make our own service process invisible to other processes or apps you need to set exported=false parameter.
The description “#string/service_description” will say to users what your service do and why user should not stop them (you create this description in strings.xml).
<service
android:process=":serviceNonStoppable"
android:name="your.package.name.serviceOn"
android:exported="false"
android:description="#string/service_description" />
Secondly we go to create a support class with static methods usable in different points.
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import java.util.Map;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
class Utils {
// This is a support class witch have static methods to use everywhere
final static int NOTIFICATION_INT_CHANNEL_ID = 110211; // my daughter birthday but you can change that with your number
final static String NOTIFICATION_STRING_CHANNEL_ID = "put.a.random.id.here"; //if you write "the.pen.is.on.the.table" is the same
final static int TEST_THIS = 111; // or you can put here something else
final static String BROADCAST_MSG_ID = "BROADCAST_MSG_ID"; // or you can put here something else
final static String APP_MESSAGE = "your.package.name.action.APP_MESSAGE"; // or you can put here pippo.pluto.and.papperino
static void returnUpMyService(final Context context) {
try {
//to avoid crashes when this method is called by service (from itself) make sure the service is not alredy running (maybe is in cache)
if (killServiceIfRun(context)) {
startServiceOn(context);
}
} finally {
System.out.println(" I'm trying to start service ");
}
}
private static boolean killServiceIfRun(final Context context) {
boolean isRunning = isMyServiceRunning(context);
if (!isRunning) { return true; }
try {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
// maybe killing process is not terminated by system in this fase
//I force to kill them by my one
if (manager != null) {
manager.killBackgroundProcesses(getServicename(context));
return true;
}
return true;
} catch (Exception e) {
System.out.println("killServiceIfRun error: " + e.toString());
}
return false;
}
private static boolean isServiceInCache(final Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (manager != null && manager.getRunningAppProcesses() != null) {
if (manager.getRunningAppProcesses().size() > 0) {
for (ActivityManager.RunningAppProcessInfo process : manager.getRunningAppProcesses()) {
if (process.processName != null) {
if (process.processName.equalsIgnoreCase(getServicename(context))) {
// Here we know that the service is running but sleep brrrrrrrr
if (process.importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE) {
return true;
}
}
}
}
}
}
return false;
}
static void StartMyService(Context context) {
// If the sevice is running doesn't need to restart
if (isMyServiceRunning(context) && !isServiceInCache(context)) {
return;
}
// If service is running but is in chache is the same like killed, so we need to kill them
if (isServiceInCache(context)) {
// this method at first kill and after that start the service
returnUpMyService(context);
} else {
//Otherwise we start own service
startServiceOn(context);
}
}
private static void startServiceOn(final Context context) {
// After we had been sure about that service doesn't exist
// we make a schedule to restart them
new ScheduledThreadPoolExecutor(1).schedule(() -> {
//Create an instance of serviceOn
serviceOn service = new serviceOn();
//prepare the launch intent
Intent launchIntent = new Intent(context, service.getClass());
// Now we start in background our service
context.startForegroundService(launchIntent);
// I put 50 ms to allow the system to take more time to execute GC on my killed service before
}, 50, TimeUnit.MILLISECONDS);
}
private static boolean isMyServiceRunning(final Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (manager != null && manager.getRunningAppProcesses() != null) {
if (manager.getRunningAppProcesses().size() > 0) {
for (ActivityManager.RunningAppProcessInfo process : manager.getRunningAppProcesses()) {
if (process != null && process.processName != null && process.processName.equalsIgnoreCase(getServicename(context))) {
return true;
}
}
}
}
return false;
}
static void SendMsgToService(Context context, int id, Map<String, Object> params) {
try {
Intent mServiceIntent = new Intent(APP_MESSAGE);
if (params != null) {
for (Map.Entry<String, Object> entry : params.entrySet()) {
//System.out.println(entry.getKey() + "/" + entry.getValue());
if (entry.getValue() instanceof String) {
mServiceIntent.putExtra(entry.getKey(), (String) entry.getValue());
} else if (entry.getValue() instanceof Integer) {
mServiceIntent.putExtra(entry.getKey(), (Integer) entry.getValue());
} else if (entry.getValue() instanceof Float) {
mServiceIntent.putExtra(entry.getKey(), (Float) entry.getValue());
} else if (entry.getValue() instanceof Double) {
mServiceIntent.putExtra(entry.getKey(), (Double) entry.getValue());
} else if (entry.getValue() instanceof byte[]) {
mServiceIntent.putExtra(entry.getKey(), (byte[]) entry.getValue());
}
}
}
mServiceIntent.putExtra(BROADCAST_MSG_ID, id);
context.sendBroadcast(mServiceIntent);
} catch (RuntimeException e) {
System.out.println(e.toString());
}
}
private static String getServicename(final Context context) {
// the name declared in manifest you remember?
return context.getPackageName() + ":serviceNonStoppable";
}
}
This is service class witch extend IntentService.
import android.app.IntentService;
import android.app.Notification;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class serviceOn extends IntentService {
// Needed to keep up notifying without show the icon
private ScheduledExecutorService notifyer = null;
// don't remove this. cause error becouse we declare this service in manifest
public serviceOn() {
super("put.a.constant.name.here");
}
// We need this class to capture messages from main activity
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, Intent intent) {
if (intent != null) {
if (intent.getAction() != null) {
if (intent.getAction().equals(Utils.APP_MESSAGE)) {
int msgID = intent.getIntExtra(Utils.BROADCAST_MSG_ID, -1);
switch (msgID) {
case Utils.TEST_THIS:
String message = intent.getStringExtra("message");
if (!TextUtils.isEmpty(message)) {
System.out.println(message);
}
//Do your task here
//Do your task here
//Do your task here
//Do your task here
break;
}
}
}
}
}
};
#Override
protected void onHandleIntent(#Nullable Intent intent) { }
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
try {
// First of all we need to register our receiver
List<String> actions = Arrays.asList(
Utils.APP_MESSAGE, // this is the string which identify our mesages
Intent.ACTION_SCREEN_ON, // this event is raised on sreen ON by system
Intent.ACTION_SCREEN_OFF, // this event is raised on screen OFF by system
Intent.ACTION_TIME_TICK);// this event is raised every minute by system (helpful for periodic tasks)
for (String curIntFilter : actions) {
IntentFilter filter = new IntentFilter(curIntFilter);
registerReceiver(broadcastReceiver, filter);
}
} catch (RuntimeException e) {
e.printStackTrace();
}
final Notification notificationDefault = new NotificationCompat.Builder(getApplicationContext(), Utils.NOTIFICATION_STRING_CHANNEL_ID)
.setOngoing(true) //Ongoing notifications do not have an 'X' close button, and are not affected by the "Clear all" button
.setCategory(Notification.CATEGORY_SERVICE) // indicate this service is running in background
.setSmallIcon(R.drawable.ic_radio) // put here a drawable from your drawables library
.setContentTitle("My Service") // Put here a title for the notification view on the top
// A smaller explanation witch system show to user this service is running
// in background (if existing other services from other apps in background)
.setContentText("My Service is unstoppable and need to run in background ")
.build();
// This is an efficient workaround to lie the system if we don't wont to show notification icon on top of the phone but a little aggressive
notifyer = Executors.newSingleThreadScheduledExecutor();
notifyer.scheduleAtFixedRate(() -> {
try {
// Here start the notification witch system need to permit this service to run and take this on.
// And we repeat that task every 15 seconds
startForeground(Utils.NOTIFICATION_INT_CHANNEL_ID, notificationDefault);
//immediately after the system know about our service and permit this to run
//at this point we remove that notification (note that is never shown before)
stopForeground(true);
//better not invoke Exception classes on error, make all a little heavy
} finally {
// Log here to tell you your code is called
System.out.println(" Service is running");
}
// So, the first call is after 1000 millisec, and successively is called every 15 seconds for infinite
}, 1000, 15000, TimeUnit.MILLISECONDS);
}
#Override
public void onDestroy() {
// unregister the receiver
unregisterReceiver(broadcastReceiver);
// stop the notifyer
if (notifyer != null) {
notifyer.shutdownNow();
notifyer = null;
System.out.println(" notifyer.shutdownNow() ");
}
final Context context = getBaseContext();
try {
new Thread() {
#Override
public void run() {
// The magic but dirty part
// When the system detect inactivity by our service decides to put them in cache or kill it
// Yes system you can kill me but I came up stronger than before
Utils.returnUpMyService(context);
}
}.start();
} finally {
System.out.println("You stop me LOL ");
}
}
}
And here the usage.
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import java.util.HashMap;
class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Sstart the first time
Utils.StartMyService(this);
// Test after 3 seconds
new Handler().postDelayed(() -> {
Utils.SendMsgToService(X_App.getContext(), Utils.TEST_THIS, new HashMap<String, Object>() {{
put("message", "Hello from main activity");
}});
}, 3000);
}
}
I discovered that we can run forground service without showing notification for android oreo and above, here is the solution first create notification with notification Channel also set channel id for notifications then start forground service with notification. now it's time to cancel notification Channel with id after 1 or 2 second that's means the notification will remove and the service will run alwayes . that's all
You would not be able to run background services long running in Oreo as there are behaviour changes, now Oreo to optimise system memory, battery etc, it kills background service, to solve your issue you should use foreground service.
Have a look at Background execution limits
https://developer.android.com/about/versions/oreo/android-8.0-changes
Hope this helps in understanding the issue....
I have a web service on my server that needs to be pinged every hour. For this, I am using an Android app to ping it every hour. I have tried using Alarm manager but it stops working after few hours and if I swipe exit it. I have tried using service but for some reason, that doesn't seem to work and my app keeps crashing. I have am thinking about using Firebase Job dispatcher. My requirement is that the app needs to ping the web service on my server every hour. This should go on for at least next 3-4 months. Is there a way to accomplish this ? Thanks in advance!
EDIT: I have tried broadcast receiver with Alarm Manager but have not been able to sustain the firing for more then 4 hours.
I second Anantha's answer but seems like job parameters are little off for your needs.
You can go over this article to learn about the subtle differences between various Job schedulers.
As a matter of fact, even Google recommends using Firebase Job Schedular if the app needs to do a network communication due to various reasons. Please watch the attached video on the Github page for more info on the same. This also gives you basic code to kickstart your application. You can just change the job parameters to suit your needs
Hopefully, this below code should suit your requirement of triggering every one hour with a tolerance of 15 minutes
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(getContext()));
final int periodicity = (int)TimeUnit.HOURS.toSeconds(1); // Every 1 hour periodicity expressed as seconds
final int toleranceInterval = (int)TimeUnit.MINUTES.toSeconds(15); // a small(ish) window of time when triggering is OK
Job myJob = dispatcher.newJobBuilder()
// the JobService that will be called
.setService(yourJobService.class)
// uniquely identifies the job
.setTag("my-unique-tag")
// recurring job
.setRecurring(true)
// persist past a device reboot
.setLifetime(Lifetime.FOREVER)
// start between 0 and 60 seconds from now
.setTrigger(Trigger.executionWindow(periodicity, toleranceInterval))
// overwrite an existing job with the same tag
.setReplaceCurrent(true)
// 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_ANY_NETWORK
)
.setExtras(schedulerextras)
.build();
dispatcher.mustSchedule(myJob);
Jhon you can use firebase jobdispatcher. because it will support from api level 9. you can see below how to create job dispatcher and how to call it.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
scheduleJob(this);
}
public static void scheduleJob(Context context) {
//creating new firebase job dispatcher
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
//creating new job and adding it with dispatcher
Job job = createJob(dispatcher);
dispatcher.mustSchedule(job);
}
public static Job createJob(FirebaseJobDispatcher dispatcher){
Job job = dispatcher.newJobBuilder()
//persist the task across boots
.setLifetime(Lifetime.FOREVER)
//.setLifetime(Lifetime.UNTIL_NEXT_BOOT)
//call this service when the criteria are met.
.setService(ScheduledJobService.class)
//unique id of the task
.setTag("UniqueTagForYourJob")
//don't overwrite an existing job with the same tag
.setReplaceCurrent(false)
// We are mentioning that the job is periodic.
.setRecurring(true)
// Run between 30 - 60 seconds from now.
.setTrigger(Trigger.executionWindow(30, 60))
// retry with exponential backoff
.setRetryStrategy(RetryStrategy.DEFAULT_LINEAR)
//.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
//Run this job only when the network is available.
.setConstraints(Constraint.ON_ANY_NETWORK, Constraint.DEVICE_CHARGING)
.build();
return job;
}
public static Job updateJob(FirebaseJobDispatcher dispatcher) {
Job newJob = dispatcher.newJobBuilder()
//update if any task with the given tag exists.
.setReplaceCurrent(true)
//Integrate the job you want to start.
.setService(ScheduledJobService.class)
.setTag("UniqueTagForYourJob")
// Run between 30 - 60 seconds from now.
.setTrigger(Trigger.executionWindow(30, 60))
.build();
return newJob;
}
public void cancelJob(Context context){
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
//Cancel all the jobs for this package
dispatcher.cancelAll();
// Cancel the job for this tag
dispatcher.cancel("UniqueTagForYourJob");
}}
ScheduledJobService.java
public class ScheduledJobService extends JobService {
private static final String TAG = ScheduledJobService.class.getSimpleName();
#Override
public boolean onStartJob(final JobParameters params) {
//Offloading work to a new thread.
new Thread(new Runnable() {
#Override
public void run() {
codeYouWantToRun(params);
}
}).start();
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
return false;
}
public void codeYouWantToRun(final JobParameters parameters) {
try {
Log.d(TAG, "completeJob: " + "jobStarted");
//This task takes 2 seconds to complete.
Thread.sleep(2000);
Log.d(TAG, "completeJob: " + "jobFinished");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//Tell the framework that the job has completed and doesnot needs to be reschedule
jobFinished(parameters, true);
}
}}
You will need to use JobScheduler(api >21 ) and GcmNetworkManager (api<21) depending on the api level of android. Check out this library from evernote which takes care of it.
Do you try broadcast receiver? I use Broadcast Receiver with Alarm Manager to vibrate every minute and it work fine. The only problem is that when device turn off or restarted, it not vibrate till I enter my application.
My test code.
public void setAlarm() {
alarmMgr =(AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(AlarmManagerActivity.this, AlarmManagerBroadcastReceiver.class);
intent.setAction("a.b.c.d");
PendingIntent pi = PendingIntent.getBroadcast( getApplicationContext(), 0, intent, 0);
//After after 5 seconds
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 13);
calendar.set(Calendar.MINUTE, 40);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis()
, (1000) * (60)
, pi);
}
My receiver
#Override
public void onReceive(Context context, Intent intent) {
Log.d(getClass().getSimpleName(), Intent.ACTION_BOOT_COMPLETED);
if ( intent.getAction().equals("a.b.c.d")) {
Log.d(getClass().getSimpleName(), "Custom Broadcast01");
Vibrator vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(10000);
}
else
Log.d(getClass().getSimpleName(), "no this action for intent!");
}
Broadcast receiver to start Alarm when device restart
<receiver
android:name=".OnBootBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class OnBootBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
setAlarm();
}
}
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.
I am learning how to use JobScheduler. as shown in onresume method, I set the criteria to be met in order to execute the job, the
job will be scheduled when the device is not charging, no matter the device is idle or not and the job will be executed every
7 seconds.
at run time, the usb cable is connected to the device in order to install the App which means the device is charging, so after
installing the App the job have not started because the device is charging, but after i unplug the usb cable i exected the job
to be executed but what happened is that the job never started, and i could not understand why
please let me know why such behavior is happeneing and please let me know the answer of the following question it will help me to
better understand the jobScheduler:
Q: is setRequiresCharging(false) means, that the task will be executed only if the device is NOT charging or it means that
the task will be executed no matter if the device is charging or not?
main activity
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static int jobId = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.w(TAG, "onCreate");
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
super.onResume();
Log.w(TAG, "onResume");
ComponentName serviceComponent = new ComponentName(this, MyJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(jobId, serviceComponent);
builder.setRequiresCharging(false);
builder.setRequiresDeviceIdle(false);
builder.setPeriodic(7 * 1000);
JobScheduler jobScheduler = (JobScheduler) getApplication().getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(builder.build());
}
}
jobService:
package example.com.jobscheduler_00;
public class MyJobService extends JobService {
private static final String TAG = MyJobService.class.getSimpleName();
#Override
public boolean onStartJob(JobParameters params) {
Log.w(TAG, "onStartJob JobId: " + params.getJobId());
Toast.makeText(this, "onStartJob JobId:" + params.getJobId(), Toast.LENGTH_SHORT).show();
jobFinished(params, false);
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
Log.w(TAG, "onStopJob");
Toast.makeText(this, "onStopJob JobId:" + params.getJobId(), Toast.LENGTH_SHORT).show();
return true;
}
}
Is setRequiresCharging(false) means, that the task will be executed only if the device is NOT charging or it means that the task will be executed no matter if the device is charging or not?
From the documentation:
Specify that to run this job, the device needs to be plugged in. This defaults to false.
In other words, if you want your job to be run only in condition, when the device is charging - you should pass true. By default it is false, which means charging criteria is disregarded, i.e. your job will be executed regardless the device is charging or no (assuming other criterias are fulfilled).
You may check whether your job has started successfully by the int value that JobScheduler.schedule(JobInfo job) returns. It will return either RESULT_SUCCESS or RESULT_FAILURE.
The new WorkManager API will help you set necessary constraints for the task you want to schedule.
Have a look at this video for a brief intro - https://www.youtube.com/watch?v=pErTyQpA390 (WorkManager at 21:44).
EDIT: Adding an example to showcase the capabilities of the new API
For eg:
You can set constraints related to charging state of the device like this (along with other constraints like if the device is supposed to be idle for the task to run etc..)
// Create a Constraints that defines when the task should run
Constraints yourConstraints = new Constraints.Builder()
.setRequiresDeviceIdle(true/false)
.setRequiresCharging(true/false)
// Many other constraints are available
.build();
// ...then create a OneTimeWorkRequest that uses those constraints
OneTimeWorkRequest yourWork =
new OneTimeWorkRequest.Builder(YourWorkerClass.class)
.setConstraints(yourConstraints)
.build();