i want guide me from scratch step by step how to m...

Erstellt am: 26. April 2026

Beantwortet mit GPT-5.5 Thinking von Chat01

Frage

i want guide me from scratch step by step how to make a mobile app using qt6 to test admob, and tell me how to show real ads and earn money and also show me all ads types and try it on app.

note: i want each step has details what.

i will use qt6 creator and want build app in qwidgets not qml, so i want where to show banner for example

ui->label, added from qtcreator and tell me what do to

#include "widget.h"
#include "./ui_widget.h"

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->setText("here banner");
}

Widget::~Widget()
{
delete ui;
}

void Widget::on_pushButton_clicked()
{
// here to show reward video
}

also tell me all available ads and how to show them in qwidgets, and if can tell me prices for each ad for 1000 users

Gedanke

undefined

Gedanke

Implementing adaptive banner ad layout logic

I’m working on setting up an adaptive banner ad in a Qt app for Android. After calculating pixel sizes based on display density, I’ll need to hide the label text. Then, in Java, I’ll use the appropriate AdSize function and ensure the layout overlays correctly above the Qt content.

Thought for 1m 26s

Antwort

Below is a from-scratch Qt 6 Widgets + Android + AdMob guide. It uses your ui->label as the banner placeholder and your pushButton to show a rewarded video.

Important: in a Qt Widgets Android app, AdMob ads are Android native views. A banner cannot truly live “inside” a QLabel. The correct approach is: put a QLabel in Qt Designer where you want the banner, then place an Android AdView overlay at that label’s screen position.

Google’s current Android Mobile Ads SDK guide says to use SDK dependency com.google.android.gms:play-services-ads:25.2.0, minimum SDK 23+, compile SDK 35+, add the AdMob App ID to AndroidManifest.xml, and initialize the SDK once before loading ads. (Google for Developers) Google also says to use test ads while developing; using real ads for testing can lead to account suspension. (Google for Developers)


1. What you will build

You will make a Qt Widgets Android app with:

Ad formatHow it appears in Qt Widgets app
BannerAndroid native banner overlay positioned over ui->label
InterstitialFull-screen ad from a button or natural transition
RewardedFull-screen rewarded video from your on_pushButton_clicked()
Rewarded interstitialFull-screen reward ad shown at natural transition, after an intro/skip screen
App openFull-screen app-open ad when app starts or returns to foreground
NativeAndroid native layout overlay positioned over a Qt placeholder label

AdMob’s main SDK formats are banner, interstitial, native, rewarded, rewarded interstitial, and app open. (Google for Developers)


2. Create the Qt Widgets project

In Qt Creator:

  1. File > New Project
  2. Choose Application > Qt Widgets Application
  3. Build system: CMake
  4. Kit: choose your Android Qt 6 kit
  5. Add these widgets in Qt Designer:
    • QLabel named label
    • QPushButton named pushButton
    • Optional extra buttons:
      • pushButtonInterstitial
      • pushButtonRewardedInterstitial
      • pushButtonAppOpen
      • pushButtonNative

For the banner label:

cpp
ui->label->setMinimumHeight(90); ui->label->setText("Banner placeholder"); ui->label->setStyleSheet("background:#dddddd;");

The banner will cover that label on Android.


3. Add Android template files to your Qt project

In Qt Creator:

  1. Open Projects
  2. Select your Android kit
  3. Find Build Android APK / Android Package Templates
  4. Click Create Templates

This creates an android/ folder in your project.

Qt’s Android deployment docs say custom Android files live under the package source directory, Java/Kotlin code goes under <QT_ANDROID_PACKAGE_SOURCE_DIR>/src/, resources under res/, and the CMake target should set QT_ANDROID_PACKAGE_SOURCE_DIR. (Qt Documentation)

In CMakeLists.txt, add this after your executable target is created:

