SDK Integration
One tenant ID, one init call, one line to show an ad. Ad credentials and placement IDs are delivered at runtime — there are no ad-network accounts or keys for you to manage.
Add the SDK repository and the mediation repositories to settings.gradle.kts (projects without dependencyResolutionManagement: the root build file's allprojects.repositories). All six are required.
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
// AdsPlus SDK
maven { url = uri("https://www.bytemedia.mobi/adsplus/maven") }
// Required mediation dependency repositories — add exactly as shown
maven { url = uri("https://jfrog.anythinktech.com/artifactory/overseas_sdk") }
maven { url = uri("https://artifact.bytedance.com/repository/pangle") }
maven { url = uri("https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea") }
maven { url = uri("https://artifactory.bidmachine.io/bidmachine") }
maven { url = uri("https://cboost.jfrog.io/artifactory/chartboost-ads/") }
}
}Then, in your app module, set compileSdk 35 and add the dependency. ProGuard / R8 rules and manifest permissions are bundled — nothing else to add.
android {
compileSdk = 35 // required by a mediation dependency
}
dependencies {
implementation("com.gameadsdk:ads-sdk-android:1.0.13")
}| compileSdk | 35 or higher (targetSdk can stay; 34 is tested) |
| minSdk | 21 or higher |
| Android Gradle Plugin | 8.5.2 or higher (needs Gradle 8.7+ and JDK 17 to run the build) |
| Java source level | 11 or higher (compileOptions) |
| Kotlin Gradle plugin | 2.0 or higher — only if your app module uses Kotlin |
On AGP 8.5.x (which officially supports up to compileSdk 34), also add this to gradle.properties:
# AGP 8.5.x only — not needed on AGP 8.6+
android.suppressUnsupportedCompileSdk=35Kotlin projects on plugin 1.9.x fail in compileDebugKotlin with Module was compiled with an incompatible version of Kotlin. Upgrade the Kotlin Gradle plugin to 2.0+ (recommended), or add this to your app module. Pure-Java apps are not affected.
// Only if you are stuck on Kotlin Gradle plugin 1.9.x — prefer upgrading to 2.0+
android {
kotlinOptions {
freeCompilerArgs += listOf("-Xskip-metadata-version-check")
}
}Initialize once, in Application.onCreate() — not in an Activity. If your project has no Application class yet, create one:
import android.app.Application;
import android.util.Log;
import com.gameadsdk.GameAdSDK;
import com.gameadsdk.callbacks.GADSDKCallback;
import com.gameadsdk.core.GADConfig;
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
GADConfig config = new GADConfig.Builder("tp_abc1234567") // your tenant ID
.packageId(getPackageName())
.build();
GameAdSDK.getInstance().initialize(this, config, new GADSDKCallback() {
@Override
public void onSDKInitialized() {
// Ready. The SDK is already preloading ads — show them whenever you like.
}
@Override
public void onSDKInitializeFailed(String error) {
Log.e("AdsPlus", "Init failed: " + error);
}
});
}
}and register it in the manifest:
<application
android:name=".MyApplication"
... >
</application>After init the SDK keeps a rewarded and an interstitial ad preloaded and refills them after every show. You never create ad objects, handle placement IDs or track load state — just call show.
import com.gameadsdk.GameAdSDK;
import com.gameadsdk.ads.RewardedAd;
import com.gameadsdk.callbacks.RewardedAdCallback;
// Optional: enable your "Watch ad" button only when an ad is ready
watchAdButton.setEnabled(GameAdSDK.getInstance().isRewardedReady());
GameAdSDK.getInstance().showRewarded(activity, new RewardedAdCallback() {
@Override
public void onUserRewarded(RewardedAd ad, String rewardType, int rewardAmount) {
// The user earned the reward — grant it here.
// rewardType / rewardAmount are placeholders ("" / 1); decide the reward in your own code.
}
// Exactly one of the next three fires per call — resume your game there.
// `ad` may be null in the two failure callbacks; use the error string.
@Override
public void onAdClosed(RewardedAd ad) { resumeGame(); }
@Override
public void onAdLoadFailed(RewardedAd ad, String error) { resumeGame(); }
@Override
public void onAdDisplayFailed(RewardedAd ad, String error) { resumeGame(); }
});import com.gameadsdk.GameAdSDK;
import com.gameadsdk.ads.InterstitialAd;
import com.gameadsdk.callbacks.InterstitialAdCallback;
GameAdSDK.getInstance().showInterstitial(activity, new InterstitialAdCallback() {
// Exactly one of these fires per call. `ad` may be null in the failure callbacks.
@Override
public void onAdClosed(InterstitialAd ad) { resumeGame(); }
@Override
public void onAdLoadFailed(InterstitialAd ad, String error) { resumeGame(); }
@Override
public void onAdDisplayFailed(InterstitialAd ad, String error) { resumeGame(); }
});The banner is the one format you hold as an object, because it stays on screen:
import com.gameadsdk.GameAdSDK;
import com.gameadsdk.ads.MRECAd;
import com.gameadsdk.callbacks.MRECAdCallback;
// Create it only after onSDKInitialized() — the placement ID is null before that.
MRECAd mrecAd = new MRECAd(GameAdSDK.getInstance().getMrecPlacementId());
mrecAd.setCallback(new MRECAdCallback() {
@Override
public void onAdLoaded(MRECAd ad) {
ad.show(activity); // auto-positioned bottom-center
}
@Override
public void onAdLoadFailed(MRECAd ad, String error) { /* no ad this time */ }
});
mrecAd.load(activity); // must be given the Activity
// Toggle visibility
mrecAd.hide();
mrecAd.show();
// In the Activity's onDestroy()
mrecAd.destroy();Build, launch the app on a device or emulator, and watch the SDK log:
adb logcat -s GameAdSDK:*| What you see | What it means |
|---|---|
| Init succeeds and onSDKInitialized() fires | Integration verified. Call showRewarded() and you should see an ad. |
| HTTP 403: Invalid tenant or package not approved | If the app is still pending review: your integration is complete — it starts working on approval, with no code change. If the app is approved: your applicationId does not exactly match the package name registered in the console. |
| Init succeeds, but ads fail with no_fill | Ad networks are still being connected to your ad slots (usually 1-3 business days after approval). Not an integration bug — wait for "Ad demand configured" on the Integration status card. |
You can also confirm it without a cable: the app's page in the console has an Integration status card that flips to “SDK initialized” within about ten seconds of the first successful init, and to “First ad shown” after the first impression.
Claude Code, Cursor, Copilot and similar agents can do all of the above unattended. We publish a self-contained spec written for them:
Or paste this generic prompt and fill in the two placeholders:
You are integrating the AdsPlus ad-monetization SDK into my Android app.
App facts (use these values verbatim):
- tenant_id: TENANT_ID <- from AdsPlus console -> My Apps
- package name (applicationId must match exactly): YOUR_PACKAGE_NAME
- platform: android
Fetch and follow EXACTLY this integration spec — it overrides any other
AdsPlus snippet you find elsewhere:
https://bytemedia.mobi/adsplus/integration/android.md
Acceptance criteria:
1. ./gradlew assembleDebug succeeds.
2. On launch, "adb logcat -s GameAdSDK:*" shows init success (onSDKInitialized fires).
3. If init fails with HTTP 403 "Invalid tenant or package not approved" and the app
is still pending review, the code integration is COMPLETE — stop and report that.
4. Never create ad objects before onSDKInitialized() fires.The SDK identifies users on its own. If your app has stable account IDs and you want the same user recognised after a reinstall, pass one as outerId when you build the config. (There is also updateUserId(), but it is ignored once the user has been identified, which normally happens during init — use outerId.)
GADConfig config = new GADConfig.Builder("tp_abc1234567")
.packageId(getPackageName())
.outerId("your_account_id") // stable per user; survives reinstall
.build();Pacing is managed on the platform side; there is nothing to configure in the app. When ads are switched off for your app or for a particular user, show calls end in the failure callback with ads_disabled (MREC: limited: <reason>). The last known state is cached, so a briefly unreachable backend does not interrupt playback.
RewardedAd and InterstitialAd can also be driven by hand (load → isReady → show → destroy). You then own preloading, reloading and cleanup yourself, so prefer the one-call API in step 4 unless you have a specific reason.
// Only if you need to own the ad object. Create it after onSDKInitialized().
RewardedAd rewardedAd = new RewardedAd(GameAdSDK.getInstance().getRewardedPlacementId());
rewardedAd.setCallback(new RewardedAdCallback() {
@Override
public void onAdLoaded(RewardedAd ad) {
// Loading is async — show from here, or just enable your button
if (ad.isReady()) ad.show(activity);
}
@Override
public void onAdClosed(RewardedAd ad) { ad.load(context); } // preload the next one
});
rewardedAd.load(context); // the first load() must be given a Context
// In onDestroy()
rewardedAd.destroy();Lets a web page inside your app's WebView (a wallet, a task list) trigger native ads. On the native side, attach the bridge to the WebView and name the hosts that are allowed to use it — without setAllowedHosts, any page the WebView navigates to could call it. Subdomains are not implied; list each host.
import com.gameadsdk.h5bridge.GADWebViewBridge;
WebView webView = findViewById(R.id.webview);
GADWebViewBridge bridge = new GADWebViewBridge(webView, this)
.setAllowedHosts("your-server.com"); // only pages on these hosts may call the bridge
webView.loadUrl("https://your-server.com/wallet");
// In onDestroy()
bridge.destroy();On the web side, download adsplus-bridge.js, host it next to your page, and call it:
<!-- Download adsplus-bridge.js (link above) and host it next to your page -->
<script src="adsplus-bridge.js"></script>// Show a rewarded ad from the web page
if (AdsPlusBridge.isAdReady('rewarded')) {
AdsPlusBridge.showRewardedAd();
}
// The user finished the ad
window.addEventListener('adsplus:userRewarded', function () {
// Ask YOUR server to credit the reward — a web page can be tampered with.
// e.detail.rewardType / e.detail.amount are placeholders; do not rely on them.
});
// No ad this time (no fill, ads switched off, ...)
window.addEventListener('adsplus:adFailed', function (e) {
console.log('Ad failed:', e.detail.adType, e.detail.error);
});| Symptom | Fix |
|---|---|
| Build error mentioning compileSdk 34 or io.bidmachine.media3 | Set compileSdk to 35 in the app module; on AGP 8.5.x also add the gradle.properties flag from step 2. |
| Module was compiled with an incompatible version of Kotlin | Upgrade the Kotlin Gradle plugin to 2.0+, or add the -Xskip-metadata-version-check snippet from step 2. |
| Could not resolve com.gameadsdk:ads-sdk-android | A repository from step 2 is missing — all six are required. Retry once before digging further; they are remote. |
| IllegalArgumentException: packageId required for tenant mode | Add .packageId(getPackageName()) to the GADConfig builder. |
| sdk_not_initialized | An ad was requested before onSDKInitialized() fired, or init failed — check the init error first. |
| no_fill | No ad is available right now. Expected until ad demand is configured for a new app; occasional afterwards. |
| ads_disabled | Ads are switched off on the platform side for this app or this user. |
| no_activity | MRECAd.load() and the first load() of a manual ad object must be given an Activity / Context. |
| ad_in_progress / load_timeout / display_failed | Another ad is already showing, the ad did not load in time, or it failed to render. Resume your game and try again later. |
Error strings can carry a detail suffix (no_fill: …), so match them with startsWith. For verbose SDK logs while integrating:
GameAdSDK.getInstance().setLogLevel(GADLogger.Level.DEBUG); // com.gameadsdk.core.GADLogger