I have the following problem: I register my alarm manager in onCreate, It gets executed each minute now. However if i kill the app via the Android taskmanager (so the app state is destoyed) the AlarmReceiver stops excecuting. Why?
My Code:
AlarmReceiver
public class AlarmReceiver extends WakefulBroadcastReceiver {
private AlarmManager mAlarm;
private PendingIntent mAlarmIntent;
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, PortalPullService.class);
startWakefulService(context, service);
}
public void setAlarm(Context context) {
mAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
mAlarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
int interval = context.getResources().getInteger(R.integer.update_interval_in_mins) * 60 * 1000;
mAlarm.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + interval,
interval,
mAlarmIntent);
ComponentName reciever = new ComponentName(context, AlarmBootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(reciever,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
public void cancelAlarm(Context context) {
if(mAlarm != null) {
mAlarm.cancel(mAlarmIntent);
}
ComponentName reciever = new ComponentName(context, AlarmBootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(reciever,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
}
PullService
public class PortalPullService extends IntentService {
private static final String LOG_TAG = "PortalPullService";
public PortalPullService() {
super(LOG_TAG);
}
#Override
protected void onHandleIntent(Intent intent) {
//TODO make request to ** check if new Infos are available, then send notification
Helper.sendNotification(this, "Test", "Testnotification"); //My test if this works
AlarmReceiver.completeWakefulIntent(intent);
}
}
AndroidManifest.xml
<receiver android:name=".PortalUpdate.AlarmReceiver" />
<service
android:name=".PortalUpdate.PortalPullService"
android:exported="false" />
The receiver gets registered via new AlarmReceiver().setAlarm(this);
I searched on sveral SO questions, but i can't find an answer... I don't know where is my fault...
Thanks in advance ;)
Okay it's a problem to install a debug version of your app on Huawei.
Because you can disable Background Services in Huawei, and Huawei doesn't recognize a debug version of your app as a proper app, it destroys all services maybe cause of security reasons ...
Installing it properly over Play Store helps!
Because the PendingIntent scheduled is a token associated with your app, and if the app is forcefully killed, stopped or hibernated, it will be removed by the system. You can set it up again in onResume or onCreate when the user starts your app again.
Related
I'm doing an Android app that requires sending its location frequently, every 1 minute or 2 minutes at the most. For this, I use a JobSchedulerService. I've already managed to make it run more than once every 15 minutes on devices with Android N version by replacing the .setPeriodic() with a .setMinimumLatency(). The fact is that at the beginning it is executed periodically in the established time, but after a while it runs every 7 or 9 minutes approximately.
I have already included the application in the battery saving white list, but didn't work. Is there any way to execute it or a similar service every minute with no restrictions? Doesn't matter how much battery the app spends.
EDIT:
This is what I've tried:
ReceiverService:
public class ReceiverService extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context ctx, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
if (!isMyServiceRunning(ServiceBackground.class, ctx))
startWakefulService(ctx, new Intent(ctx, ServiceBackground.class));
new ServiceAlarmManager(ctx).register();
}
}
private boolean isMyServiceRunning(Class<?> serviceClass,Context context) {
ActivityManager manager = (ActivityManager)context. getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.i("Service already","running");
return true;
}
}
Log.i("Service not","running");
return false;
}
}
The ServiceAlarmManager is exactly the same as #madking said.
You can put your code that sends location in a Service and implement an AlarmManager that periodically checks if your Service is running and restarts it if the Service has been killed by OS. You'll have to implement the AlarmManager using a WakefulBroadcastReceiver.
ReceiverService.java
public class ReceiverService extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context ctx, Intent intent) {
if (!YourService.isRunning()) {
startWakefulService(ctx, new Intent(ctx, YourService.class));
}
new ServiceAlarmManager(ctx).register();
}
}
ServiceAlarmManager.java
public class ServiceAlarmManager {
private Context ctx;
private static final int TIME_INTERVAL = 300 * 1000;
public ServiceAlarmManager(Context context) {
ctx = context;
}
public void register() {
Intent serviceRestarter = new Intent();
serviceRestarter.setAction("someString");
PendingIntent pendingIntentServiceRestarter = PendingIntent.getBroadcast(ctx, 0, serviceRestarter, 0);
AlarmManager alarmManager = (AlarmManager) ctx.getSystemService(ctx.ALARM_SERVICE);
Date now = new Date();
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, now.getTime() + TIME_INTERVAL, pendingIntentServiceRestarter);
}
}
Also register your BroadcastReceiver in your Manifest.xml file
<receiver android:name=".ReceiverService">
<intent-filter>
<action android:name="someString" />
</intent-filter>
</receiver>
The register() method does two things.
1- Issues a broadcast which is caught by WakefulBroadcastReceiver and restarts the Service if required
2- Sets the next alarm to be invoked to check if the Service has been killed.
This way the service keeps running even if the OS kills it and you'll be able to send location updates periodically.
Note: Though this practice is not recommended as your application will use more battery but you don't seem to care about it as I did not either as some business requirements don't leave us a choice.
I tried this and it works: in the onCreate() of your activity you schedule an Alarm for every minute (setAlarm). Everytime the alarm is triggered, WakefulBroadcastReceiver is called, and that's where we launch our service(s):
private static long INTERVAL_ALARM = 1 * 60 * 1000;
public static void setAlarm(Context context) {
long current_time = Calendar.getInstance().getTimeInMillis();
Intent myAlarm = new Intent(context.getApplicationContext(), AlarmReceiver.class);
PendingIntent recurringAlarm = PendingIntent.getBroadcast(context.getApplicationContext(), 0, myAlarm, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.RTC_WAKEUP, current_time, INTERVAL_ALARM, recurringAlarm);
}
And in the receiver:
public class AlarmReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent myService = new Intent(context, MyService.class);
context.startService(myService);
}
}
In your service, you should stopSeflf() in the end of your treatment.
Don't forget to register your BroadcastReceiver in your Manifest.xml file
NB: WakefulBroadcastReceiver is deprecated in API level 26.1.0. JobSchedulerService does the work
My alarm is killed when OS kills the app. I thought that was one of the points of an Alarm, that it would keep running even though OS killed the app? I check the life of the Alarm using the "./adb shell dumpsys alarm" command, and every time OS kills my app, the Alarm is also gone. How I start my Alarm:
public static void startLocationAlarm(Context context){
if(ActivityLifecycleHandler.isApplicationInForeground()) {
return; // If App is in foreground do not start alarm!
}
String alarm = Context.ALARM_SERVICE;
AlarmManager am = ( AlarmManager ) context.getSystemService( alarm );
Intent intent = new Intent(locationBroadcastAction);
PendingIntent pi = PendingIntent.getBroadcast( context.getApplicationContext(), 0, intent, 0 );
int type = AlarmManager.ELAPSED_REALTIME_WAKEUP;
long interval = ONE_MINUTE;
long triggerTime = SystemClock.elapsedRealtime() + interval;
am.setRepeating(type, triggerTime, ONE_MINUTE, pi );
}
To add some more context, I am trying do some location operation in a service (not IntentService) in background. Here is my receiver. Used Wakeful because I did not want the service to be killed before it was done.
public class LocationBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent ) {
Intent myIntent = new Intent( context, LocationServiceAlarmOwnGoogleClient.class );
//context.startW( myIntent );
LocationBroadcastReceiver.startWakefulService(context, myIntent);
}
}
For some more information: I cancel the alarm in OnStart method of several activities that the user can return to after having it in the background. I do not know if that can cause this weird behaviour? Here is my cancel method:
public static void stopLocationAlarm(Context context){
Intent intent = new Intent(locationBroadcastAction);
PendingIntent sender = PendingIntent.getBroadcast(context.getApplicationContext(), 0, intent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(context.ALARM_SERVICE);
alarmManager.cancel(sender);
}
You can add service which listens to the phone's turning on callback.
add this permission into the manifest
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
and register reciever
<receiver android:name=".util.notification.local.MyBootCompletedService">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
public class MyBootCompletedService extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
AlarmReceiver.startLocalNotificationService(context);
}
}
The error that caused the Alarm to be canceled had actually nothing to do with the code, but had to do with special battery settings on Huawei devices. If your app is not set as "protected" in "protected apps", the system will cancel your alarm when it kills the app. Adding your app to "protected apps" will solve this problem. Same goes for Xiaomi devices. Have to add them to "Protected apps", then the Alarm will work as intended. Thank you #CommonsWare for leading me to the solution.
I'm stumped. I know this question has already been answered a hundred times but nothing I've tried works.
My question: I made an Android widget that needs to refresh precisely at each minute, much like all clock widgets do. (This widget tells me in how many minutes are left before my train leaves, a one minute error makes it useless).
Here are my attempts to far, and the respective outcomes:
I put android:updatePeriodMillis="60000" in my appwidget_info.xml. However, as specified in API Docs, "Updates requested with updatePeriodMillis will not be delivered more than once every 30 minutes" and indeed that's about how often my widget gets updated.
I tried using an AlarmManager. In my WidgetProvider.onEnabled:
AlarmManager am = (AlarmManager)context.getSystemService
(Context.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
long now = System.currentTimeMillis();
// start at the next minute
calendar.setTimeInMillis(now + 60000 - (now % 60000));
am.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis(), 60000,
createUpdateIntent(context));
however as stated in the API docs, "as of API 19, all repeating alarms are inexact" and indeed my widget actually gets updated every five minutes or so.
Based on the previous point I tried setting targetSdkVersion to 18 and saw no difference (updates every five minutes or so).
The setRepeating documentation seems to recommend using setExact. I tried the following. At the end of my update logic:
Calendar calendar = Calendar.getInstance();
long now = System.currentTimeMillis();
long delta = 60000 - (now % 60000);
Log.d(LOG_TAG, "Scheduling another update in "+ (delta/1000) +" seconds");
calendar.setTimeInMillis(now + delta);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setExact(AlarmManager.RTC, calendar.getTimeInMillis(), //UPDATE_PERIOD_SECONDS * 1000,
createUpdateIntent(context));
It works perfectly for a couple minutes and then reverts to updating every five minutes or so (and not even near minute changes). Here are some timestamps of when the update intent is received:
21:44:17.962
21:52:37.232
21:59:13.872
22:00:00.012 ← hey suddenly it becomes exact again??
22:01:47.352
22:02:25.132
22:06:56.202
Some recommend using a Handler. I defined a Service which I start when the widget provider is enabled, and does this after update code:
int delay = (int)(60000 - (System.currentTimeMillis() % 60000));
Log.d(LOG_TAG, "Scheduling another update in " + delay/1000 + " seconds");
new Handler().postDelayed(new Runnable() {
public void run() {
Log.d(LOG_TAG, "Scheduled update running");
updateAppWidget();
}
}, delay);
and this one works perfectly for several hours, but then the service gets suddenly killed and gets "scheduled to restart after HUGE delay". Concretely, the widget just gets stuck at some point and doesn't get updated at all.
Some other options I've seen online: the linked post above suggests creating a foreground service (which, if I understand correctly, means having a permanently visible icon in my already crowded status bar. I don't have one permanent icon for each clock widget I use so that should not be necessary). Another suggestion is to run a high priority thread from the service, which feels awfully overkill.
I've also seen recommendations to use Timers and BroadcastReceivers but the former is said to be "not appropriate for the task" and I remember having trouble doing the latter. I think I had to do it in a service and then the service gets killed just like when I use Handlers.
It should be noted that the AlarmManager seems to work well when the phone is connected to the computer (presumably because it means the battery is charging), which doesn't help because most of the time I want to know when my train will leave is when I'm already on the way...
As the Handler is perfectly accurate but just stops working after a while, and the AlarmManager option is too inaccurate but does not stop working, I'm thinking of combining them by having AlarmManager start a service every ten minutes or so, and have that service use a Handler to update the display each minute. Somehow I feel this will get detected by Android as a power hog and get killed, and anyway I'm sure I must be missing something obvious. It shouldn't be that hard to do what's essentially a text-only clock widget.
EDIT: if it matters, I'm using my widget on a Samsung Galaxy Note 4 (2016-06-01) with Android 6.0.1.
Sorry, i totally forgot, was busy.. Well, i hope you got the idea of what you need, snippets are following, hope i dod not forgot something.
on the widget provider class.
public static final String ACTION_TICK = "CLOCK_TICK";
public static final String SETTINGS_CHANGED = "SETTINGS_CHANGED";
public static final String JOB_TICK = "JOB_CLOCK_TICK";
#Override
public void onReceive(Context context, Intent intent){
super.onReceive(context, intent);
preferences = PreferenceManager.getDefaultSharedPreferences(context);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName thisAppWidget = new ComponentName(context.getPackageName(), WidgetProvider.class.getName());
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget);
if (intent.getAction().equals(SETTINGS_CHANGED)) {
onUpdate(context, appWidgetManager, appWidgetIds);
if (appWidgetIds.length > 0) {
restartAll(context);
}
}
if (intent.getAction().equals(JOB_TICK) || intent.getAction().equals(ACTION_TICK) ||
intent.getAction().equals(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
|| intent.getAction().equals(Intent.ACTION_DATE_CHANGED)
|| intent.getAction().equals(Intent.ACTION_TIME_CHANGED)
|| intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
restartAll(context);
onUpdate(context, appWidgetManager, appWidgetIds);
}
}
private void restartAll(Context context){
Intent serviceBG = new Intent(context.getApplicationContext(), WidgetBackgroundService.class);
context.getApplicationContext().startService(serviceBG);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
scheduleJob(context);
} else {
AppWidgetAlarm appWidgetAlarm = new AppWidgetAlarm(context.getApplicationContext());
appWidgetAlarm.startAlarm();
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void scheduleJob(Context context) {
ComponentName serviceComponent = new ComponentName(context.getPackageName(), RepeatingJob.class.getName());
JobInfo.Builder builder = new JobInfo.Builder(0, serviceComponent);
builder.setPersisted(true);
builder.setPeriodic(600000);
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
int jobResult = jobScheduler.schedule(builder.build());
if (jobResult == JobScheduler.RESULT_SUCCESS){
}
}
#Override
public void onEnabled(Context context){
restartAll(context);
}
#Override
public void onDisabled(Context context){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.cancelAll();
} else {
// stop alarm
AppWidgetAlarm appWidgetAlarm = new AppWidgetAlarm(context.getApplicationContext());
appWidgetAlarm.stopAlarm();
}
Intent serviceBG = new Intent(context.getApplicationContext(), WidgetBackgroundService.class);
serviceBG.putExtra("SHUTDOWN", true);
context.getApplicationContext().startService(serviceBG);
context.getApplicationContext().stopService(serviceBG);
}
WidgetBackgroundService
public class WidgetBackgroundService extends Service {
private static final String TAG = "WidgetBackground";
private static BroadcastReceiver mMinuteTickReceiver;
#Override
public IBinder onBind(Intent arg0){
return null;
}
#Override
public void onCreate(){
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(intent != null) {
if (intent.hasExtra("SHUTDOWN")) {
if (intent.getBooleanExtra("SHUTDOWN", false)) {
if(mMinuteTickReceiver!=null) {
unregisterReceiver(mMinuteTickReceiver);
mMinuteTickReceiver = null;
}
stopSelf();
return START_NOT_STICKY;
}
}
}
if(mMinuteTickReceiver==null) {
registerOnTickReceiver();
}
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
#Override
public void onDestroy(){
if(mMinuteTickReceiver!=null) {
unregisterReceiver(mMinuteTickReceiver);
mMinuteTickReceiver = null;
}
super.onDestroy();
}
private void registerOnTickReceiver() {
mMinuteTickReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent){
Intent timeTick=new Intent(WidgetProvider.ACTION_TICK);
sendBroadcast(timeTick);
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_SCREEN_ON);
registerReceiver(mMinuteTickReceiver, filter);
}
}
RepeatingJob class
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class RepeatingJob extends JobService {
private final static String TAG = "RepeatingJob";
#Override
public boolean onStartJob(JobParameters params) {
Log.d(TAG, "onStartJob");
Intent intent=new Intent(WidgetProvider.JOB_TICK);
sendBroadcast(intent);
return false;
}
#Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
AppWidgetAlarm class
public class AppWidgetAlarm {
private static final String TAG = "AppWidgetAlarm";
private final int ALARM_ID = 0;
private static final int INTERVAL_MILLIS = 240000;
private Context mContext;
public AppWidgetAlarm(Context context){
mContext = context;
}
public void startAlarm() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MILLISECOND, INTERVAL_MILLIS);
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(WidgetProvider.ACTION_TICK);
PendingIntent removedIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
Log.d(TAG, "StartAlarm");
alarmManager.cancel(removedIntent);
// needs RTC_WAKEUP to wake the device
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), INTERVAL_MILLIS, pendingIntent);
}
public void stopAlarm()
{
Log.d(TAG, "StopAlarm");
Intent alarmIntent = new Intent(WidgetProvider.ACTION_TICK);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
}
}
manifest
<receiver android:name=".services.SlowWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<intent-filter>
<action android:name="CLOCK_TICK" />
</intent-filter>
<intent-filter>
<action android:name="JOB_CLOCK_TICK" />
</intent-filter>
<intent-filter>
<action android:name="SETTINGS_CHANGED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.TIME_SET" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.DATE_CHANGED" />
</intent-filter>
<intent-filter>
<action android:name="android.os.action.DEVICE_IDLE_MODE_CHANGED"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.ACTION_DREAMING_STOPPED" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/slow_widget_info" />
</receiver>
<service
android:name=".services.RepeatingJob"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"/>
<service android:name=".services.WidgetBackgroundService" />
The code snippets provided by #Nikiforos was a blessing for me, although I've felt into many problems when using them on Android 8, thus I decided to let you know how I've solved my issues. There are two problems related with the snippets provided:
they use BackgroundService which is now forbidden in some cases in Android 8
they use implicit broadcasts which have also been restricted in Android O (you can read about why it happened here)
To address first issue I had to switch from BackgroundService to ForegroundService. I know this is not possible in many cases, but for those who can do the change here are the instructions to modify the codes:
Change the restartAll() function as follows:
private void restartAll(Context context){
Intent serviceBG = new Intent(context.getApplicationContext(), WidgetBackgroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// for Android 8 start the service in foreground
context.startForegroundService(serviceBG);
} else {
context.startService(serviceBG);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
scheduleJob(context);
} else {
AppWidgetAlarm appWidgetAlarm = new AppWidgetAlarm(context.getApplicationContext());
appWidgetAlarm.startAlarm();
}
}
Update the onStartCommand() function in your WidgetBackgroundService code:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// for Android 8 bring the service to foreground
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startForeground(1, buildForegroundNotification("Test 3"));
if(intent != null) {
if (intent.hasExtra("SHUTDOWN")) {
if (intent.getBooleanExtra("SHUTDOWN", false)) {
if(mMinuteTickReceiver!=null) {
unregisterReceiver(mMinuteTickReceiver);
mMinuteTickReceiver = null;
}
stopSelf();
return START_NOT_STICKY;
}
}
}
if(mMinuteTickReceiver==null) {
registerOnTickReceiver();
}
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
Add sendImplicitBroadcast() function to your WidgetBackgroundService:
private static void sendImplicitBroadcast(Context ctxt, Intent i) {
PackageManager pm=ctxt.getPackageManager();
List<ResolveInfo> matches=pm.queryBroadcastReceivers(i, 0);
for (ResolveInfo resolveInfo : matches) {
Intent explicit=new Intent(i);
ComponentName cn=
new ComponentName(resolveInfo.activityInfo.applicationInfo.packageName,
resolveInfo.activityInfo.name);
explicit.setComponent(cn);
ctxt.sendBroadcast(explicit);
}
}
Modify registerOnTickReceiver() function in the following way:
private void registerOnTickReceiver() {
mMinuteTickReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent){
Intent timeTick=new Intent(LifeTimerClockWidget.ACTION_TICK);
// for Android 8 send an explicit broadcast
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
sendImplicitBroadcast(context, timeTick);
else
sendBroadcast(timeTick);
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_SCREEN_ON);
registerReceiver(mMinuteTickReceiver, filter);
}
Hope it helps!
Use the widget itself as the host for the delayed runnable. Widgets have a postDelayed method.
If the widget is killed and recreated, then also recreate the runnable as part of the basic initialization.
Edit:
The above suggestion was based on the inaccurate assumption that the OP was writing a custom view, not an app widget. For an app widget my best suggestion is:
create a foreground service with ONE icon.
the service manages all widgets and clicking on the notification icon will show the various reminders that are active and/allow them to be managed
There is no correct and fully working answer to widget update every minute. Android OS developer purposely exclude such feature or api in order to save the battery and workload.
For my case, I tried to create clock homescreen appwidget and tried many attempt on alarm manager, service etc.
None of them are working correctly.
For those who want to create Clock Widget, which need update time everyminute precisely.
Just use
<TextClock
android:id="#+id/clock"
style="#style/widget_big_thin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top"
android:ellipsize="none"
android:format12Hour="#string/lock_screen_12_hour_format"
android:format24Hour="#string/lock_screen_24_hour_format"
android:includeFontPadding="false"
android:singleLine="true"
android:textColor="#color/white" />
for digital clock text view and
For Analog Clock
<AnalogClock xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/analog_appwidget"
android:dial="#drawable/appwidget_clock_dial"
android:hand_hour="#drawable/appwidget_clock_hour"
android:hand_minute="#drawable/appwidget_clock_minute"
android:layout_width="match_parent"
android:layout_height="match_parent" />
I've found those code from Google Desk Clock Opensource Project. You may already know Google Clock has such widget which update precisely every minute.
To learn more
Google Desk Clock Opensource Repo
Try this code
Intent intent = new Intent(ACTION_AUTO_UPDATE_WIDGET);
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) + 1);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmMgr.setInexactRepeating(AlarmManager.RTC, calendar.getTimeInMillis(), 60 * 1000, alarmIntent);
I am trying to figure out how alarm timers work so I can trigger an event when a user selects the predefined times in the app. To start off I just want to show a toast so I can clearly see the app is working. But when I run the app and set the time for 10 seconds the class handling my Intent never seems to get called.
I am using Log.d in the Main and I can see it being logged correctly when the button is clicked. But the event does not fire off at the selected time.
This is the function that fires off when the button is clicked and the Log.d is displayed in the console.
public void scheduleAlarm()
{
Long time = System.currentTimeMillis() + 10000;
Log.d("logs", "This is running in the main act");
Intent intentAlarm = new Intent(this, affirmationSchedule.class);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, time, PendingIntent.getBroadcast(this, 1, intentAlarm, PendingIntent.FLAG_UPDATE_CURRENT));
Toast.makeText(this, "Alarm Has Been Scheduled", Toast.LENGTH_LONG).show();
}
And this is the class which handles the code to run when the alarm time comes
public class affirmationSchedule extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("logs", "This function is running");
Toast.makeText(context, "this is a toast working.", Toast.LENGTH_LONG).show();
}
}
Log.d never displays. the toast in this class never displays.
This leads me to believe I am not creating my object correctly.
This is how I am registering receiver in the manifest.
<receiver
android:name="com.wuno.wunoaffirmations"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="com.wuno.BroadcastReceiver" />
</intent-filter>
</receiver>
Any ideas?
This might be relevant,
After I click the button and the original toast goes away. This pops up in console.
05-16 23:10:11.989 14242-14268/com.wuno.wunoaffirmations E/Surface: getSlotFromBufferLocked: unknown buffer: 0xb4015c60
But not in ten seconds. More like 5.The alarmManager is set for 10 seconds.
This how i used alarm manager within a project of mine. Basically i followed some code from a google's app code. so here it is. I hope this will help you.
How to use it? Well just Create instance of AlramReciver and then set it.
private AlarmReceiver alarmReceiver = new AlarmReceiver();
alramReceiver.setAlram();
This is helper class which set the alram receiver.
public class AlarmReceiver extends WakefulBroadcastReceiver {
private static AlarmManager alarmManager;
private static PendingIntent alarmIntent;
#Override
public void onReceive(Context context, Intent intent) {
/*
* If your receiver intent includes extras that need to be passed along to the
* service, use setComponent() to indicate that the service should handle the
* receiver's intent. For example:
*
* ComponentName comp = new ComponentName(context.getPackageName(),
* MyService.class.getName());
*
* // This intent passed in this call will include the wake lock extra as well as
* // the receiver intent contents.
* startWakefulService(context, (intent.setComponent(comp)));
*
* In this example, we simply create a new intent to deliver to the service.
* This intent holds an extra identifying the wake lock.
*/
Intent service= new Intent(context, AlarmService.class);
startWakefulService(context,service);
}
/**
*set the alram
* #param context
*/
public void setAlarm(Context context){
alarmManager=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent =PendingIntent.getBroadcast(context,0,intent,0);
alarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis(), 1000 * 60, alarmIntent);
ComponentName receiver = new ComponentName(context, BootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
/**
* cancels the alram
* #param context
*/
public void cancelAlarm(Context context){
// If the alarm has been set, cancel it.
if (alarmManager!= null) {
alarmManager.cancel(alarmIntent);
}
// Disable {#code SampleBootReceiver} so that it doesn't automatically restart the
// alarm when the device is rebooted.
ComponentName receiver = new ComponentName(context, BootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
}
This is bootReceiver class used when your device goes off and switch on again
public class BootReceiver extends BroadcastReceiver {
AlarmReceiver alarmReceiver = new AlarmReceiver();
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED"))
{
alarmReceiver.setAlarm(context);
}
}
}
This is intent service class here you have to write your logic for your app.
public class AlarmService extends IntentService {
public AlarmService() {
super("AlarmService");
}
#Override
protected void onHandleIntent(Intent intent) {
//Write the logice here
AlarmReceiver.completeWakefulIntent(intent); // this tell if the related work is complete then system tracks for another alram.
}
Last you have to make changes in your manifest.
<service android:name="AlarmService" />
<receiver android:name="AlarmReceiver" />
<receiver android:name="BootReceiver" />
I hope this will help you atleast. P.s you don't have to post same question twice.
public void scheduleAlarm()
{
Calendar cal = Calendar.getInstance();
Log.d("logs", "This is running in the main act");
Intent intentAlarm = new Intent(this, affirmationSchedule.class);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, cal + 10000, PendingIntent.getBroadcast(this, 1, intentAlarm, PendingIntent.FLAG_UPDATE_CURRENT));
Toast.makeText(this, "Alarm Has Been Scheduled", Toast.LENGTH_LONG).show();
}
Broadcast Receiver
public class affirmationSchedule extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("logs", "This function is running");
Toast.makeText(context, "this is a toast so this is working.", Toast.LENGTH_LONG).show();
}
}
In manifest
<receiver
android:name="com.wuno.affirmationSchedule"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="com.wuno.BroadcastReceiver" />
</intent-filter>
</receiver>
I'm making an app that uses an Alarm service. I'm still learning how it works but one thing is very unclear and explained nowhere.
Say you create an Alarm when you launch your app. The alarm is saved somewhere because it needs to trigger even when your app is not running, right?
If so, how can I get this alarm when relaunching my app, so I don't create a new one everytime and have an infinity of alarms stored somewhere?
If not, how does it work? I was thinking about using a database or a json file but I have a feeling it's not necessary.
In my MainActivity class, I have this code to check if the alarm exists already (this code is obviously wrong)...
AlarmReceiver alarm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button b = (Button) findViewById(R.id.button);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(alarm != null){
alarm.cancel();
}
alarm = new AlarmReceiver(MainActivity.this);
}
});
}
I have set a BroadcastReceiver for when the device is rebooted (as explained in the android tutorial)
public class SampleBootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
new AlarmReceiver(context);
}
}
}
This is the AlarmReceiver class itself:
public class AlarmReceiver {
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
public AlarmReceiver(Context context){
alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmBroadcastReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 17);
calendar.set(Calendar.MINUTE, 30);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
1000 * 60 * 20, alarmIntent);
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
public void cancel(){
alarmMgr.cancel(alarmIntent);
}
}
And the AlarmBroadcastReceiver that simply launches a notification (which works):
public class AlarmBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
new NotificationMessage(context);
}
}
The alarm is saved somewhere because it needs to trigger even when your app is not running, right?
Correct.
how can I get this alarm when relaunching my app
You don't. It's a write-only API.
so I don't create a new one everytime and have an infinity of alarms stored somewhere?
Only create an alarm when it is needed, not on every run of your app.
Beyond that, use an equivalent PendingIntent to an existing alarm when calling the AlarmManager methods to replace that alarm (or using cancel() to cancel the alarm).
I was thinking about using a database or a json file but I have a feeling it's not necessary.
You need enough information in persistent storage to know what to do when the alarm goes off. You also need enough information in persistent storage to know what alarms are needed, to handle reboots, when you have to reschedule your previously-scheduled alarms.