Skip to content

feat(android): add opt-in support for GMA Next-Gen SDK - #882

Open
exzos28 wants to merge 7 commits into
invertase:mainfrom
exzos28:feat/next-gen-sdk-support
Open

feat(android): add opt-in support for GMA Next-Gen SDK#882
exzos28 wants to merge 7 commits into
invertase:mainfrom
exzos28:feat/next-gen-sdk-support

Conversation

@exzos28

@exzos28 exzos28 commented Aug 20, 2026

Copy link
Copy Markdown

Adds an opt-in Android build path against Google's newer GMA Next-Gen SDK (ads-mobile-sdk), selected via a google_mobile_ads_sdk app.json flag (or googleMobileAdsSdk Expo config-plugin parameter). Defaults to the existing legacy SDK (play-services-ads), so existing apps are unaffected.

  • Dual native implementations per ad format under android/src/legacy and android/src/nextgen, selected at build time via a Gradle.
  • GMA Next-Gen SDK has no Google Ad Manager equivalent, so Ad Manager ad unit IDs are explicitly rejected under next-gen rather than silently degrading.
  • Excludes play-services-ads from the classpath when next-gen is selected, since both SDKs ship classes in the same Java package and cannot coexist.
  • Exposes mobileAds().googleMobileAdsSdk so JS can detect which SDK variant is active (e.g. to hide Ad Manager specific UI).
  • Adds Expo config-plugin support, CI build matrix coverage, and docs (docs/next-gen-sdk.mdx).

Description

Google is pushing a rewritten Mobile Ads SDK for Android as the eventual successor to play-services-ads. It's a from-scratch package with no Ad Manager support and its own set of API quirks, so this isn't a drop-in swap — it's wired up as an alternative build target you opt into, with the legacy SDK staying the default.

The two SDKs can't be linked into the same app (same Java package, different classes), which is why this is a build-time choice rather than a runtime one.

Related issues

#864
#850
#836

Release Summary

Adds opt-in Android support for Google's GMA Next-Gen SDK via a google_mobile_ads_sdk app.json flag.

Checklist

  • I read the Contributor Guide
    and followed the process outlined there for submitting PRs.
    • Yes
  • My change supports the following platforms;
    • Android
    • iOS
  • My change includes tests;
    • e2e tests added or updated in __tests__e2e__
    • jest tests added or updated in __tests__
  • I have updated TypeScript types that are affected by my change.
  • This is a breaking change;
    • Yes
    • No

Test Plan

Built and installed both variants (legacy default and -PgoogleMobileAdsSdk=next-gen) on a real emulator. Confirmed via logcat that next-gen actually initializes and talks to Google's ad servers, and manually loaded real test ads for interstitial, rewarded, banner and native under both. Ad Manager unit IDs get rejected with a clear error under next-gen; regular AdMob unit IDs for the same formats work normally under both.

yarn tests:jest and yarn tsc:compile pass. iOS wasn't built locally (no Xcode in this environment) — the only iOS change is a small constantsToExport addition, worth having CI confirm.

Adds an opt-in Android build path against Google's newer GMA Next-Gen
SDK (ads-mobile-sdk), selected via a `google_mobile_ads_sdk` app.json
flag (or `googleMobileAdsSdk` Expo config-plugin parameter). Defaults
to the existing legacy SDK (play-services-ads), so existing apps are
unaffected.

- Dual native implementations per ad format under android/src/legacy
  and android/src/nextgen, selected at build time via a Gradle source
  set switch (init/consent, Interstitial, Rewarded, RewardedInterstitial,
  AppOpen, Banner, Native Ads).
- GMA Next-Gen SDK has no Google Ad Manager equivalent, so Ad Manager
  ad unit IDs are explicitly rejected under next-gen rather than
  silently degrading.
- Excludes play-services-ads from the classpath when next-gen is
  selected, since both SDKs ship classes in the same Java package and
  cannot coexist.
- Exposes `mobileAds().googleMobileAdsSdk` so JS can detect which SDK
  variant is active (e.g. to hide Ad Manager specific UI).
- Adds Expo config-plugin support, CI build matrix coverage, and docs
  (docs/next-gen-sdk.mdx).
@docs-page

docs-page Bot commented Aug 20, 2026

Copy link
Copy Markdown

To preview the documentation for this pull request, visit the following URL:

docs.page/invertase/react-native-google-mobile-ads~882

Documentation is deployed and generated using docs.page

@exzos28

exzos28 commented Aug 20, 2026

Copy link
Copy Markdown
Author

jet's "Run All" crashes with "Maximum update depth exceeded" on React 19/RN 0.86. Also can't find where jet/mocha actually gets invoked anywhere - no mocha, no config, CI just does react-native run-android. Might be worth moving to Detox at some point?

@mikehardy

Copy link
Copy Markdown
Collaborator