cmake
if(ANDROID) set_property(TARGET YourTargetName PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android" ) endif()

Replace YourTargetName with your real target name.

Example:

cmake
qt_add_executable(QtAdMobTest main.cpp widget.cpp widget.h widget.ui ) target_link_libraries(QtAdMobTest PRIVATE Qt6::Widgets) if(ANDROID) set_property(TARGET QtAdMobTest PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android" ) endif()

4. Edit android/build.gradle

Open:

text
android/build.gradle

Find the dependencies block and add:

gradle
dependencies { implementation 'com.google.android.gms:play-services-ads:25.2.0' }

Also make sure repositories include:

gradle
repositories { google() mavenCentral() }

Google’s quick-start guide currently lists play-services-ads:25.2.0. (Google for Developers)


5. Edit AndroidManifest.xml

Open:

text
android/AndroidManifest.xml

Inside <application>, add the test AdMob App ID first:

xml
<meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3940256099942544~3347511713"/>

Also make sure your app has hardware acceleration enabled:

xml
<application android:hardwareAccelerated="true" ...>

Google notes that video ads in banner/native views require hardware acceleration. (Google for Developers)

You can also add these permissions near the top:

xml
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

Your manifest should contain something like:

xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <application android:hardwareAccelerated="true" android:label="QtAdMobTest"> <meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3940256099942544~3347511713"/> </application> </manifest>

Later, for real ads, replace the test App ID with your real AdMob App ID.


6. Create the Java bridge

Create this file:

text
android/src/com/example/qtadmob/AdMobBridge.java

Use this code:

java
package com.example.qtadmob; import android.app.Activity; import android.graphics.Color; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.Gravity; import android.view.ViewGroup; import android.widget.Button; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import com.google.android.gms.ads.AdError; import com.google.android.gms.ads.AdListener; import com.google.android.gms.ads.AdLoader; import com.google.android.gms.ads.AdRequest; import com.google.android.gms.ads.AdSize; import com.google.android.gms.ads.AdView; import com.google.android.gms.ads.FullScreenContentCallback; import com.google.android.gms.ads.LoadAdError; import com.google.android.gms.ads.MobileAds; import com.google.android.gms.ads.appopen.AppOpenAd; import com.google.android.gms.ads.interstitial.InterstitialAd; import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback; import com.google.android.gms.ads.nativead.MediaView; import com.google.android.gms.ads.nativead.NativeAd; import com.google.android.gms.ads.nativead.NativeAdView; import com.google.android.gms.ads.rewarded.RewardedAd; import com.google.android.gms.ads.rewarded.RewardItem; import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback; import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd; import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAdLoadCallback; public class AdMobBridge { private static final String TAG = "QtAdMobBridge"; // Google official Android demo ad unit IDs. private static final String TEST_BANNER = "ca-app-pub-3940256099942544/9214589741"; private static final String TEST_INTERSTITIAL = "ca-app-pub-3940256099942544/1033173712"; private static final String TEST_REWARDED = "ca-app-pub-3940256099942544/5224354917"; private static final String TEST_REWARDED_INTERSTITIAL = "ca-app-pub-3940256099942544/5354046379"; private static final String TEST_APP_OPEN = "ca-app-pub-3940256099942544/9257395921"; private static final String TEST_NATIVE = "ca-app-pub-3940256099942544/2247696110"; private static final Handler mainHandler = new Handler(Looper.getMainLooper()); private static AdView bannerView; private static InterstitialAd interstitialAd; private static RewardedAd rewardedAd; private static RewardedInterstitialAd rewardedInterstitialAd; private static AppOpenAd appOpenAd; private static NativeAd nativeAd; private static FrameLayout nativeContainer; public static void initialize(Activity activity) { mainHandler.post(() -> { MobileAds.initialize(activity, initializationStatus -> { Log.d(TAG, "Mobile Ads SDK initialized"); loadInterstitial(activity); loadRewarded(activity); loadRewardedInterstitial(activity); loadAppOpen(activity); }); }); } public static void showBanner(Activity activity, int x, int y, int width, int height) { mainHandler.post(() -> { FrameLayout root = activity.findViewById(android.R.id.content); if (root == null) { Log.e(TAG, "Root view not found"); return; } if (bannerView != null) { root.removeView(bannerView); bannerView.destroy(); bannerView = null; } bannerView = new AdView(activity); bannerView.setAdUnitId(TEST_BANNER); float density = activity.getResources().getDisplayMetrics().density; int adWidthDp = Math.max(320, Math.round(width / density)); AdSize adSize = AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize( activity, adWidthDp ); bannerView.setAdSize(adSize); int adHeightPx = adSize.getHeightInPixels(activity); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, adHeightPx); params.gravity = Gravity.TOP | Gravity.LEFT; params.leftMargin = x; params.topMargin = y; root.addView(bannerView, params); bannerView.setAdListener(new AdListener() { @Override public void onAdLoaded() { Log.d(TAG, "Banner loaded"); } @Override public void onAdFailedToLoad(LoadAdError adError) { Log.e(TAG, "Banner failed: " + adError.getMessage()); } }); bannerView.loadAd(new AdRequest.Builder().build()); }); } public static void hideBanner(Activity activity) { mainHandler.post(() -> { FrameLayout root = activity.findViewById(android.R.id.content); if (root != null && bannerView != null) { root.removeView(bannerView); bannerView.destroy(); bannerView = null; } }); } public static void loadInterstitial(Activity activity) { mainHandler.post(() -> InterstitialAd.load( activity, TEST_INTERSTITIAL, new AdRequest.Builder().build(), new InterstitialAdLoadCallback() { @Override public void onAdLoaded(InterstitialAd ad) { interstitialAd = ad; Log.d(TAG, "Interstitial loaded"); } @Override public void onAdFailedToLoad(LoadAdError error) { interstitialAd = null; Log.e(TAG, "Interstitial failed: " + error.getMessage()); } } )); } public static void showInterstitial(Activity activity) { mainHandler.post(() -> { if (interstitialAd == null) { Toast.makeText(activity, "Interstitial not ready yet", Toast.LENGTH_SHORT).show(); loadInterstitial(activity); return; } interstitialAd.setFullScreenContentCallback(new FullScreenContentCallback() { @Override public void onAdDismissedFullScreenContent() { interstitialAd = null; loadInterstitial(activity); } @Override public void onAdFailedToShowFullScreenContent(AdError adError) { interstitialAd = null; loadInterstitial(activity); } @Override public void onAdShowedFullScreenContent() { Log.d(TAG, "Interstitial shown"); } }); interstitialAd.show(activity); }); } public static void loadRewarded(Activity activity) { mainHandler.post(() -> RewardedAd.load( activity, TEST_REWARDED, new AdRequest.Builder().build(), new RewardedAdLoadCallback() { @Override public void onAdLoaded(RewardedAd ad) { rewardedAd = ad; Log.d(TAG, "Rewarded loaded"); } @Override public void onAdFailedToLoad(LoadAdError error) { rewardedAd = null; Log.e(TAG, "Rewarded failed: " + error.getMessage()); } } )); } public static void showRewarded(Activity activity) { mainHandler.post(() -> { if (rewardedAd == null) { Toast.makeText(activity, "Rewarded ad not ready yet", Toast.LENGTH_SHORT).show(); loadRewarded(activity); return; } rewardedAd.setFullScreenContentCallback(new FullScreenContentCallback() { @Override public void onAdDismissedFullScreenContent() { rewardedAd = null; loadRewarded(activity); } @Override public void onAdFailedToShowFullScreenContent(AdError adError) { rewardedAd = null; loadRewarded(activity); } }); rewardedAd.show(activity, rewardItem -> { int amount = rewardItem.getAmount(); String type = rewardItem.getType(); Toast.makeText( activity, "Reward earned: " + amount + " " + type, Toast.LENGTH_LONG ).show(); Log.d(TAG, "Reward earned: " + amount + " " + type); }); }); } public static void loadRewardedInterstitial(Activity activity) { mainHandler.post(() -> RewardedInterstitialAd.load( activity, TEST_REWARDED_INTERSTITIAL, new AdRequest.Builder().build(), new RewardedInterstitialAdLoadCallback() { @Override public void onAdLoaded(RewardedInterstitialAd ad) { rewardedInterstitialAd = ad; Log.d(TAG, "Rewarded interstitial loaded"); } @Override public void onAdFailedToLoad(LoadAdError error) { rewardedInterstitialAd = null; Log.e(TAG, "Rewarded interstitial failed: " + error.getMessage()); } } )); } public static void showRewardedInterstitial(Activity activity) { mainHandler.post(() -> { if (rewardedInterstitialAd == null) { Toast.makeText(activity, "Rewarded interstitial not ready yet", Toast.LENGTH_SHORT).show(); loadRewardedInterstitial(activity); return; } rewardedInterstitialAd.setFullScreenContentCallback(new FullScreenContentCallback() { @Override public void onAdDismissedFullScreenContent() { rewardedInterstitialAd = null; loadRewardedInterstitial(activity); } @Override public void onAdFailedToShowFullScreenContent(AdError adError) { rewardedInterstitialAd = null; loadRewardedInterstitial(activity); } }); rewardedInterstitialAd.show(activity, rewardItem -> { Toast.makeText(activity, "Rewarded interstitial reward earned", Toast.LENGTH_LONG).show(); }); }); } public static void loadAppOpen(Activity activity) { mainHandler.post(() -> AppOpenAd.load( activity, TEST_APP_OPEN, new AdRequest.Builder().build(), new AppOpenAd.AppOpenAdLoadCallback() { @Override public void onAdLoaded(AppOpenAd ad) { appOpenAd = ad; Log.d(TAG, "App open loaded"); } @Override public void onAdFailedToLoad(LoadAdError error) { appOpenAd = null; Log.e(TAG, "App open failed: " + error.getMessage()); } } )); } public static void showAppOpen(Activity activity) { mainHandler.post(() -> { if (appOpenAd == null) { Toast.makeText(activity, "App open ad not ready yet", Toast.LENGTH_SHORT).show(); loadAppOpen(activity); return; } appOpenAd.setFullScreenContentCallback(new FullScreenContentCallback() { @Override public void onAdDismissedFullScreenContent() { appOpenAd = null; loadAppOpen(activity); } @Override public void onAdFailedToShowFullScreenContent(AdError adError) { appOpenAd = null; loadAppOpen(activity); } }); appOpenAd.show(activity); }); } public static void showNative(Activity activity, int x, int y, int width, int height) { mainHandler.post(() -> { FrameLayout root = activity.findViewById(android.R.id.content); if (root == null) { Log.e(TAG, "Root view not found"); return; } if (nativeContainer != null) { root.removeView(nativeContainer); nativeContainer = null; } if (nativeAd != null) { nativeAd.destroy(); nativeAd = null; } nativeContainer = new FrameLayout(activity); FrameLayout.LayoutParams containerParams = new FrameLayout.LayoutParams(width, height); containerParams.gravity = Gravity.TOP | Gravity.LEFT; containerParams.leftMargin = x; containerParams.topMargin = y; root.addView(nativeContainer, containerParams); AdLoader adLoader = new AdLoader.Builder(activity, TEST_NATIVE) .forNativeAd(ad -> { nativeAd = ad; NativeAdView adView = createNativeAdView(activity, ad); nativeContainer.removeAllViews(); nativeContainer.addView( adView, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) ); }) .withAdListener(new AdListener() { @Override public void onAdFailedToLoad(LoadAdError adError) { Log.e(TAG, "Native failed: " + adError.getMessage()); } }) .build(); adLoader.loadAd(new AdRequest.Builder().build()); }); } private static NativeAdView createNativeAdView(Activity activity, NativeAd ad) { NativeAdView adView = new NativeAdView(activity); LinearLayout layout = new LinearLayout(activity); layout.setOrientation(LinearLayout.VERTICAL); layout.setPadding(18, 18, 18, 18); layout.setBackgroundColor(Color.rgb(245, 245, 245)); TextView adLabel = new TextView(activity); adLabel.setText("Ad"); adLabel.setTextColor(Color.WHITE); adLabel.setBackgroundColor(Color.rgb(255, 170, 0)); adLabel.setPadding(8, 4, 8, 4); TextView headline = new TextView(activity); headline.setTextSize(18); headline.setTextColor(Color.BLACK); TextView body = new TextView(activity); body.setTextSize(14); body.setTextColor(Color.DKGRAY); MediaView mediaView = new MediaView(activity); mediaView.setMinimumHeight(160); Button callToAction = new Button(activity); layout.addView(adLabel); layout.addView(headline); layout.addView(body); layout.addView(mediaView); layout.addView(callToAction); adView.addView(layout); adView.setHeadlineView(headline); adView.setBodyView(body); adView.setMediaView(mediaView); adView.setCallToActionView(callToAction); headline.setText(ad.getHeadline()); if (ad.getBody() != null) { body.setText(ad.getBody()); } else { body.setText(""); } if (ad.getCallToAction() != null) { callToAction.setText(ad.getCallToAction()); } else { callToAction.setText("Open"); } if (ad.getMediaContent() != null) { mediaView.setMediaContent(ad.getMediaContent()); } adView.setNativeAd(ad); return adView; } public static void destroyAds(Activity activity) { mainHandler.post(() -> { FrameLayout root = activity.findViewById(android.R.id.content); if (root != null && bannerView != null) { root.removeView(bannerView); } if (root != null && nativeContainer != null) { root.removeView(nativeContainer); } if (bannerView != null) { bannerView.destroy(); bannerView = null; } if (nativeAd != null) { nativeAd.destroy(); nativeAd = null; } nativeContainer = null; interstitialAd = null; rewardedAd = null; rewardedInterstitialAd = null; appOpenAd = null; }); } }

Google’s official test ad unit IDs include adaptive banner, interstitial, rewarded, rewarded interstitial, app open, native, and native video. (Google for Developers)


7. Connect C++ Qt Widgets to Java

Edit your widget.h:

cpp
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); protected: void showEvent(QShowEvent *event) override; void resizeEvent(QResizeEvent *event) override; private slots: void on_pushButton_clicked(); private: Ui::Widget *ui; void initializeAds(); void showBannerAtLabel(); void showNativeAtLabel(); void showInterstitial(); void showRewarded(); void showRewardedInterstitial(); void showAppOpen(); void destroyAds(); }; #endif

Now edit your widget.cpp:

cpp
#include "widget.h" #include "./ui_widget.h" #include <QTimer> #include <QPoint> #include <QRect> #include <QShowEvent> #include <QResizeEvent> #ifdef Q_OS_ANDROID #include <QJniObject> #include <QNativeInterface> #endif static const char *ADMOB_BRIDGE_CLASS = "com/example/qtadmob/AdMobBridge"; Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); ui->label->setText("Banner placeholder"); ui->label->setMinimumHeight(90); ui->label->setStyleSheet("background:#dddddd; color:#333333;"); initializeAds(); } Widget::~Widget() { destroyAds(); delete ui; } void Widget::showEvent(QShowEvent *event) { QWidget::showEvent(event); // Wait until Qt lays out the widget, then place Android banner over ui->label. QTimer::singleShot(500, this, [this]() { showBannerAtLabel(); }); } void Widget::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); // Reposition banner after rotation or layout changes. QTimer::singleShot(300, this, [this]() { showBannerAtLabel(); }); } void Widget::initializeAds() { #ifdef Q_OS_ANDROID QJniObject activity = QNativeInterface::QAndroidApplication::context(); if (!activity.isValid()) return; QJniObject::callStaticMethod<void>( ADMOB_BRIDGE_CLASS, "initialize", "(Landroid/app/Activity;)V", activity.object<jobject>() ); #endif } void Widget::showBannerAtLabel() { #ifdef Q_OS_ANDROID QJniObject activity = QNativeInterface::QAndroidApplication::context(); if (!activity.isValid()) return; QPoint topLeft = ui->label->mapToGlobal(QPoint(0, 0)); QRect rect(topLeft, ui->label->size()); qreal dpr = devicePixelRatioF(); int x = int(rect.x() * dpr); int y = int(rect.y() * dpr); int w = int(rect.width() * dpr); int h = int(rect.height() * dpr); QJniObject::callStaticMethod<void>( ADMOB_BRIDGE_CLASS, "showBanner", "(Landroid/app/Activity;IIII)V", activity.object<jobject>(), x, y, w, h ); #endif } void Widget::showNativeAtLabel() { #ifdef Q_OS_ANDROID QJniObject activity = QNativeInterface::QAndroidApplication::context(); if (!activity.isValid()) return; QPoint topLeft = ui->label->mapToGlobal(QPoint(0, 0)); QRect rect(topLeft, ui->label->size()); qreal dpr = devicePixelRatioF(); int x = int(rect.x() * dpr); int y = int(rect.y() * dpr); int w = int(rect.width() * dpr); int h = int(rect.height() * dpr); QJniObject::callStaticMethod<void>( ADMOB_BRIDGE_CLASS, "showNative", "(Landroid/app/Activity;IIII)V", activity.object<jobject>(), x, y, w, h ); #endif } void Widget::showInterstitial() { #ifdef Q_OS_ANDROID QJniObject activity = QNativeInterface::QAndroidApplication::context(); if (!activity.isValid()) return; QJniObject::callStaticMethod<void>( ADMOB_BRIDGE_CLASS, "showInterstitial", "(Landroid/app/Activity;)V", activity.object<jobject>() ); #endif } void Widget::showRewarded() { #ifdef Q_OS_ANDROID QJniObject activity = QNativeInterface::QAndroidApplication::context(); if (!activity.isValid()) return; QJniObject::callStaticMethod<void>( ADMOB_BRIDGE_CLASS, "showRewarded", "(Landroid/app/Activity;)V", activity.object<jobject>() ); #endif } void Widget::showRewardedInterstitial() { #ifdef Q_OS_ANDROID QJniObject activity = QNativeInterface::QAndroidApplication::context(); if (!activity.isValid()) return; QJniObject::callStaticMethod<void>( ADMOB_BRIDGE_CLASS, "showRewardedInterstitial", "(Landroid/app/Activity;)V", activity.object<jobject>() ); #endif } void Widget::showAppOpen() { #ifdef Q_OS_ANDROID QJniObject activity = QNativeInterface::QAndroidApplication::context(); if (!activity.isValid()) return; QJniObject::callStaticMethod<void>( ADMOB_BRIDGE_CLASS, "showAppOpen", "(Landroid/app/Activity;)V", activity.object<jobject>() ); #endif } void Widget::destroyAds() { #ifdef Q_OS_ANDROID QJniObject activity = QNativeInterface::QAndroidApplication::context(); if (!activity.isValid()) return; QJniObject::callStaticMethod<void>( ADMOB_BRIDGE_CLASS, "destroyAds", "(Landroid/app/Activity;)V", activity.object<jobject>() ); #endif } void Widget::on_pushButton_clicked() { // here to show reward video showRewarded(); }

Qt provides QJniObject for calling Java from Qt, and QNativeInterface::QAndroidApplication::context() can return the current Android Activity context. (Qt Documentation)


8. How to test each ad type

Banner

Your banner shows automatically at ui->label after the widget is shown:

cpp
QTimer::singleShot(500, this, [this]() { showBannerAtLabel(); });

Google describes banners as rectangular ads that occupy part of the layout, often anchored at the top or bottom. (Google for Developers)

Rewarded video

Your existing button slot is correct:

cpp
void Widget::on_pushButton_clicked() { showRewarded(); }

Rewarded ads let you give in-app rewards after the user interacts with a video, playable ad, or survey. (Google for Developers)

Interstitial

Add another button in Qt Designer and connect it:

cpp
void Widget::on_pushButtonInterstitial_clicked() { showInterstitial(); }

Interstitial ads are full-screen ads best shown at natural breaks, not randomly during interaction. (Google for Developers)

Rewarded interstitial

Add a button:

cpp
void Widget::on_pushButtonRewardedInterstitial_clicked() { showRewardedInterstitial(); }

For production, show your own intro screen first: “Watch this ad to get 10 coins” with a Skip option. Google says rewarded interstitials require an intro screen with clear reward messaging and a skip option. (Google for Developers)

App open

For testing, add a button:

cpp
void Widget::on_pushButtonAppOpen_clicked() { showAppOpen(); }

For real apps, use it only when the app opens or returns to foreground. App open ads are designed for app load screens. (Google for Developers)

Native

For testing, you can reuse the same label area:

cpp
void Widget::on_pushButtonNative_clicked() { showNativeAtLabel(); }

For a real app, use a larger placeholder area than a banner. Native ads need room for headline, body, media, call-to-action, and attribution. Google says native ads provide assets to your app, and your app is responsible for displaying them. (Google for Developers)


9. Test ad IDs you should use first

Use these while developing:

FormatTest ad unit ID
App openca-app-pub-3940256099942544/9257395921
Adaptive bannerca-app-pub-3940256099942544/9214589741
Fixed bannerca-app-pub-3940256099942544/6300978111
Interstitialca-app-pub-3940256099942544/1033173712
Rewardedca-app-pub-3940256099942544/5224354917
Rewarded interstitialca-app-pub-3940256099942544/5354046379
Nativeca-app-pub-3940256099942544/2247696110
Native videoca-app-pub-3940256099942544/1044960115

These are Google’s official demo ad unit IDs. (Google for Developers)


10. How to show real ads and earn money

Do not use real ads while debugging. First make everything work with test IDs.

When ready:

  1. Create an AdMob account.
  2. Add your Android app in AdMob.
  3. Create real ad units: banner, interstitial, rewarded, etc.
  4. Replace the test App ID in AndroidManifest.xml:
xml
android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy"
  1. Replace every test ad unit ID in AdMobBridge.java with your real ad unit IDs.
  2. Publish the app to Google Play or another supported store.
  3. Link the store listing in AdMob.
  4. Set up app-ads.txt if required.
  5. Wait for app readiness review.
  6. Do not click your own ads.

AdMob says new apps must pass app readiness review before they can fully serve ads. (Google Help) AdMob also says apps will not be able to fully serve ads until they are verified with app-ads.txt and approved after app readiness review when verification is required. (Google Help) New AdMob accounts may take up to 24 hours for approval, and new apps/ad units typically take up to an hour to start showing ads. (Google Help)

You should also implement consent. Google’s UMP SDK is used to manage privacy choices, and Google says you should request consent info at every app launch and check canRequestAds() before requesting ads. (Google for Developers)


11. Important AdMob policy rules

Follow these strictly:

  • Never click your own real ads.
  • Do not ask users to click ads.
  • Do not place banners too close to buttons.
  • Do not show interstitials unexpectedly while users are typing or clicking.
  • Do not refresh ads manually too aggressively.
  • Use test ads during development.
  • Use rewarded ads only when the user knowingly chooses the reward.
  • For rewarded interstitial, show an intro/skip screen first.

AdMob estimated earnings are adjusted at the end of the month for invalid clicks or impressions, and revenue from invalid activity can be deducted. (Google Help)


12. How much money per 1000 users?

There is no fixed “price per 1000 users.” AdMob pays based on impressions, geography, ad format, advertiser demand, match rate, click-through rate, and eCPM. Google defines eCPM as:

text
eCPM = (Total earnings / Impressions) × 1000

(Google Help)

Google also says revenue depends on metrics such as impressions, eCPM, impression CTR, match rate, and show rate. (Google Help)

Use this formula:

text
Revenue = (Impressions / 1000) × eCPM

Example with 1000 daily users:

ScenarioAds per user/dayImpressionsExample eCPMEstimated revenue/day
Light banner only22,000$0.20$0.40
Banner + interstitial44,000$1.00$4.00
Rewarded-heavy app22,000$5.00$10.00
Strong US/game traffic55,000$8.00$40.00

These are examples, not guaranteed prices. Real eCPM can be much lower or higher. Rewarded and interstitial ads often earn more per impression than banners, but they must be used carefully or users will uninstall the app.


13. Which ad format should you use?

For a simple Qt Widgets test app:

  1. Banner: show at bottom/top using ui->label.
  2. Rewarded: show from your pushButton.
  3. Interstitial: show only after completing a task or screen.
  4. Native: use only if you can design a proper native ad card.
  5. App open: use only on app launch/return, not during normal use.
  6. Rewarded interstitial: use carefully, with intro + skip.

For your current code, the direct version is:

cpp
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); ui->label->setText("Banner placeholder"); ui->label->setMinimumHeight(90); initializeAds(); QTimer::singleShot(500, this, [this]() { showBannerAtLabel(); }); } void Widget::on_pushButton_clicked() { showRewarded(); }

