I am currently working on an update on a widget I made a while ago, and I am having troubles with the widget view freezing, or it stops updating after a while.
The app is a system monitor that updates every second (Yes, I know it is bad for battery life for it to update this often, but I set up some broadcast receivers to disable updating to help this significantly), and since Alarm managers no longer allow updating every second, I had to use a handler and a runnable to call an object that takes context to update our remote views and widget.
It starts with a provider creating and calling the object:
ProviderHelper.java
//Save our context
context = parentContext;
//creating Handler to update every second
handler = new Handler();
//Create our updater
updater = new WidgetUpdater(parentContext);
//Start our sleep receiver
registBroadcastReceiver(context);
//Set our quit to false
quit = false;
runUpdate = new Runnable() {
public void run() {
//Run our updates in the service
if(!ProviderHelper.isQuitting())
{
updater.runUpdate(context);
}
//Quit the handler
if(ProviderHelper.isQuitting()) {
//Remove all pending callbacks, and then return
handler.removeCallbacks(this);
return;
}
//Use post at time to only update when the phone is not in deep sleep :)
else handler.postAtTime(this, SystemClock.uptimeMillis() + updateInterval);
}
};
handler.post(runUpdate);
}
Then we call a helper to update the specified section of the widget
WidgetUpdater.java
public void update(Context context) {
Log.d("WIDGET UPDATER", "Updating...");
//call time methods, not calling if unchecked
if (timeMan.timeStatus()) timeMan.getTime();
//call system methods
if (battMan.percentStatus() || battMan.tempStatus()) {
battMan.getBatteryPercent(context);
battMan.getBatteryTemp(context);
}
if (cpuMan.cpuStatus()) cpuMan.getCpuUsage();
if (timeMan.upTimeStatus()) timeMan.getUptime();
//call memory methods
if (diskMan.memoryStatus()) diskMan.getSpace();
if (diskMan.ramStatus()) diskMan.getRam(context);
//call network methods
if (networkMan.typeStatus()) networkMan.getNetworkType(context);
if (networkMan.ipStatus()) networkMan.getIp(context);
if (networkMan.downSpeedStatus() || networkMan.upSpeedStatus()) networkMan.getSpeeds();
//update widget for all size
// Need to grab our views every single time
views = new RemoteViews(context.getApplicationContext().getPackageName(), R.layout.widget_layout);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(thiswidget, views);
manager.updateAppWidget(thiswidgetsmall, views);
manager.updateAppWidget(thiswidgetbig, views);
manager.updateAppWidget(thiswidgetbigger, views);
manager.updateAppWidget(thiswidgetsmallest, views);
//Try notifying the wifgets their data changed
//Yes this is redundant, I am trying everything to get this to work
manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(thiswidget), views.getLayoutId());
manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(thiswidgetsmall), views.getLayoutId());
manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(thiswidgetbig), views.getLayoutId());
manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(thiswidgetbigger), views.getLayoutId());
manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(thiswidgetsmallest), views.getLayoutId());
}
For example, here is a call from the time widget helper
TimeHelper.java
//function for time
@SuppressLint("SimpleDateFormat")
public void getTime()
{
//time, creating date and inserting it to simple date format
Date date = new Date();
SimpleDateFormat time= new SimpleDateFormat("hh:mm:ss a");
SimpleDateFormat day = new SimpleDateFormat("EEEE, MMMM dd, yyyy");
if(shortBool) day = new SimpleDateFormat("EEE, MMM dd, yyyy");
else day = new SimpleDateFormat("EEEE, MMMM dd, yyyy");
//24 hour settings
if(hourFormat == true) time = new SimpleDateFormat("HH:mm:ss a");
else time = new SimpleDateFormat("hh:mm:ss a");
//setting text to time
views.setTextViewText(R.id.time, "Time: " + time.format(date));
views.setTextViewText(R.id.date, "Date: " + day.format(date));
}
And I already posted this, but here is how I am updating the views:
WidgetUpdater.java (In update function posted above)
//update widget for all size
// Need to grab our views every single time?
views = new RemoteViews(context.getApplicationContext().getPackageName(), R.layout.widget_layout);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(thiswidget, views);
manager.updateAppWidget(thiswidgetsmall, views);
manager.updateAppWidget(thiswidgetbig, views);
manager.updateAppWidget(thiswidgetbigger, views);
manager.updateAppWidget(thiswidgetsmallest, views);
//Try notifying the wifgets their data changed
//Yes this is redundant, I am trying everything to get this to work
manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(thiswidget), views.getLayoutId());
manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(thiswidgetsmall), views.getLayoutId());
manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(thiswidgetbig), views.getLayoutId());
manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(thiswidgetbigger), views.getLayoutId());
manager.notifyAppWidgetViewDataChanged(manager.getAppWidgetIds(thiswidgetsmallest), views.getLayoutId());
I've tried solutions such as these:
Android widget stops working after a while?
And they didn't work. And If you noticed, I am logging whenever the update function is called where the AppWidgetManager is being called to update the app widget, so I am certain this is all being called as well. In the sense that the update function is being called, however the remoteview of the widget is NOT updating (in case that was unclear). Any help on why the widget remote view would stop updating after a while of time (~5-10 minutes), would be much appreciated. If you need any more info please let me know!
Also the project is open-source, so you can view all of the code here:
https://github.com/torch2424/Stats-Monitor-Widget
Thank you!