How to monetize Android apps with ad-free subscriptions using RevenueCat
A step-by-step approach to monetizing Android users with an optional ad-free upgrade.

Most apps monetize through ads using things like AdMob or Audience Network. To maximize your earnings with ads, you need to show as many of them as possible, which makes for a pretty lousy user experience.
There’s a middle ground, though: Show ads to users with low willingness to pay, and use a subscription to offer those users that love your service an ad free experience. This article will walk you through how to implement this using RevenueCat’s Android SDK.
Connect to Google Play Store
First, connect your Google Play Store account to the RevenueCat dashboard to easily import all existing products. This integration enables unified user identities, simplified subscription management, cross-platform analytics, and accurate pricing insights across app stores.
To do this, simply follow the official guide on connecting to the Google Play Store. If you want to explore more detailed steps, refer to the Google Play Product Setup article for the one by one step guidelines.
Set up products in the RevenueCat dashboard
Once you’ve connected your Google Play Store account to the RevenueCat dashboard, the next step is configuring your products. If you’re new to in-app purchases or subscription-based apps, it’s important to understand the structure of product configuration. There are four key item types you should become familiar with.
- Product: Individual in-app purchases configured directly within store platforms (e.g., Apple App Store or Google Play).
- Offering: An offering is a group of products displayed to users as a paywall, configured in the RevenueCat dashboard and used to manage pricing experiments and segmentation.
- Package: A package represents equivalent products across different platforms (iOS, Android, web) bundled under a single identifier within an offering.
- Entitlement: An entitlement defines the access or feature level granted to users after a purchase, abstracting product identifiers and simplifying permission management across apps.
For easier understanding, you can refer to the visual diagram in the RevenueCat In-App Purchases Cheat Sheet below:

Once you’re familiar with the concepts above, or if you already have experience, you can start configuring products in the dashboard by following the quickstart guide or referring to this article to get up to speed.
Integrate purchases SDK into your project
Now it’s time to integrate RevenueCat’s Purchases SDK into your project. To get started, add the following dependency to your build.gradle.kts
file:
1implementation("com.revenuecat.purchases:purchases:8.17.1")
Next, initialize the Purchases SDK in your Application class using the code below:
1/**
2 * Initialize the RevenueCat Purchases SDK.
3 *
4 * `appUserID` is nil, so an anonymous ID will be generated automatically by the Purchases SDK.
5 * Read more about Identifying Users here: https://docs.revenuecat.com/docs/user-ids
6 *
7 * `purchasesAreCompletedBy` is set to REVENUECAT, so Purchases will automatically handle finishing transactions.
8 * Read more about finishing transactions here: https://www.revenuecat.com/docs/migrating-to-revenuecat/sdk-or-not/finishing-transactions
9 */
10val builder = PurchasesConfiguration.Builder(this, BuildConfig.REVENUECAT_API_KEY)
11Purchases.configure(
12 builder
13 .purchasesAreCompletedBy(PurchasesAreCompletedBy.REVENUECAT)
14 .appUserID(null)
15 .diagnosticsEnabled(true)
16 .build(),
17)
Validating user entitlements
Now let’s validate user entitlements. As discussed earlier, an entitlement defines the level of access or features granted to a user after a purchase, so you can use it to determine whether to show an ad banner or not.
You can simply check if a user has an active entitlement or not by using the code below:
1val ENTITLEMENT_IDENTIFIER = ".." // get specific entitlement identifier from your RevenueCat dashboard
2val customerInfo = Purchases.sharedInstance.awaitCustomerInfo()
3val isEntitled = customerInfo?.entitlements[ENTITLEMENT_IDENTIFIER]?.isActive == true
As shown in the code above, after retrieving the customer info using Purchases.sharedInstance.awaitCustomerInfo()
within a coroutine scope, you can check whether the customer is entitled to a specific entitlement.
Disabling an Admob banner
Once you’ve determined whether a user is entitled to a specific entitlement, you can decide whether to display an Admob banner based on your product requirements, as shown in the example below:
1@Composable
2fun Bottom(isEntitled: Boolean) {
3 if (isEntitled) {
4 // if the user is granted access to this entitlement. don't need to display a banner.
5 } else {
6 // display a banner UI here
7 AndroidView(
8 modifier = Modifier.fillMaxWidth(),
9 factory = { context ->
10 AdView(context).apply {
11 setAdSize(AdSize.BANNER)
12 adUnitId = "ca-app-pub-xxxx"
13 loadAd(AdRequest.Builder().build())
14 }
15 },
16 update = { adView ->
17 adView.loadAd(AdRequest.Builder().build())
18 }
19 )
20 }
21}
Disabling an Audience Network banner
Just like with AdMob, you can easily disable an Audience Network banner using the same logic, as shown in the example below:
1@Composable
2fun Bottom(isEntitled: Boolean) {
3 if (isEntitled) {
4 // if the user is granted access to this entitlement. don't need to display a banner.
5 } else {
6 // display a banner UI here
7 ..
8 }
9}
Implement in-app purchase for ad-free feature
Now, let’s implement in-app purchases to offer an ad-free experience. This approach benefits both sides — your service can boost revenue, while power users enjoy a cleaner, uninterrupted experience through a paid upgrade.
First, retrieve the appropriate product information from the RevenueCat dashboard. You can do this easily by calling Purchases.sharedInstance.awaitGetProducts()
, as shown in the example below:
1// fetches the product information from the RevenueCat server
2val products = Purchases.sharedInstance.awaitGetProducts(
3 productIds = listOf("paywall_tester.subs"),
4)
5
6// proceed in-app purchases
7val purchaseResult = Purchases.sharedInstance.awaitPurchase(
8 purchaseParams = PurchaseParams.Builder(
9 activity = context as Activity,
10 storeProduct = products.first(),
11 ).build()
12)
In this case, if you have multiple products like paywall_tester.subs:weekly, paywall_tester.subs:monthly
, and paywall_tester.subs:yearly
, you should use paywall_tester.subs
as the value for the productIds
field. This will allow you to retrieve all related product information as a list.
After that, you can initiate the in-app purchase flow by calling Purchases.sharedInstance.awaitPurchase()
. This will trigger the Google Play purchase dialog, allowing the user to complete the transaction — just like that, you’ve implemented in-app purchases with only a few lines of code.
Conclusion
By combining in-app purchases with ad gating, you can serve both high-intent and low-intent users — maximizing revenue while giving premium users a cleaner experience. Using RevenueCat, implementing this dual-monetization strategy is both scalable and straightforward.
As always, happy coding!
— Jaewoong
You might also like
- Blog post
Turn Your App into Revenue: Building Paywalls in Android With Jetpack Compose
In this article, you'll learn how to seamlessly implement in-app subscriptions and paywall features in Android using Jetpack Compose and the RevenueCat SDK.
- Blog post
Android In-App Subscription Tutorial
Get up and running with in-app subscription on Android
- Blog post
Ensure public interface reliability: Tracking API compatibility for Android and Kotlin
This article explores how to ensure public API reliability by tracking compatibility changes, by exploring the RevenueCat SDK.