That is the cleanest Qt Widgets pattern for testing AdMob on Android.

Frage

W/default : java.lang.ClassNotFoundException: Didn't find class "com.example.admob.AdMobBridge" on path: DexPathList[[zip file "/data/app/~~PADbnrlpzEgq922cGau3Xw==/org.qtproject.example.Admob-ptcM-xzz741jyTk_Uoo1Yg==/base.apk"],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64]]
W/default : at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:218)
W/default : at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
W/default : at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
W/default : at org.qtproject.qt.android.QtNative.startQtNativeApplication(Native Method)
W/default : at org.qtproject.qt.android.QtNative.lambdastartApplicationstartApplication2(QtNative.java:366)
W/default : at org.qtproject.qt.android.QtNativeExternalSyntheticLambda3.run(D8ExternalSyntheticLambda3.run(D8SyntheticClass:0)
W/default : at org.qtproject.qt.android.QtThread1.run(QtThread.java:25)W/default:atjava.lang.Thread.run(Thread.java:920)W/default:W/default:java.lang.ClassNotFoundException:Didntfindclass"com.example.admob.AdMobBridge"onpath:DexPathList[[zipfile"/data/app/  PADbnrlpzEgq922cGau3Xw==/org.qtproject.example.AdmobptcMxzz741jyTkUoo1Yg==/base.apk"],nativeLibraryDirectories=[/system/lib64,/systemext/lib64]]W/default:atdalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:218)W/default:atjava.lang.ClassLoader.loadClass(ClassLoader.java:379)W/default:atjava.lang.ClassLoader.loadClass(ClassLoader.java:312)W/default:atorg.qtproject.qt.android.QtNative.startQtNativeApplication(NativeMethod)W/default:atorg.qtproject.qt.android.QtNative.lambda1.run(QtThread.java:25) W/default : at java.lang.Thread.run(Thread.java:920) W/default : W/default : java.lang.ClassNotFoundException: Didn't find class "com.example.admob.AdMobBridge" on path: DexPathList[[zip file "/data/app/~~PADbnrlpzEgq922cGau3Xw==/org.qtproject.example.Admob-ptcM-xzz741jyTk_Uoo1Yg==/base.apk"],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64]] W/default : at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:218) W/default : at java.lang.ClassLoader.loadClass(ClassLoader.java:379) W/default : at java.lang.ClassLoader.loadClass(ClassLoader.java:312) W/default : at org.qtproject.qt.android.QtNative.startQtNativeApplication(Native Method) W/default : at org.qtproject.qt.android.QtNative.lambdastartApplication2(QtNative.java:366) W/default : at org.qtproject.qt.android.QtNative$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0) W/default : at org.qtproject.qt.android.QtThread1.run(QtThread.java:25)
W/default : at java.lang.Thread.run(Thread.java:920)

Gedanke

undefined

Gedanke

Thought for 8s

Antwort

This error is exactly here:

text
ClassNotFoundException: Didn't find class "com.example.admob.AdMobBridge"

Your C++ code is calling:

cpp
"com/example/admob/AdMobBridge"

But your Java file is probably in a different package, for example from my previous example:

java
package com.example.qtadmob;

So Android cannot find:

text
com.example.admob.AdMobBridge

because the real class is probably:

text
com.example.qtadmob.AdMobBridge

Qt only packages Java/Kotlin files correctly when they are under <QT_ANDROID_PACKAGE_SOURCE_DIR>/src/. Qt documents that custom Java/Kotlin code should be placed under that src directory, and QT_ANDROID_PACKAGE_SOURCE_DIR must point to the Android package template folder. (Qt Documentation)


Fix 1: Make package name, folder path, and C++ class name match

Choose one package name. Since your error expects this:

text
com.example.admob.AdMobBridge

use that everywhere.

Your Java file must be here:

text
your_project/ android/ src/ com/ example/ admob/ AdMobBridge.java

At the top of AdMobBridge.java, use:

java
package com.example.admob;

In C++, use:

cpp
static const char *ADMOB_BRIDGE_CLASS = "com/example/admob/AdMobBridge";

So your C++ call becomes:

cpp
QJniObject::callStaticMethod<void>( "com/example/admob/AdMobBridge", "initialize", "(Landroid/app/Activity;)V", activity.object<jobject>() );

QJniObject is the Qt class used for Java/JNI calls on Android. (Qt Documentation)


Fix 2: Check your CMakeLists.txt

Make sure your target has this:

cmake
if(ANDROID) set_property(TARGET Admob PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android" ) endif()

Your target may not be called Admob. Check this part:

cmake
qt_add_executable(Admob main.cpp widget.cpp widget.h widget.ui )

The name after qt_add_executable is the target name. Use the same name in set_property.

Example:

cmake
qt_add_executable(Admob main.cpp widget.cpp widget.h widget.ui ) target_link_libraries(Admob PRIVATE Qt6::Widgets) if(ANDROID) set_property(TARGET Admob PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android" ) endif()

Fix 3: Clean rebuild the APK

After changing Java package/folders:

  1. In Qt Creator: Build > Clean Project
  2. Delete the Android build folder if needed.
  3. Run CMake again
  4. Rebuild
  5. Uninstall the old app from the phone/emulator.
  6. Install again from Qt Creator.

This matters because old APKs/build directories can keep stale Java packaging.


Fix 4: Confirm Java is actually inside the APK

After building, you can check the APK content.

In Qt Creator build folder, find the APK, then run:

bash
unzip -l app-debug.apk | grep AdMobBridge

You should see something related to compiled dex/classes, not necessarily the .java file. If nothing related is found, your Java file is not being included.

The most common reason is this folder is wrong:

text
android/com/example/admob/AdMobBridge.java

It must be:

text
android/src/com/example/admob/AdMobBridge.java

Minimal correct files

AdMobBridge.java

Path:

text
android/src/com/example/admob/AdMobBridge.java

Top lines:

java
package com.example.admob; import android.app.Activity; import android.util.Log; public class AdMobBridge { public static void initialize(Activity activity) { Log.d("AdMobBridge", "initialize called"); } }

widget.cpp

cpp
#ifdef Q_OS_ANDROID #include <QJniObject> #include <QNativeInterface> #endif static const char *ADMOB_BRIDGE_CLASS = "com/example/admob/AdMobBridge"; void Widget::initializeAds() { #ifdef Q_OS_ANDROID QJniObject activity = QNativeInterface::QAndroidApplication::context(); if (!activity.isValid()) return; QJniObject::callStaticMethod<void>( ADMOB_BRIDGE_CLASS, "initialize", "(Landroid/app/Activity;)V", activity.object<jobject>() ); #endif }

First test only this minimal bridge. If logcat shows:

text
AdMobBridge: initialize called

then JNI is working. After that, add the full AdMob code back.

Frage

great it works i want create func named AdReward_Watched in widget.cpp so when he watch full ad, i do something like qDebug()<< ward added,

also i want control message: Reward earned: 10 coins ,after ad finished i want change this message

Gedanke

Nice — now the next part is a JNI callback in the opposite direction: Java tells C++ “reward completed,” and C++ calls your Widget::AdReward_Watched() function.

Thought for 15s

Antwort

Great. Add a Java → C++ callback so when the rewarded ad gives the reward, your Qt function runs.

Assuming your package is now:

text
com.example.admob

1. Add this to widget.h

cpp
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); void AdReward_Watched(int amount, const QString &type); private slots: void on_pushButton_clicked(); private: Ui::Widget *ui; void initializeAds(); void showRewarded(); void setRewardMessage(const QString &message); }; #endif

