googleads / googleads-mobile-flutter

A Flutter plugin for the Google Mobile Ads SDK
Apache License 2.0
336 stars 276 forks source link

[App Open Ad] `AdError code 3, message the ad can not be shown when app is not in foreground` in production #906

Closed peterweb2005 closed 11 months ago

peterweb2005 commented 1 year ago

AppLifecycleState https://api.flutter.dev/flutter/dart-ui/AppLifecycleState.html AppStateEventNotifier https://pub.dev/documentation/google_mobile_ads/latest/google_mobile_ads/AppStateEventNotifier-class.html

otherwise may throw AdError code 3, message the ad can not be shown when app is not in foreground

but AppStateEventNotifier events are very not active (rare) compared with AppLifecycleState events?

thanks

updated provide message for the ad error code 3

Interstitial

Interstitials that show when your app is in the background are a violation of AdMob policies and may lead to blocked ad serving. To learn more, visit https://googlemobileadssdk.page.link/admob-interstitial-policies

App Open

The ad can not be shown when app is not in foreground.

huycozy commented 1 year ago

Hi @peterweb2005 Can you please share the detailed steps to reproduce along with a minimal sample code to replicate for Interstitial and App Open cases? (You can use sample code from this plugin and modify it if needed on your forked project and then share it here).

Please provide the entire error stack trace as well. Thank you!

peterweb2005 commented 1 year ago

this only means that if I mismatched event for fullscreen ads, eg AppLifecycleState event for App Open Ads,, but not AppStateEventNotifier.appStateStream events that would easily lead fullscreen ad not showing in foreground error,

and moreover, AppStateEventNotifier.appStateStream still inaccurate for App Open Ads, which I opened another issue

Edited problem is why AppStateEventNotifier.appStateStream? in other posts, I found that, the event may be dirty,, not sync between native and flutter engine

Edited also when switching between pause & resume, may produce event propagate delay, is it only happens in dev mode?

huycozy commented 1 year ago

Hi @peterweb2005 I would suggest you provide clear Steps to reproduce and a minimal sample code along with it so that we can verify the issue properly.

peterweb2005 commented 1 year ago

just your sample, app open ad, and crash report the show error, it happens often

thanks

huycozy commented 1 year ago

Can you share the steps to reproduce the issue? I check this with app_open_example but the ad can display well:

Demo https://github.com/googleads/googleads-mobile-flutter/assets/104349824/0c464ff2-01c0-44e4-8841-966d168ea157
peterweb2005 commented 1 year ago

yes

but if you had a published app, and use the sample code for app open ads, and crash log the show error,

      onAdFailedToShowFullScreenContent: (ad, error) {

the crashlytics would tell

for example, it had about 26 events of "AdError code 3, message the ad can not be shown when app is not in foreground", in just few days

huycozy commented 1 year ago

Thanks for your response. It looks like _appOpenAd!.show() or _interstitialAd!.show() is called when app is in the background.

This could be related to your implementation, please see https://github.com/googleads/googleads-mobile-android-examples/issues/145 and please confirm you also didn't trigger ad to show from background. Btw, can you share places where you are showing ad in your app?

peterweb2005 commented 1 year ago

yes, please, as i mentioned many times, all logics are from the example:

  1. on app init event, register AppLifecycleReactor

    @override
    void initState() {
    super.initState();
    AppOpenAdManager appOpenAdManager = AppOpenAdManager()..loadAd();
    _appLifecycleReactor =
        AppLifecycleReactor(appOpenAdManager: appOpenAdManager);
    _appLifecycleReactor.listenToAppStateChanges();
    }
  2. on AppLifecycleReactor foreground event, call showAdIfAvailable()

    void listenToAppStateChanges() {
    AppStateEventNotifier.startListening();
    AppStateEventNotifier.appStateStream
        .forEach((state) => _onAppStateChanged(state));
    }
    
    void _onAppStateChanged(AppState appState) {
    print('New AppState state: $appState');
    if (appState == AppState.foreground) {
      appOpenAdManager.showAdIfAvailable();
    }
    }
huycozy commented 1 year ago

Thanks for your response. Looks like it's only reproducible in production, I will label the issue for the investigation.

I will mark this issue as App Open Ad issue. For the interstitial ad, I'm not sure how your implementation looks like, please file a new issue for it. Thank you!

peterweb2005 commented 1 year ago

all I can tell is that it is not only in production, but I don't want to continue

peterweb2005 commented 1 year ago

summary, 2 problems much decline the revenue:

  1. AppStateEventNotifier foreground event fewer happens than AppLifecycleState resume event
  2. even if have AppStateEventNotifier foreground event, show() call would throw error "AdError code 3, message the ad can not be shown when app is not in foreground"
wujieliulan commented 1 year ago

I ran into exactly the same issue (or bug). If I sleep for 80ms before calling showAdIfAvailable, it always works. I found around 20ms is the threshold on my phone (SM-G937U1). Here is my "workaround": public void onStart(@NonNull LifecycleOwner owner) { DefaultLifecycleObserver.super.onStart(owner); // Show the ad (if available) when the app moves to foreground. try { Thread.sleep(80); } catch (Exception e) { } appOpenAdManager.showAdIfAvailable(currentActivity); }

peterweb2005 commented 1 year ago

@wujieliulan thanks

I currently trying visibility detector, which the plugin depends, there has a important updateIntevral variable is that because?

wujieliulan commented 1 year ago

May I ask how you detect visibility?

peterweb2005 commented 1 year ago

May I ask how you detect visibility?

EDIT sorry, i know your question, but i used not in the case, just trying to find cause

LTPhantom commented 11 months ago

Hello, I would like to get a more detailed explanation on how to reproduce this problem since my attempts to get the same error message were unsuccessful.

If I understand correctly, this happens even in the app_open_example project on both production and debug versions. Also, I understand that it happens sometimes but what I don't understand is the exact steps you follow to get the error.

What I did:

  1. Open app open example app.
  2. Minimize it and quickly bring it back to the foreground (I sometimes did fast enough that it didn't trigger the ad).
  3. Also tried doing it at a normal pace but I never got the error.

My result was that, when it detected going to background and then foreground, it always showed the open ad with no error message at all. I tried to reproduce on a physical device as well as on Android emulators with similar results.

peterweb2005 commented 11 months ago

it is hardly to reproduce the error when just 1 user, or developer But if one has a production project, and just copy the example, full stuffs (on lifecycle events, register, showIfAvailible()), maybe the app has hundred active users, the crash log count can be 1/10 of user count, in 1 week

don't forget, add the line to report error inside onAdFailedToShowFullScreenContent, if error code is 3

Thanks

EDIT highlighted text

LTPhantom commented 11 months ago

Thanks @peterweb2005 . I found out that locking and unlocking the phone while the app is in the foreground is the most reliable way to reproduce this error message. I also found that the issue comes from the native Android format (don't know about iOS) and have triaged to look into the problem.

wujieliulan commented 11 months ago

Actually Google had a new release on last week, they seemed to fix it, it doesn't happen to me, which is good, I don't need the work around anymore. The new version is com.google.android.gms:play-services-ads:22.4.0

malandr2 commented 11 months ago

@wujieliulan Thanks for that.

@peterweb2005 can you confirm that upgrading the Android SDK to com.google.android.gms:play-services-ads:22.4.0 resolves your issue?

peterweb2005 commented 11 months ago

@malandr2

how? only set the dependencies in android/app/build.gradle?

EDIT i think that LTPhantom is the best choice for the task, as i dont know exactly how to reproduce

malandr2 commented 11 months ago

@peterweb2005 For the sake of experiment, if the api of the Android GMA SDK is set to com.google.android.gms:play-services-ads:22.0.0 please try changing to com.google.android.gms:play-services-ads:22.4.0. This will also require a kotlin version change.

In doing so, are you still able to reproduce the issue?

peterweb2005 commented 11 months ago

EDIT

publishing after comfired the versions 22.4.0

malandr2 commented 11 months ago

Got it, thanks.

peterweb2005 commented 11 months ago

seems still happen, but dont know if the rate lower

peterweb2005 commented 11 months ago

isnt it obvious?

when showing interstitial, after wait on resumed, no error code 3

why when showing app opened ad, after on foreground, but not wait on resumed? if it do so, there should be no error code 3?

peterweb2005 commented 11 months ago

is it ok?? check both app state AppLifecycleState.resumed & AppStateEventNotifier's AppState.foreground

  static const interval = Duration(milliseconds: 50);
  static const maxInterval = Duration(milliseconds: 250);
  StreamSubscription<int>? appResumedSubscription;

  pause() {
    log.finer('pause()');
    appResumedSubscription?.cancel();
    appResumedSubscription = null;
  }

  resume({required AdAppState adAppState}) async {
    log.finer('resume()');
    log.finest('userAccepted: ', userAccepted);
    if (userAccepted) {
      log.finest('adAppState: ', adAppState);
      switch (adAppState) {
        case AdAppState.app:
          // page ad..
        case AdAppState.admob:
          // if page ad end
          if (ref.read(adProvider.notifier).pageAdEnded == true) {
            log.finest('isAppResumed: ', isAppResumed);
            if (isAppResumed) {
              appOpenAdHandler.resume();
            } else {
              appResumedSubscription ??= RxUtil.interval(
                initialDelay: interval,
                period: interval,
              ).listen(
                (int i) {
                  log.finer('onData()');
                  //log.finest('i: ', i);
                  final time = interval * (i + 1);
                  log.finest('time: ', time);
                  if (isAppResumed || time >= maxInterval) {
                    //
                    if (foreground) {
                      log.finest('call resume, time: ', time);
                      debugShow('call resume, time: $time');
                      appOpenAdHandler.resume();
                    }

                    appResumedSubscription?.cancel();
                    appResumedSubscription = null;
                  }
                },
                onError: (e) {
                  throw e;
                },
                //cancelOnError: true,
              );
            }
          }
        default:
      }
    }
  }
peterweb2005 commented 11 months ago

WARNING

the above approach is good, much lower error rate, but google can ban account

it is just admob foreground event (which happen earlier) wait for flutter resumed event, delay for 0.1 to 0.15 second

malandr2 commented 11 months ago

Following up on this, the issue was fixed in https://github.com/googleads/googleads-mobile-flutter/pull/843, which updated androidx.lifecycle:lifecycle-process dependency from 2.2.0 to the latest stable version of 2.6.2. This fix will be out in the next release.

Closing the issue.

peterweb2005 commented 9 months ago

Following up on this, the issue was fixed in #843, which updated androidx.lifecycle:lifecycle-process dependency from 2.2.0 to the latest stable version of 2.6.2. This fix will be out in the next release.

Closing the issue.

nothing changed

peterweb2005 commented 9 months ago

@malandr2 stat update, error rating updated from 5.8% to 9.5%

malandr2 commented 6 months ago

See https://github.com/googleads/googleads-mobile-flutter/issues/1029#issuecomment-2008350027 for any insight, the fix applied there may be applicable for others.

peterweb2005 commented 6 months ago

See #1029 (comment) for any insight, the fix applied there may be applicable for others.

NO that 2 flags, thanks

EDIT

Rewarded Ad, App Open Ad, Interstitial Ad, diff cases