Floxis Android SDK guide for AppLovin MAX#
Availability: floxis-sdk-max ships in the next SDK release. Until then, ask your Floxis account manager for
early access and the version number to use.
The Floxis MAX SDK adds Floxis demand to an Android app that already mediates with AppLovin MAX. It is a thin layer over the open-source Prebid Mobile SDK and Prebid's own MAX adapter, installed as one Gradle dependency.
Before you start, set up an endpoint for the app on the Supply page with Integration type Floxis SDK. When you open the saved endpoint, its integration panel shows:
- the seat and region you pass to the SDK, in its Android setup line
FloxisConfig(seat = "…", region = "…", …); - the AdMob setup sheet, which lists your price tiers. You use the same tiers for MAX (see step 4).
How it works#
- Floxis auction. Before each MAX load, the SDK runs one auction for the placement (1.5 s deadline by default).
- MAX load. The SDK then loads the MAX ad. If Floxis won, Prebid keeps the winning bid and hands its ID to the
MAX ad object, together with the price tier (for example
hb_pb = 0.70). - Custom network renders. MAX works down its waterfall. When it reaches the Floxis placement whose Custom
Parameters match the tier (
{"hb_pb":"0.70"}), Prebid's MAX adapter shows the Floxis ad.
Floxis takes part in your MAX waterfall, at the CPM Price you give each Floxis placement. It does not take part in MAX's in-app bidding auction: the adapter is an SDK custom network and supplies no bidding signals to MAX. A bidder or waterfall entry that MAX ranks above the Floxis placement and that fills first still wins.
If Floxis doesn't bid, doesn't answer in time, is switched off or has no consent, MAX still loads (after at most
timeoutMs when Floxis doesn't answer) and the Floxis placements pass to the next entry.
Requirements#
| Item | Value |
|---|---|
| Android | Minimum SDK 24. Built and tested with compileSdk = 35. |
| AppLovin MAX SDK | Built and tested with 13.1.0, which Gradle adds for you. |
| Prebid Mobile | 3.4.0 and its MAX adapter 3.4.0, added for you. |
| Formats | Banner 320x50, MREC 300x250, interstitial (display, video or both), rewarded video. |
| Not supported | Native, app open, rewarded interstitial, rewarded display, other banner sizes (e.g. 728x90). |
| Floxis iOS SDK with MAX | Not available. The iOS SDK supports AdMob only. |
1. Add the SDK#
Add the SDK repository to settings.gradle.kts. The exclusiveContent filter makes Gradle look there only for
tech.floxis packages, and everything else still comes from Google and Maven Central:
dependencyResolutionManagement {
repositories {
google(); mavenCentral()
exclusiveContent {
forRepository { maven("https://maven.floxis.tech/releases") }
filter { includeGroup("tech.floxis") }
}
}
}Then add the dependency to app/build.gradle.kts. Replace VERSION with the version your account manager gives
you:
implementation("tech.floxis:floxis-sdk-max:VERSION")Gradle brings in Prebid Mobile 3.4.0, Prebid's MAX adapter 3.4.0, AppLovin MAX SDK 13.1.0 and the shared Floxis
runtime floxis-sdk-common. Don't add floxis-sdk-common yourself. If your app already declares a newer AppLovin
MAX SDK, Gradle uses yours. Imports are com.floxis.sdk.* and com.floxis.sdk.max.*.
R8, ProGuard and permissions#
- R8 / ProGuard: the SDK ships its own consumer rules, including a keep rule for the adapter class MAX loads by name. You don't add any rules.
- Permissions: the Floxis SDK adds
INTERNETandACCESS_NETWORK_STATEonly. It never asks for location. The AppLovin MAX SDK addscom.google.android.gms.permission.AD_IDitself.
Apps with more than one mediation SDK#
floxis-sdk-max can share an app with any other Floxis Android artifact: AdMob (floxis-sdk), Next-Gen
(floxis-sdk-nextgen), Google Ad Manager (floxis-sdk-gam) and Unity LevelPlay (floxis-sdk-levelplay). Next-Gen
can't share an app with floxis-sdk or floxis-sdk-gam, and Gradle refuses that pair.
One Floxis.initialize call covers the AdMob, Next-Gen, Ad Manager and MAX artifacts, so they all use one seat
and region. The LevelPlay adapter takes its seat and region from the LevelPlay dashboard instead. Use the same ones
there, or the Floxis network in LevelPlay fails to initialise. Keep every tech.floxis artifact on the same version.
Floxis sets Prebid Mobile's server and account for the whole app. If your app already uses Prebid Mobile with another Prebid Server, talk to your account manager before adding Floxis.
2. Initialise Floxis, then MAX#
Prebid's MAX adapter tells MAX it is ready only if Floxis has already started Prebid Mobile when MAX initialises its networks. So start MAX from the Floxis callback. The callback always fires, on the main thread, even when Floxis initialisation fails (at the latest after 12 seconds), so MAX always starts.
Use the seat and region from your endpoint's integration panel and your MAX SDK key. This runs in an Activity:
import android.util.Log
import com.applovin.sdk.AppLovinMediationProvider
import com.applovin.sdk.AppLovinSdk
import com.applovin.sdk.AppLovinSdkInitializationConfiguration
import com.floxis.sdk.Floxis
import com.floxis.sdk.FloxisConfig
Floxis.initialize(
applicationContext,
FloxisConfig(seat = "YOUR_SEAT", region = "us-e", timeoutMs = 1500),
) { status, description ->
Log.d("Floxis", "init $status $description")
val maxConfig = AppLovinSdkInitializationConfiguration.builder("YOUR_MAX_SDK_KEY", this)
.setMediationProvider(AppLovinMediationProvider.MAX)
.build()
AppLovinSdk.getInstance(this).initialize(maxConfig) { runOnUiThread { loadAds() } }
}If Floxis initialisation failed, or Floxis is switched off, MAX still serves your other networks, but the Floxis placements may not fill in that session. A failed initialisation is retried on a later ad load, at most once every 30 seconds.
Options#
| Option | What it does |
|---|---|
timeoutMs | The longest the SDK waits for Floxis before MAX loads without it (200–10,000 ms, default 1,500). |
debug | Adds FLX-xxx debug lines under the tag Floxis and turns on Prebid's debug log. Warnings are logged either way. It never changes what is sent. |
testMode | Marks auctions as test traffic. It only takes effect in a debuggable build; a release build ignores it. |
enabled / Floxis.setEnabled() | Kill switch. false stops all Floxis auctions at once, for example from your remote config. MAX keeps loading. |
personalizedAds / Floxis.setPersonalizedAds() | false skips Floxis auctions; MAX still loads. Update it when consent changes. |
shareLocation | Default true: sends the device's last known location when the app already has permission (see step 6). |
storeUrl | The app's store URL sent to buyers. Defaults to the store URL already set on Prebid Mobile, otherwise the app's Google Play page. |
FloxisConfig throws IllegalArgumentException for a seat that isn't 1–128 characters of letters, digits, ., _
or -, a region that isn't a lowercase name such as us-e, a timeout outside 200–10,000 ms or a blank storeUrl.
That way a typo shows up the first time the code runs.
3. Add Floxis as a custom network in MAX#
Do this once per MAX account:
In MAX, open Mediation → Manage → Networks and choose Click here to add a Custom Network.
Set Network Type to SDK.
Set Name to a name you will recognise, for example
Floxis. MAX shows this name in its reports and inMaxAd.networkName.Set Android Adapter Class Name to:
com.applovin.mediation.adapters.PrebidMaxMediationAdapterLeave iOS Adapter Class Name empty. Save.
This is Prebid's open-source MAX adapter, which the Floxis SDK brings in. There is no separate Floxis adapter class, and the network needs no app ID or keys.
4. Add Floxis placements to each ad unit#
For each MAX ad unit that should get Floxis demand (banner, MREC, interstitial or rewarded):
- Open Mediation → Manage → Ad Units, open the ad unit, and find your Floxis network under **Custom Networks & Deals. Set its **Status to on.
- Add one placement per price tier:
- Placement ID: any label, for example
floxis_0.70. - Custom Parameters:
{"hb_pb":"0.70"} - CPM Price:
0.70 - Country Targeting: all countries. A Floxis win in an excluded country can't show.
- Placement ID: any label, for example
- Create every tier. By default the tiers are every $0.10 step from 0.10 to 20.00 (200 placements). The endpoint's AdMob setup sheet lists your tiers: use its Parameter column as the Custom Parameters and its eCPM column as the CPM Price. Ignore its Label and Class Name columns, which are for AdMob.
- Save the ad unit.
Matching rules#
- Prebid's adapter renders the Floxis ad only when every key in the placement's Custom Parameters is in the winning
bid with the same value. Use the
hb_pbkey only, written exactly as in the sheet:"0.70", not"0.7". - Floxis rounds each winning price down to the highest tier at or below it, and sends no bid below the lowest tier. A missing placement means a winning Floxis bid at that tier can't show.
- Fewer placements: enter a shorter list in the endpoint's AdMob price tiers field, then create one placement per tier in that list. The list applies to every app on the endpoint, so an app that also uses AdMob on the same endpoint needs a Custom Event for each tier too. When you change the tiers, update MAX to match.
5. Load ads#
configId is your name for the placement (for example home_banner, level_end_interstitial). Floxis reports by
placement, so give each placement its own name and keep it the same across app versions. It is separate from your
MAX ad unit ID and your seat.
Rules for every format:
- Create one Floxis wrapper per MAX ad object and reuse it for every later load.
- Always reload through the wrapper. Calling
loadAd()on the MAX object directly loads without a Floxis bid. - The
FloxisAuctionListenerreports the Floxis auction.BIDmeans Floxis bid, not that MAX chose Floxis. To see whether your Floxis network served, comparead.networkNamein MAX'sonAdLoadedwith the name from step 3. - MAX's own listeners still report loads, impressions, clicks, hides and rewards.
- Floxis callbacks arrive on the main thread.
Banner and MREC#
Create the MAX view, add it to your layout, then wrap it:
private lateinit var bannerView: MaxAdView
private lateinit var banner: FloxisMaxBannerAd
private fun setUpBanner() {
bannerView = MaxAdView("YOUR_MAX_BANNER_AD_UNIT_ID", this)
container.addView(bannerView, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, bannerHeightPx))
banner = FloxisMaxBannerAd(
adView = bannerView,
configId = "home_banner",
size = FloxisSize(320, 50),
refreshIntervalSeconds = 60, // 30..120, 0 turns refresh off
listener = { outcome -> Log.d("Floxis", "$outcome") },
)
banner.load()
}
override fun onPause() { banner.pause(); super.onPause() }
override fun onResume() { super.onResume(); banner.resume() }
override fun onDestroy() { banner.destroy(); bannerView.destroy(); super.onDestroy() }For an MREC, create the view with MaxAdView("YOUR_MAX_MREC_AD_UNIT_ID", MaxAdFormat.MREC, this) and pass
FloxisSize(300, 250). Any other size throws IllegalArgumentException.
The wrapper stops MAX's own auto-refresh and refreshes the banner itself, with a new Floxis auction each time. Don't
call startAutoRefresh(). A refresh runs only while the app is in the foreground, the screen is on and at least half
of the banner is visible; otherwise it waits for the next interval. Call pause() and resume() from your
activity yourself. destroy() stops the Floxis banner but not the MAX view, so destroy the view as well. If you
forget, and the activity is an AndroidX ComponentActivity (for example AppCompatActivity), the Floxis banner
stops itself when the activity is destroyed. Refresh also stops while the view is detached from the window.
Interstitial#
private lateinit var maxInterstitial: MaxInterstitialAd
private lateinit var floxisInterstitial: FloxisMaxInterstitialAd
private fun setUpInterstitial() {
maxInterstitial = MaxInterstitialAd("YOUR_MAX_INTERSTITIAL_AD_UNIT_ID", this)
floxisInterstitial = FloxisMaxInterstitialAd(this, maxInterstitial, "level_end_interstitial")
maxInterstitial.setListener(object : MaxAdListener {
override fun onAdLoaded(ad: MaxAd) { Log.d("MAX", "loaded from ${ad.networkName}") }
override fun onAdDisplayed(ad: MaxAd) { }
override fun onAdHidden(ad: MaxAd) { floxisInterstitial.load() }
override fun onAdClicked(ad: MaxAd) { }
override fun onAdLoadFailed(adUnitId: String, error: MaxError) { reloadInterstitialLater() }
override fun onAdDisplayFailed(ad: MaxAd, error: MaxError) { floxisInterstitial.load() }
})
floxisInterstitial.load { outcome -> Log.d("Floxis", "$outcome") }
}
private fun showInterstitial() {
if (maxInterstitial.isReady) maxInterstitial.showAd(this)
}In reloadInterstitialLater(), call floxisInterstitial.load() after your usual back-off, never
maxInterstitial.loadAd().
By default each interstitial auction asks for display and video together, and the higher bid wins. For one kind
only, pass FloxisInterstitialOptions(FloxisInterstitialFormat.DISPLAY) (or VIDEO) as the fourth argument. Video
length and skipping can be limited with FloxisInterstitialOptions(video = FloxisVideoOptions(maxDurationSeconds = 30)).
Rewarded#
private lateinit var maxRewarded: MaxRewardedAd
private lateinit var floxisRewarded: FloxisMaxRewardedAd
private fun setUpRewarded() {
maxRewarded = MaxRewardedAd.getInstance("YOUR_MAX_REWARDED_AD_UNIT_ID", this)
floxisRewarded = FloxisMaxRewardedAd(this, maxRewarded, "rewarded_main")
maxRewarded.setListener(object : MaxRewardedAdListener {
override fun onUserRewarded(ad: MaxAd, reward: MaxReward) { grant(reward) }
override fun onAdLoaded(ad: MaxAd) { }
override fun onAdDisplayed(ad: MaxAd) { }
override fun onAdHidden(ad: MaxAd) { floxisRewarded.load() }
override fun onAdClicked(ad: MaxAd) { }
override fun onAdLoadFailed(adUnitId: String, error: MaxError) { reloadRewardedLater() }
override fun onAdDisplayFailed(ad: MaxAd, error: MaxError) { floxisRewarded.load() }
})
floxisRewarded.load { outcome -> Log.d("Floxis", "$outcome") }
}Grant rewards only from MAX's onUserRewarded. A Floxis BID is not a reward. Video length and skipping can be
limited with FloxisMaxRewardedAd(this, maxRewarded, "rewarded_main", FloxisVideoOptions(maxDurationSeconds = 30)).
Floxis rewarded demand is VAST video only. Prebid Mobile 3.4.0 can crash on a rewarded video that has neither a
click-through URL nor an end card, so the SDK turns such a bid down before MAX loads. The auction then ends as
NO_BID with the detail declined: rewarded VAST without ClickThrough or companion end card.
Cancel and clean up#
For interstitial and rewarded, load() returns a handle, and each new load() cancels the one before. cancel() on
the wrapper (or the handle) drops the pending auction and the MAX load that would follow it; no Floxis callback
fires afterwards. Cancel before you destroy the MAX ad. (Banners have no cancel(); use destroy() as shown above.)
override fun onDestroy() {
floxisInterstitial.cancel(); maxInterstitial.destroy()
floxisRewarded.cancel(); maxRewarded.destroy()
super.onDestroy()
}6. Privacy and consent#
You stay responsible for collecting consent and declaring data use. The SDK privacy and consent guide has the full list of what is sent and the Play Data safety answers.
- Consent strings. Your CMP must write the standard IAB TCF v2, US Privacy and GPP keys to the app's default
SharedPreferencesbefore the first ad load. Prebid Mobile reads them and sends them with every Floxis auction. - Personalised ads.
Floxis.setPersonalizedAds(false)skips Floxis auctions only. It doesn't change MAX's settings, so keep setting MAX's own consent (AppLovinPrivacySettings.setHasUserConsent,AppLovinPrivacySettings.setDoNotSell) for your other networks as you do today. - Device and session signals. Battery level, charging, battery saver, dark mode, ringer state, whether the media
volume is muted (as video
playbackmethod), impression depth and session length are sent only when personalised ads are on, GDPR doesn't apply or the TC string grants Purpose 1, and neither the US Privacy string nor an applicable GPP US section records an opt-out of sale, sharing or targeted advertising. - Limited ad tracking and opt-outs. For these requests, Floxis strips user IDs, the full IP address and the device and session signals.
- Location. Sent only when
shareLocationis on and the app already holds a location permission, and never for a child-directed request, when GDPR applies without TCF special feature 1, or after a US privacy or GPP US opt-out.
Child-directed apps (COPPA)#
Floxis doesn't block child-directed traffic. It passes your COPPA flag to the auction as regs.coppa, as Prebid does.
MAX gives Floxis no child-directed flag to read, so set Prebid's flag yourself inside the Floxis.initialize
callback, before the first ad load. Prebid ignores the call if it runs before Floxis has started Prebid, and it
keeps the flag only until the app process ends, so set it on every launch. If the callback reports FAILED or
DISABLED, set it again once Floxis.isInitialized() returns true, for example before your next ad load.
import org.prebid.mobile.TargetingParams
TargetingParams.setSubjectToCOPPA(true)If the app also uses the AdMob, Next-Gen or Ad Manager Floxis SDK and sets Google's tagForChildDirectedTreatment,
Floxis copies that tag into the same flag before every auction, so keep the two consistent.
Floxis is not part of Google Play's Families Self-Certified Ads SDK Program. Check AppLovin's own policy for
child-directed apps as well. To remove the advertising ID permission that the AppLovin SDK adds, declare it in your
manifest with tools:node="remove":
<uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove" />7. Add the app-ads.txt line#
Buyers that check app-ads.txt will not bid on the app without the Floxis line, in the form
floxis.tech,<Floxis seller id>,DIRECT. Add it to app-ads.txt at the root of the developer website on the app's
Play Store listing. Ask the dashboard assistant "what is my ads.txt line for Floxis" for the exact line, and see the
Floxis ads.txt, app-ads.txt and sellers.json Guide.
8. Test before release#
- In a debug build, set
debug = trueandtestMode = trueinFloxisConfig. - Run
adb logcat -s Floxis. You should seeFLX-101 SUCCEEDEDat start-up, then oneFLX-200 AuctionOutcome(status=…)line per ad load. - Open MAX's Mediation Debugger with
AppLovinSdk.getInstance(this).showMediationDebugger(). Check that your Floxis custom network is listed and initialised, and that your ad units include it. Prebid's adapter reports version 3.4.0. - If you use MAX test mode, register your test device through
setTestDeviceAdvertisingIds(listOf("YOUR_GAID"))on theAppLovinSdkInitializationConfigurationbuilder. - Load each format. When the auction says
BID, check thatad.networkNameinonAdLoadedis your Floxis network. Test auctions get fewer bids, because some buyers don't bid on test traffic. - Before release, remove the test device IDs and set
testMode = false. A release build ignorestestModeanyway and logsFLX-105. - Tell your account manager your placement names and the date of the release build, so the first live traffic can be checked with you.
9. Troubleshooting#
Auction results#
AuctionOutcome.status | What it means | What to do |
|---|---|---|
BID | Floxis bid. priceBucket is the tier MAX must match. | If MAX shows another network, see "Floxis bid but MAX showed another network" below. |
NO_BID | No buyer bid above your lowest tier, or a rewarded bid was declined. | Normal. Read detail for a declined rewarded bid. |
TIMEOUT | Floxis didn't answer within timeoutMs. | Check the network. Raise timeoutMs only if your ad load can afford it. |
ERROR | The auction failed; detail says why. | Check the seat and region. Send detail to your account manager if it repeats. |
SKIPPED_NOT_INITIALIZED | The load ran before Floxis finished starting, or start-up failed. | Load ads after the Floxis callback. A failed start is retried on later loads. |
SKIPPED_NO_CONSENT | Personalised ads are off. | Expected after a consent refusal. |
SKIPPED_DISABLED | The kill switch is on. | Call Floxis.setEnabled(true) or fix your remote config. |
Floxis bid but MAX showed another network#
- MAX filled from a bidder or a waterfall entry ranked above the Floxis placement. That is normal waterfall behaviour.
- No placement exists for that
priceBucket, or its Custom Parameters differ ("0.7"instead of"0.70"). - The Floxis network is off in the ad unit, or its placement excludes the user's country.
- MAX started before Floxis, so MAX never marked the Floxis network ready. Start MAX from the Floxis callback.
- The app called
loadAd()on the MAX object instead ofload()on the Floxis wrapper.
MAX reports the adapter's error for each Floxis placement it tried, in the Mediation Debugger and in the waterfall
details of MaxError:
| Code | Message | Meaning |
|---|---|---|
| 1001 | Parameters are empty! | The placement has no Custom Parameters. |
| 1002 | Response id is null | There was no Floxis bid for this load. Normal after NO_BID, TIMEOUT or a skip. |
| 1003 | Parameters don't match | The bid's tier differs from this placement. Normal for every Floxis placement except one. |
| 1004 | There's no response for id: … | The bid was already used or has gone stale. Reload through the Floxis wrapper. |
Other problems#
IllegalArgumentExceptionwhen creating a wrapper: theconfigIdis blank, or the banner size is not 320x50 or 300x250.- The banner doesn't refresh: the view is hidden or less than half visible, the app is in the background,
refreshIntervalSecondsis 0, or you calledpause()withoutresume(). - Log codes (tag
Floxis):FLX-101start-up result,FLX-102switched off inFloxisConfig,FLX-103start-up threw an error,FLX-104start-up retry,FLX-105test mode ignored,FLX-200auction result,FLX-201auction skipped,FLX-203auction failed,FLX-204bid declined,FLX-300toFLX-303banner refresh. Warnings (FLX-102,FLX-103,FLX-104,FLX-105,FLX-203,FLX-204,FLX-302) always appear; the other codes appear only withdebug = true.
For the full list of codes and test steps, see the SDK testing and troubleshooting guide.
Versions, API reference, licence and sample app#
The SDK release notes list each version and the AppLovin MAX and Prebid versions it was built and tested with.
The SDK API reference lists every public class and method. The SDK ships as signed binaries from
maven.floxis.tech, under the licence published there. Your account manager can share a sample app that loads each
MAX format on your seat.