2. Add this to the top of widget.cpp

Add these includes:

cpp
#include "widget.h" #include "./ui_widget.h" #include <QDebug> #include <QMetaObject> #ifdef Q_OS_ANDROID #include <QJniObject> #include <QNativeInterface> #include <jni.h> #endif

Then add this global pointer near the top of widget.cpp:

cpp
static Widget *g_widgetInstance = nullptr; static const char *ADMOB_BRIDGE_CLASS = "com/example/admob/AdMobBridge";

3. Set the global widget instance

In your constructor:

cpp
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { g_widgetInstance = this; ui->setupUi(this); ui->label->setText("here banner"); initializeAds(); // Change rewarded message here. setRewardMessage("Reward added: {amount} {type}"); }

In your destructor:

cpp
Widget::~Widget() { if (g_widgetInstance == this) g_widgetInstance = nullptr; delete ui; }

4. Create your reward function in widget.cpp

cpp
void Widget::AdReward_Watched(int amount, const QString &type) { qDebug() << "reward added"; qDebug() << "Amount:" << amount; qDebug() << "Type:" << type; // Example: update UI // ui->labelCoins->setText(QString("Coins: %1").arg(amount)); // Example: add coins to your own variable/database // coins += amount; }

This function is called only when the AdMob rewarded callback says the user earned the reward.


5. Add the native JNI callback in widget.cpp

Add this outside the Widget class functions:

cpp
#ifdef Q_OS_ANDROID extern "C" JNIEXPORT void JNICALL Java_com_example_admob_AdMobBridge_nativeRewardWatched( JNIEnv *env, jclass, jint amount, jstring type ) { QString rewardType; if (type) { const char *chars = env->GetStringUTFChars(type, nullptr); rewardType = QString::fromUtf8(chars); env->ReleaseStringUTFChars(type, chars); } if (!g_widgetInstance) return; QMetaObject::invokeMethod( g_widgetInstance, [amount, rewardType]() { if (g_widgetInstance) { g_widgetInstance->AdReward_Watched(amount, rewardType); } }, Qt::QueuedConnection ); } #endif

Important: because your Java package is:

java
package com.example.admob;

the C++ JNI function must be named:

cpp
Java_com_example_admob_AdMobBridge_nativeRewardWatched

If your package is different, the function name must change too.


6. Add this function to control the reward message

In widget.cpp:

cpp
void Widget::setRewardMessage(const QString &message) { #ifdef Q_OS_ANDROID QJniObject jMessage = QJniObject::fromString(message); QJniObject::callStaticMethod<void>( ADMOB_BRIDGE_CLASS, "setRewardMessage", "(Ljava/lang/String;)V", jMessage.object<jstring>() ); #else Q_UNUSED(message); #endif }

You can now change the message from C++ like this:

cpp
setRewardMessage("Reward added: {amount} {type}");

or:

cpp
setRewardMessage("You got {amount} coins!");

or:

cpp
setRewardMessage("Thanks for watching the full ad");

7. Modify AdMobBridge.java

Inside your Java class, add this near the top:

java
private static String rewardedMessage = "Reward earned: {amount} {type}"; public static native void nativeRewardWatched(int amount, String type); public static void setRewardMessage(String message) { mainHandler.post(() -> { rewardedMessage = message; }); } private static String buildRewardMessage(int amount, String type) { return rewardedMessage .replace("{amount}", String.valueOf(amount)) .replace("{type}", type); }

So your Java class has something like:

java
public class AdMobBridge { private static final Handler mainHandler = new Handler(Looper.getMainLooper()); private static String rewardedMessage = "Reward earned: {amount} {type}"; public static native void nativeRewardWatched(int amount, String type); public static void setRewardMessage(String message) { mainHandler.post(() -> { rewardedMessage = message; }); } private static String buildRewardMessage(int amount, String type) { return rewardedMessage .replace("{amount}", String.valueOf(amount)) .replace("{type}", type); } // rest of your class... }

8. Replace the rewarded callback in Java

Find this part in your Java showRewarded function:

java
rewardedAd.show(activity, rewardItem -> { int amount = rewardItem.getAmount(); String type = rewardItem.getType(); Toast.makeText( activity, "Reward earned: " + amount + " " + type, Toast.LENGTH_LONG ).show(); Log.d(TAG, "Reward earned: " + amount + " " + type); });

Replace it with this:

java
rewardedAd.show(activity, rewardItem -> { int amount = rewardItem.getAmount(); String type = rewardItem.getType(); nativeRewardWatched(amount, type); Toast.makeText( activity, buildRewardMessage(amount, type), Toast.LENGTH_LONG ).show(); Log.d(TAG, "Reward earned: " + amount + " " + type); });

9. Your button stays simple

Your Qt button remains:

cpp
void Widget::on_pushButton_clicked() { showRewarded(); }

Now the full flow is:

text
Qt button clicked → Java showRewarded() → user watches rewarded ad → AdMob gives reward → Java calls nativeRewardWatched(amount, type) → C++ calls Widget::AdReward_Watched(amount, type) → qDebug() << "reward added"

10. Example final behavior

If you set:

cpp
setRewardMessage("You got {amount} coins!");

and the reward amount is 10, the Android toast becomes:

text
You got 10 coins!

And Qt debug output becomes:

text
reward added Amount: 10 Type: coins

Diesen Q&A teilen