Reputation: 42690
Since I'm using AlarmManager
to perform periodical widget update, I need to ensure onEnabled
& onDisabled
will work reliably.
However, I realize they will not be triggered sometimes. I'm not the only one who is facing this problem.
Android appWidgetProvider onEnabled never called on tablet
onDisabled
? As I do not want AlarmManager still being triggered repeatably, after the last widget had been removed. <receiver android:name="org.yccheok.MyAppWidgetProvider"
android:exported="true" >
<intent-filter >
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<action android:name="android.appwidget.action.APPWIDGET_DISABLED" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>
public class MyAppWidgetProvider extends AppWidgetProvider {
private static PendingIntent createAlarmUpdatePendingIntent(Context context) {
Intent intent = new Intent(context, JStockAppWidgetProvider.class);
intent.setAction(JStockAppWidgetProvider.ALARM_UPDATE_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent;
}
@Override
public void onEnabled(Context context)
{
super.onEnabled(context);
PendingIntent pendingIntent = createAlarmUpdatePendingIntent(context);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
int scanSpeed = JStockApplication.instance().getJStockOptions().getScanSpeed();
alarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis() + scanSpeed, scanSpeed, pendingIntent);
}
@Override
public void onDisabled(Context context)
{
super.onDisabled(context);
PendingIntent pendingIntent = createAlarmUpdatePendingIntent(context);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
}
Upvotes: 6
Views: 3577
Reputation: 2461
I was confused by the following situation.
If you have added the widget to the lock screen and then you are testing by adding and removing the widget to the home screen, then you never see onEnabled/onDisabled called. The reason is that there is still a widget added - the lock screen widget.
Upvotes: 0
Reputation: 1323
For what I understood you just need one alarm, and I suppose all widgets will be all the same. So, the idea for your alarm is run on service, not on widget. You shouldn't run long time actions on BroadcastReceiver.
We will use the onUpdate and not onEnable (because the design of this demo). Then aways we get a new widget we can perform the service.
AppWidgetProvider:
public class MyAppWidgetProvider extends AppWidgetProvider {
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds)
{
ComponentName thisWidget = new ComponentName(context, MyAppWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
Intent intent = new Intent(context.getApplicationContext(), JStockAppWidgetService.class);
intent.setAction(JStockAppWidgetService.ALARM_UPDATE_ACTION);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);
context.startService(intent);
}
@Override
public void onDisabled(Context context)
{
super.onDisabled(context);
Intent intent = new Intent(context.getApplicationContext(), JStockAppWidgetService.class);
intent.setAction(JStockAppWidgetService.ALARM_STOP_ACTION);
// I kept this just in case you wanna keep running your alarm without widget. You can just stopService here too.
context.startService(intent);
}
}
And here the Service:
public class JStockAppWidgetService extends Service {
public static final String ALARM_UPDATE_ACTION = "ALARM_UPDATE_ACTION";
public static final String ALARM_STOP_ACTION = "ALARM_STOP_ACTION";
//delay to refresh your widget
private int delay = 10000; //10 secs
private Thread myThread;
@Override
public void onStart(Intent intent, int startId) {
final int[] allWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
//running your timeout while is not interrupted
while(!Thread.currentThread().isInterrupted()) {
//we need to back to GUI thread
handler.post(new Runnable() {
@Override
public void run() {
runInMyGuiThread(allWidgetIds);
}
});
//everybody needs to sleep sometime =p
Thread.sleep(delay);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
};
String action = intent.getAction();
if(action == ALARM_UPDATE_ACTION) {
if(myThread != null)
myThread.interrupt();
myThread = new Thread(runnable);
myThread.start();
} else if(action == ALARM_STOP_ACTION && myThread != null) {
myThread.interrupt();
}
}
//here you are in GUI thread with all your widgets id
public void runInMyGuiThread(int[] allWidgetIds) {
for(int widgetId : allWidgetIds){
//do what you want to update each widget
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
This is just a little demo, you can stop the service or keep it running without widget, I will let with you.
Upvotes: 1
Reputation: 34301
I afraid you have misunderstood the methods of AppWidgetProvider
.
onDeleted(Context context, int[] appWidgetIds)
Called in response to the ACTION_APPWIDGET_DELETED broadcast **when one or more** AppWidget instances have been deleted. Override this method to implement your own AppWidget functionality.
onDisabled(Context context)
Called in response to the ACTION_APPWIDGET_DISABLED broadcast, which is sent when the last AppWidget instance for this provider is deleted. Override this method to implement your own AppWidget functionality.
In short, If you have two or more AppWidget instances then if you remove any of them at that time only onDeleted()
method for particular widget will be called.
If you have only single AppWidget instance then if you remove that time onDesabled()
and onDeleted()
both will be called.
So you will have to move your code from onDesabled()
method to onDeleted()
method and it will get called every time.!
Also take care that onEnabled()
will be called only for the first instance and not for every next instance you create.
Upvotes: 1
Reputation: 3747
you can check in method a number of instances that are currently running.
private boolean hasInstances(Context context) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(
new ComponentName(context, this.getClass()));
return (appWidgetIds.length > 0);
}
Upvotes: 0