Android SharedPreferences Tutorial and Example

Updating Widget Data With RemoteViews In Android Apps

From the weather panel on a Telstra-powered handset in a Brisbane high-rise to the AFL scores pinned on a Pixel running stock Android, home screen widgets remain one of the most used surfaces across Australian devices. They give users at-a-glance information without forcing them to open an app, which is handy when you're dashing out the door or stuck on a Sydney train with patchy reception.

Under the hood, every one of those compact views is powered by a special object called RemoteViews, and knowing how to update its contents smoothly is what separates a forgettable experiment from a widget people actually keep on screen. RemoteViews is essentially a marshalled description of a layout hierarchy that lives outside your activity process. The system reads this description and renders it in another process, which is why your background data updates need to flow through a well-defined API rather than direct view manipulation.

This guide walks through the practical steps of pushing fresh data into a widget, from initial setup and layouts to live refreshes, user taps, and list-backed grids. Along the way, you'll pick up patterns that scale whether you're building a finance tracker for the ASX or a footy tips companion for the weekend.

Understanding What RemoteViews Can And Cannot Do

RemoteViews supports a limited subset of the full View toolkit. You can't inflate arbitrary custom views from XML, and most heavyweight adapters aren't supported either. That restriction exists because the widget host process needs to render your layouts without having access to your app's classes, which keeps things stable but also shapes how you think about updates.

The good news is that the supported view set covers most everyday scenarios: TextView, ImageView, Button, ProgressBar, ListView, GridView, StackView, ViewFlipper, and a handful of layout containers. Refreshes happen by calling set methods on the RemoteViews object, such as setTextViewText or setImageViewResource, then handing the package back to the AppWidgetManager.

Because cross-process serialisation carries overhead, batch your updates whenever possible. Build a RemoteViews instance once, mutate it, then push it. This habit pays off when your widget is one of many on a user's home screen.

Wiring Up The AppWidgetProvider

Every widget starts with a BroadcastReceiver subclass called AppWidgetProvider. You declare it in the manifest with an intent filter for android.appwidget.action.APPWIDGET_UPDATE, then point it at a res/xml metadata file describing size, preview image, and update period.

Inside the receiver, override onUpdate to fetch any cached data and apply it through RemoteViews. A minimal implementation reads a SharedPreferences value, builds a RemoteViews from your layout resource, and calls appWidgetManager.updateAppWidget(widgetId, remoteViews).

If your widget needs work outside the onUpdate window, you can also issue explicit broadcasts back to your own provider. That keeps the refresh path self-contained and avoids relying on the system's coarse minimum update interval, which on most Australian OEM builds tends to be thirty minutes or more.

Composing The Layout That Ships With Each Update

Layouts for widgets behave like any other Android XML layout, but you should favour ConstraintLayout or the older LinearLayout and RelativeLayout to stay within the supported view family. Avoid ScrollView because long scrolling surfaces tend to be clipped by host launchers, particularly on older devices still in use across regional Queensland.

Set your background drawable thoughtfully, since transparent gradients can clash with whatever wallpaper a user has chosen. Material You theming plays well here, particularly on Pixel handsets running Android 12 and up, while vendor skins tend to add their own rounded corners that you'll want to respect.

When you build the RemoteViews instance at runtime, swap visual properties based on the data. Toggle between a daytime and nighttime image, swap a status icon, or append text, all via set methods on the marshalled object before handing it to AppWidgetManager.

Forcing Live Refreshes From Background Work

The system's update period is intentionally conservative to protect battery life. To push fresher data, schedule your own refresh with either AlarmManager or WorkManager. WorkManager is the friendlier option in 2024 because it handles Doze, app-standby buckets, and constraints like network availability without you micromanaging intents.

Inside a Worker, load the latest payload, build a fresh RemoteViews, and iterate over the installed widget IDs. Retrieve them via appWidgetManager.getInstalledProviders matched to your provider's component name. This pattern is what most production apps in the Australian Play Store use to keep their widgets current.

For teams running larger test rigs, infrastructure pipelines that automate VM provisioning can spin up multiple Android images in parallel, which is handy when validating widgets across the diverse device fleets in active use today.

Responding To User Taps With PendingIntents

Widgets are interactive, but every tap must travel through a PendingIntent because the widget host, not your activity, owns the click. setOnClickPendingIntent binds a view id to a PendingIntent that can launch an activity, start a service, or fire a broadcast.

For a refresh button, send a broadcast back to your AppWidgetProvider carrying the widget id as an extra. The provider reloads the data and pushes another RemoteViews update. This is the cleanest pattern for self-contained widgets like a currency converter or a Pomodoro timer.

For deep navigation, use getActivity and pass through any relevant data as intent extras. Make sure FLAG_UPDATE_CURRENT is set so the latest extras reach the target, since cached intents can otherwise return stale values during emulator sessions.

Driving Dynamic Lists Inside The Widget

RemoteViewsFactory is the bridge between an adapter-style data source and a ListView or GridView running inside a widget. It implements onDataSetChanged, getViewAt, getCount, and similar callbacks, and it runs in the background of your app process.

Pair this factory with a RemoteViewsService declared in the manifest, then bind it through a RemoteViews setRemoteAdapter call on a ListView. The system requests views on demand as the user scrolls through the widget, keeping memory pressure low.

Remember that lists inside widgets don't support per-item interaction beyond the row-level click set by setPendingIntentTemplate. Individual subviews within a row can't carry their own click handlers, which shapes how you design things like a sports widget that wants each match to open a different screen.

Common Pitfalls And Performance Tips

A few recurring issues catch developers out. First, applying updates from the main thread can stall rendering, so always push RemoteViews from a worker or IO context. Second, avoid calling notifyAppWidgetViewDataChanged too aggressively, since each call re-runs the factory's onDataSetChanged.

Keep layouts shallow and drawables light. Widgets that hit the 100KB cross-process limit get truncated silently, leaving blank squares on the wide range of phones in active use. Test on low-end profiles too, because entry-level Android Go devices still make up a noticeable share of the market.

Finally, log when your widget updates and what data it fed on. A quick Log.d inside onUpdate saves hours of head-scratching when a user in Perth reports their widget hasn't refreshed since the Anzac Day long weekend. Telemetry beats guesswork, and the same principle scales from a weekend project to a fully fledged banking app used by millions.

Update Mechanism Best For Battery Impact Refresh Granularity
System updatePeriodMillis Cheap, static info Low Every 30 minutes minimum
WorkManager Network-backed live data Moderate Constraint-aware, scheduled
AlarmManager setExactAndAllowWhileIdle Time-critical refreshes Higher Sub-minute if needed
Self-broadcast refresh User-triggered reloads Minimal Manual, on tap