The Klaviyo Android SDK allows developers to incorporate Klaviyo analytics and push notification functionality in their native Android applications. The SDK assists in identifying users and tracking user events via the latest Klaviyo Client APIs. To reduce performance overhead, API requests are queued and sent in batches. The queue is persisted to local storage so that data is not lost if the device is offline or the app is terminated.
Once integrated, your marketing team will be able to better understand your app users' needs and send them timely push notifications via FCM (Firebase Cloud Messaging).
- Kotlin 1.8.0 or later
- Android API level 23 or later
-
Include the JitPack repository in your project's build file
Kotlin DSL
// settings.gradle.kts dependencyResolutionManagement { repositories { maven(url = "https://jitpack.io") } }
Groovy
// settings.gradle dependencyResolutionManagement { repositories { maven { url "https://jitpack.io" } } }
-
Add the dependencies to your app's build file
Kotlin DSL
// build.gradle.kts dependencies { implementation("com.github.klaviyo.klaviyo-android-sdk:analytics:3.0.0") implementation("com.github.klaviyo.klaviyo-android-sdk:push-fcm:3.0.0") }
Groovy
// build.gradle dependencies { implementation "com.github.klaviyo.klaviyo-android-sdk:analytics:3.0.0" implementation "com.github.klaviyo.klaviyo-android-sdk:push-fcm:3.0.0" }
The SDK must be initialized with the short alphanumeric
public API key
for your Klaviyo account, also known as your Site ID. We require access to the applicationContext
so the
SDK can be responsive to changes in application state and network conditions, and access SharedPreferences
to
persist data. Upon initialize, the SDK registers listeners for your application's activity lifecycle callbacks,
to gracefully manage background processes.
Klaviyo.initialize()
must be called before any other SDK methods can be invoked. We recommend initializing from
the earliest point in your application code, such as the Application.onCreate()
method.
// Application subclass
import android.app.Application
import com.klaviyo.analytics.Klaviyo
class YourApplication : Application() {
override fun onCreate() {
super.onCreate()
/* ... */
// Initialize is required before invoking any other Klaviyo SDK functionality
Klaviyo.initialize("KLAVIYO_PUBLIC_API_KEY", applicationContext)
}
}
The SDK provides methods to identify profiles via the Create Client Profile API. A profile can be identified by any combination of the following:
- External ID: A unique identifier used by customers to associate Klaviyo profiles with profiles in an external system, such as a point-of-sale system. Format varies based on the external system.
- Individual's email address
- Individual's phone number in E.164 format
Identifiers are persisted to local storage so that the SDK can keep track of the current profile.
Profile identifiers and other attributes can be set all at once using the Profile
data class:
val profile = Profile(
externalId = "USER_IDENTIFIER",
email = "kermit@example.com",
phoneNumber = "+12223334444",
properties = mapOf(
ProfileKey.FIRST_NAME to "Kermit",
ProfileKey.CUSTOM("instrument") to "banjo"
)
)
Klaviyo.setProfile(profile)
Or individually with additive fluent setters:
Klaviyo.setExternalId("USER_IDENTIFIER")
.setEmail("kermit@example.com")
.setPhoneNumber("+12223334444")
.setProfileAttribute(ProfileKey.FIRST_NAME, "Kermit")
.setProfileAttribute(ProfileKey.CUSTOM("instrument"), "banjo")
Either way, the SDK will group and batch API calls to improve performance.
To start a new profile altogether (e.g. if a user logs out) either call Klaviyo.resetProfile()
to clear the currently tracked profile identifiers (e.g. on logout), or use Klaviyo.setProfile(profile)
to overwrite it with a new profile object.
// Start a profile for Kermit
Klaviyo.setEmail("kermit@example.com")
.setPhoneNumber("+12223334444")
.setProfileAttribute(ProfileKey.FIRST_NAME, "Kermit")
// Stop tracking Kermit
Klaviyo.resetProfile()
// Start a new profile for Robin
Klaviyo.setEmail("robin@example.com")
.setPhoneNumber("+5556667777")
.setProfileAttribute(ProfileKey.FIRST_NAME, "Robin")
Note: We trim leading and trailing whitespace off of identifier values.
Empty strings will be ignored with a logged warning. If you are trying to remove an identifier's value,
use setProfile
or resetProfile
.
Klaviyo will track unidentified users with an autogenerated ID whenever a push token is set or an event is created. That way, you can collect push tokens and track events prior to collecting profile identifiers such as email or phone number. When an identifier is provided, Klaviyo will merge the anonymous user with an identified user.
The SDK also provides tools for tracking analytics events via the
Create Client Event API.
A list of common Klaviyo-defined event metrics is provided in EventMetric
, or
you can use EventMetric.CUSTOM("name")
for custom event metric names.
Additional event properties can be specified as part of EventModel
val event = Event(EventMetric.VIEWED_PRODUCT)
.setProperty(EventKey.CUSTOM("Product"), "Coffee Mug")
.setValue(10.0)
Klaviyo.createEvent(event)
- A Firebase account for your Android app
- Familiarity with Firebase documentation
- Configure Android push in your Klaviyo account settings
The Klaviyo Push SDK for Android works as a wrapper around FirebaseMessagingService
, so the
setup process is very similar to the Firebase client documentation linked above.
In your AndroidManifest.xml
file, register KlaviyoPushService
to receive MESSAGING_EVENT
intents.
<!-- AndroidManifest.xml -->
<manifest>
<!-- ... -->
<application>
<!-- ... -->
<service android:name="com.klaviyo.pushFcm.KlaviyoPushService" android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
To specify an icon and/or color for Klaviyo notifications, add the following optional metadata elements to the
application component of AndroidManifest.xml
. Absent these keys, the firebase keys
com.google.firebase.messaging.default_notification_icon
and com.google.firebase.messaging.default_notification_color
will be used if present, else we fall back on the application's launcher icon, and omit setting a color.
<!-- AndroidManifest.xml -->
<manifest>
<!-- ... -->
<application>
<!-- ... -->
<meta-data android:name="com.klaviyo.push.default_notification_icon"
android:resource="{YOUR_ICON_RESOURCE}" />
<meta-data android:name="com.klaviyo.push.default_notification_color"
android:resource="{YOUR_COLOR}" />
</application>
</manifest>
In order to send push notifications to your users, you must collect their push tokens and register them with Klaviyo.
This is done via the Klaviyo.setPushToken
method, which registers push token and current authorization state
via the Create Client Push Token API.
Once registered in your manifest, KlaviyoPushService
will receive new push tokens via the onNewToken
method.
We also recommend retrieving the latest token value on app startup and registering it with Klaviyo SDK.
Add the following to your Application.onCreate
method.
override fun onCreate(savedInstanceState: Bundle?) {
/* ... */
// Fetches the current push token and registers with Push SDK
FirebaseMessaging.getInstance().token.addOnSuccessListener { pushToken ->
Klaviyo.setPushToken(pushToken)
}
}
As of version 3.0.0: After setting a push token, the Klaviyo SDK will automatically track changes to the user's notification permission whenever the application is opened or resumed from the background.
Reminder: Klaviyo.initialize
is required before using any other Klaviyo SDK functionality, even
if you are only using the SDK for push notifications and not analytics.
Android 13 introduced a new runtime permission for displaying notifications. The Klaviyo SDK automatically adds the
POST_NOTIFICATIONS
permission to the manifest, but you will need to request user permission according to Android best practices and the best user experience in the context of your application. The linked resources provide code examples for requesting permission and handling the user's response.
If a new profile was set using setProfile
or if resetProfile
was called and a new anonymous
profile was created, the push token will be automatically associated with the new profile without
any additional action (like setting token again) required. This functionality was added in release 3.0.0
.
KlaviyoPushService
will handle displaying all notifications via the onMessageReceived
method regardless of
whether the app is in the foreground or background. You can send test notifications to a specific token using
the push notification preview feature in order
to test your integration. If you wish to customize how notifications are displayed, see Advanced Setup.
Rich Push is the ability to add images to
push notification messages. This feature is supported in version 1.3.1 and up of the Klaviyo Android SDK.
No additional setup is needed to support rich push. Downloading the image and attaching it to the notification
is handled within KlaviyoPushService
. If an image fails to download (e.g. if the device has a poor network
connection) the notification will be displayed without an image after the download times out.
To track push notification opens, you must call Klaviyo.handlePush(intent)
when your app is launched from an intent.
This method will check if the app was opened from a notification originating from Klaviyo and if so, create an
Opened Push
event with required message tracking parameters. For example:
// Main Activity
override fun onCreate(savedInstanceState: Bundle?) {
/* ... */
onNewIntent(intent)
}
override fun onNewIntent(intent: Intent?) {
/* ... */
// Tracks when a system tray notification is opened
Klaviyo.handlePush(intent)
}
Note: Intent handling may differ depending on your app's architecture. By default, the Klaviyo SDK will use your
app's launch intent for a tapped notification. Adjust this example to your use-case, ensuring that
Klaviyo.handlePush(intent)
is called whenever your app is opened from a notification.
Deep Links allow you to navigate to a particular page within your app in response to the user opening a notification. There are broadly three steps to implement deep links in your app.
-
Add intent filters for incoming links:
Add an intent filter to the activity element of your
AndroidManifest.xml
file. Replace the scheme and host to match the URI scheme that you will embed in your push notifications.<!-- AndroidManifest.xml --> <manifest> <!-- ... --> <application> <!-- ... --> <activity> <!-- ... --> <intent-filter android:label="@string/filter_view_example_gizmos"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- Accepts URIs formatted "example://host.com” --> <data android:scheme="example" android:host="host.com"/> </intent-filter> </activity> </application> </manifest>
-
Read data from incoming intents:
When the app is opened from a deep link, the intent that started the activity contains data for the deep link. You can parse the URI from the intent's data property and use it to navigate to the appropriate part of your app.
override fun onCreate(savedInstanceState: Bundle?) { /* ... */ onNewIntent(intent) } override fun onNewIntent(intent: Intent?) { // Tracks when a system tray notification is opened Klaviyo.handlePush(intent) // Read deep link data from intent val action: String? = intent?.action val deepLink: Uri? = intent?.data }
-
Test your deep links:
Using android debug bridge (adb), run the following command to launch your app via an intent containing a deep link to test your deep link handler.
adb shell am start -W -a android.intent.action.VIEW -d <URI> <PACKAGE>
To perform integration testing, you can send a preview push notification containing a deep link from the Klaviyo push editor.
For additional resources on deep linking, refer to Android developer documentation.
If you'd prefer to have your own implementation of FirebaseMessagingService
,
follow the FCM setup docs including referencing your own service class in the manifest.
<!-- AndroidManifest.xml -->
<manifest>
<!-- ... -->
<application>
<!-- ... -->
<service android:name="your.package.name.YourPushService" android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
The Application
code snippets above for handling push tokens and intents are still required.
You may either subclass KlaviyoPushService
or invoke the necessary Klaviyo SDK methods in your service.
-
Subclass
KlaviyoPushService
:import com.google.firebase.messaging.RemoteMessage import com.klaviyo.pushFcm.KlaviyoPushService import com.klaviyo.pushFcm.KlaviyoRemoteMessage.isKlaviyoNotification class YourPushService : KlaviyoPushService() { override fun onNewToken(newToken: String) { // Invoking the super method will ensure Klaviyo SDK gets the new token super.onNewToken(newToken) } override fun onMessageReceived(message: RemoteMessage) { // Invoking the super method allows Klaviyo SDK to handle Klaviyo messages super.onMessageReceived(message) // This extension method allows you to distinguish Klaviyo from other sources if (!message.isKlaviyoNotification) { // Handle non-Klaviyo messages } } }
-
Subclass
FirebaseMessagingService
and invoke Klaviyo SDK methods directlyimport com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage import com.klaviyo.analytics.Klaviyo import com.klaviyo.pushFcm.KlaviyoNotification import com.klaviyo.pushFcm.KlaviyoRemoteMessage.isKlaviyoNotification class YourPushService : FirebaseMessagingService() { override fun onNewToken(newToken: String) { super.onNewToken(newToken) Klaviyo.setPushToken(newToken) } override fun onMessageReceived(message: RemoteMessage) { super.onMessageReceived(message) // This extension method allows you to distinguish Klaviyo from other sources if (message.isKlaviyoNotification) { // Handle displaying a notification from Klaviyo KlaviyoNotification(message).displayNotification(this) } else { // Handle non-Klaviyo messages } } }
Note: Klaviyo uses data messages
to provide consistent notification formatting. As a result, all Klaviyo notifications are
handled via onMessageReceived
regardless of the app being in the background or foreground.
If you are working with multiple remote sources, you can check whether a message originated
from Klaviyo with the extension method RemoteMessage.isKlaviyoNotification
.
If you wish to fully customize the display of notifications, we provide a set of RemoteMessage
extensions such as import com.klaviyo.pushFcm.KlaviyoRemoteMessage.body
to access all the properties sent from Klaviyo.
We also provide an Intent.appendKlaviyoExtras(RemoteMessage)
extension method, which attaches the data to your
notification intent that the Klaviyo SDK requires in order to track opens when you call Klaviyo.handlePush(intent)
.
The SDK contains logging at different levels from verbose
to assert
. By default, the SDK logs at the error
level
in a production environment and at the warning
level in a debug environment. You can change the log level by adding
the following metadata tag to your manifest file.
0
= disable logging entirely1
=Verbose
and above2
=Debug
and above3
=Info
and above4
=Warning
and above5
=Error
and above6
=Assert
only
<!-- AndroidManifest.xml -->
<manifest>
<!-- ... -->
<application>
<!-- Enable SDK debug logging -->
<meta-data
android:name="com.klaviyo.core.log_level"
android:value="2" />
</application>
</manifest>
If you notice issues in the release build of your apps, you can try to manually add a couple rules
to your proguard-rules.pro
to prevent obfuscation:
-keep class com.klaviyo.analytics.** { *; }
-keep class com.klaviyo.core.** { *; }
-keep class com.klaviyo.push-fcm.** { *; }
See the contributing guide to learn how to contribute to the Klaviyo Android SDK. We welcome your feedback in the issues section of our public GitHub repository.
The Klaviyo Android SDK is available under the MIT license. See LICENSE for more info.
Browse complete code documentation autogenerated with Dokka here.