Hey @exzos28 👋 ! This is amazing, thank you - also, an amazing coincidence - over @invertase we'd noticed this push for Android next gen now and we've got some time planned starting a couple days ago to not only adopt it here but to give the whole repository a refresh (with Android Next Gen being just one piece)

We've been busy doing analysis on the same but we hadn't published anything yet. Let me see how you've done the work for the NextGen adoption and see what I can get in for you now quickly or what we can harvest - but either way, testing will get a refresh (it's not working well, as you noticed) - and we will have Next Gen support very soon either through this PR, or some other way, really soon

@mikehardy

Copy link
Copy Markdown
Collaborator

Hey @exzos28 👋

Thanks for putting this together, and for the genuinely thorough test plan. The overall shape here is what I'd want: build-time selection rather than runtime, legacy staying the default so nobody's upgrade silently changes SDKs, dual source sets, and the classpath exclusion because the two SDKs collide on the same Java package. That last one is a constraint we'd otherwise have found out the hard way, so thank you for working it through.

One correction I think we need before this can land, and I want to be crystal clear about it because it's the load-bearing part. The PR rejects Ad Manager ad unit IDs under next-gen, on the basis that next-gen has no Ad Manager equivalent. According to Google's docs, Ad Manager is supported. There's a full Ad Manager next-gen documentation tree, for example the quick start and combining native + banner.

If I'm reading it right, what's actually gone is the separate AdManagerAdRequest / AdManagerAdView classes. Ad Manager folds into the same per-format request builders with Ad Manager-only fields on them (NativeAdRequest.Builder.addCategoryExclusion is documented as Ad Manager only, for instance). So I think the fix is to drop the rejection and map the GAM-only options onto the next-gen builders, rather than gating on the shape of the unit ID. Please correct me if I'm wrong, I'd much rather be shown a symbol that doesn't exist than ship a gate that blocks working setups.

This matters more than a normal review nit because Ad Manager publishers are a meaningful share of this library's users, and for them a next-gen path that refuses their unit IDs isn't opt-in, it's unusable.

Fair warning on process, since I don't want you sinking more time into rework blind: we have a larger piece of work going on with this library internally right now, and next-gen Android behind the existing JS API is part of it. I'll come back here with a concrete answer on whether we take this as-is with the Ad Manager fix, take the mechanism and build on it, or land it another way. Either way, the app.json / Expo config flag design and the source-set split are the parts I expect to keep, and I'll credit the work.

One more thing worth flagging because it will bite someone eventually: per Google's migration guide, next-gen works only with no mediation or with AdMob as the mediation platform. Apps hosting mediation elsewhere can't use next-gen at all. Probably worth a line in docs/next-gen-sdk.mdx if this lands.

Thanks again.

Cheers

GMA Next-Gen SDK has no separate AdManagerAdRequest/AdManagerAdView -
Ad Manager unit IDs load through the same AdRequest.Builder as AdMob
ones, so rejecting them here was incorrect.
Ad Manager native ad units load through the same NativeAdRequest as
AdMob ones under GMA Next-Gen SDK - there is no dedicated Ad Manager
request/view type to justify rejecting them.
GMA Next-Gen SDK's AdView/BannerAdRequest load Ad Manager and AdMob
unit IDs the same way, so rejecting "/" prefixed unit IDs was wrong.
No caller rejects Ad Manager unit IDs anymore, so this helper and its
now-inaccurate doc comment are dead weight.
Ad Manager unit IDs work fine under next-gen (see the android module
fixes), so there's no longer a reason to gate these demos behind
googleMobileAdsSdk !== 'next-gen'.
Ad Manager is supported under next-gen through the same request
builders as AdMob - the real constraint is mediation (AdMob-only or
none), not Ad Manager.
@exzos28

exzos28 commented Aug 25, 2026

Copy link
Copy Markdown
Author

Hi @mikehardy

Thanks for the careful read, and yeah, you're right. I went back and checked against the actual SDK rather than assuming, and the rejection was wrong - Ad Manager unit IDs go through the exact same builders as AdMob ones, no separate classes needed. Dropped the gate, wired the GAM-only fields through properly, and un-hid the Ad Manager demos in the example app that I'd gated off for no reason.

Good call on the mediation note too - added a line about that to the docs page.

@exzos28

exzos28 commented Aug 25, 2026

Copy link
Copy Markdown
Author

Quick update - tested on a real emulator against the actual GAM test units:

  • GAM Anchored Adaptive Banner - loads fine;
  • GAM Interstitial - load/show both work.
  • GAM Fluid (reusing TestIds.GAM_NATIVE per the existing example comment) - consistently no-fill for me. To rule out a regression, I rebuilt the same example against legacy and got the exact same no-fill on the identical unit/request, so it's not next-gen-specific - likely just inventory/regional availability for that test unit's fluid-shaped creative.

@exzos28

exzos28 commented Aug 25, 2026

Copy link
Copy Markdown
Author

Separately: happy to help set up proper e2e testing for this repo if that's useful, the current jet-based setup seems to have some issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants