Android SharedPreferences Tutorial and Example

Sending Push Notifications with Firebase Cloud Messaging on Android

Firebase Cloud Messaging has become the backbone of mobile alerting for Android apps, replacing the older Google Cloud Messaging service that many developers in Melbourne and Sydney first experimented with back in 2013. Whether you are building a small side project in Adelaide or maintaining a production app downloaded by thousands of users across the country, FCM offers a reliable path to delivering timely updates.

This walkthrough assumes you already know how to build an Android app and have worked with Gradle, activities, and basic UI elements. We will start from creating a Firebase project, then move through notification channels, NotificationCompat builders, token handling, and a few practical considerations that often trip up developers working in the Australian market.

By the end, you should be able to wire up FCM in your own application, handle messages arriving while the app is open or running in the background, and respect the local guidelines around digital communications from the Australian Communications and Media Authority.

Setting Up Firebase in Your Android Project

The first step is creating a project in the Firebase console and connecting it to your Android application. Open the Firebase console, click "Add project", give it a sensible name, and follow the wizard. Once the project exists, add an Android app to it by entering your application ID, the value of applicationId in your module-level build.gradle file.

After registering the app, download the google-services.json file and place it inside the app/ directory. Open the project-level build.gradle and add the Google Services classpath, then apply the plugin in the app-level build.gradle. Add the Firebase Messaging dependency itself: implementation 'com.google.firebase:firebase-messaging:23.4.0' (or whatever the current version is).

Sync Gradle, build the project, and run it once on a real device or emulator. This initial run lets Firebase verify the configuration and generate the necessary registration credentials. Without this step, no token will be generated later, and your logs will show cryptic authentication errors that are hard to debug otherwise.

Understanding Notification Channels and Delivery Options

Since Android 8.0 (Oreo), every notification must belong to a channel. Channels give users granular control over which kinds of alerts they receive, which became a hot topic in Australia after the 2023 amendments to the Spam Act enforcement guidelines. Creating at least two channels, such as "general" and "promotions", is a sensible default.

You create a channel once, usually in your Application subclass or first Activity. The NotificationChannel constructor takes an ID, a user-visible name, and an importance level. Importance levels range from IMPORTANCE_MIN (silent) to IMPORTANCE_HIGH (heads-up display). Choose these thoughtfully because users can override them at any time through system Settings.

Before settling on FCM, it helps to weigh the main delivery options side by side.

Approach Transport Works without Google Play Services Suitable for one-to-many broadcasts Battery impact
Firebase Cloud Messaging Google infrastructure No Yes Low
WebSockets from your own server Direct TCP Yes Possible but expensive Medium to high
Long-polling HTTP HTTP Yes No High
Third-party SDK (OneSignal) Wrapper around FCM/Push No Yes Low

If your app sends marketing pushes alongside transactional alerts, separating them into different channels is also relevant under the Australian Privacy Principles when collecting consent for marketing communications. A user who disables your promotions channel is exercising clear opt-out behaviour that you should respect.

Building the Notification in Code with NotificationCompat

The NotificationCompat.Builder class remains the recommended way to construct notifications. Start with a builder, set the small icon (mandatory, and avoid transparent backgrounds, which Android silently drops), set a content title and text, and assign the channel ID you registered earlier.

For richer interactions, attach a PendingIntent to the content intent slot so tapping the notification opens a specific screen. PendingIntent.FLAG_IMMUTABLE is required on Android 12 and above. Style options such as BigTextStyle, InboxStyle, and BigPictureStyle let you display more elaborate content without overwhelming the user.

Always set an auto-cancel flag if you want the notification to disappear when tapped, and consider adding a category such as CATEGORY_MESSAGE or CATEGORY_PROMO. These categories help some Android skins, including the Samsung One UI used on phones sold by JB Hi-Fi and Telstra, prioritise or group alerts more intelligently.

Handling FCM Tokens and Saving User Preferences

Every installation of your app receives a unique FCM registration token. You obtain it by extending FirebaseMessagingService and overriding onNewToken, then sending the value to your application server. Tokens rotate occasionally, especially after the user clears app data or reinstalls the app, so your server logic must handle updates gracefully.

On the client side, you will often want to remember whether the user has already opted in to notifications. This is a perfect place to reach for SharedPreferences, which is covered in detail in storing user preferences. Storing the token along with the opt-in flag keeps everything simple and avoids unnecessary network traffic.

Avoid hardcoding server keys in your app. The FCM HTTP v1 API uses short-lived OAuth tokens obtained from a service account, which is far safer than the legacy server key approach. Anyone reverse-engineering your APK can extract static secrets, and a leaked key quickly racks up costs or gets used for spam.

Working with Background and Foreground Message Reception

FCM delivers two kinds of payloads: notification messages, which the system displays automatically when the app is in the background, and data messages, which your code always receives. Mixing both in one payload works but can lead to surprising behaviour, so choose deliberately based on what you want the user to see.

When the app is in the foreground, the system does not display the notification automatically. You must intercept it in onMessageReceived, build a NotificationCompat.Builder manually, and post it through NotificationManagerCompat. This is also where you apply any custom logic, such as suppressing duplicates or batching multiple updates into an InboxStyle summary.

For background messages, extend FirebaseMessagingService and override onMessageReceived with a special note: the method must complete within about 20 seconds, or Android may kill the process. Heavy work should be offloaded to WorkManager, which plays nicely with battery optimisation profiles that Australian carriers push to devices through Telstra and Optus firmware customisations.

Local Considerations and Best Practices for Australian Apps

Australians spend a lot of time on public transport and waiting for coffee, which means notifications need to be timely and concise to be welcome. Heavy promotional blasts during the working day tend to be muted or disabled quickly, so schedule marketing pushes around Australian time zones: AEST (UTC+10) for the eastern states, ACST (UTC+9:30) for South Australia and the Northern Territory, and AWST (UTC+8) for Western Australia.

Be aware of the Spam Act 2003 and the related ACMA guidance. Marketing notifications must include a working unsubscribe path, and the sender identity must be clear. Under the Australian Privacy Principles, you also need to handle any user data collected through notification interactions responsibly.

If your project involves native code or you are debugging a tricky dependency issue, looking at how shared libraries resolve functions on Windows can illuminate analogous patterns on Android. A walkthrough of resolving DLL functions is a useful reference when you need to understand dynamic symbol resolution at a deeper level.

Practical Recommendations for a Stable FCM Integration

Before wrapping up, here are some practical tips that will save you time during development and after release:

  • Test with both Wi-Fi and Australian mobile networks, including Telstra, Optus, and Vodafone, because routing differences occasionally cause delays.
  • Use notification channels from day one, even if your minimum SDK is below Oreo, by guarding the channel creation code with a version check.
  • Store FCM tokens with a timestamp in SharedPreferences so your server can detect stale entries during periodic cleanup.
  • Log everything to Logcat during development, but strip or obfuscate logs in release builds to keep tokens out of crash reports.
  • Subscribe to the Firebase release notes RSS feed and review changes quarterly, especially around authentication scopes and message priority changes.