invertase / react-native-firebase

🔥 A well-tested feature-rich modular Firebase implementation for React Native. Supports both iOS & Android platforms for all Firebase services.
https://rnfirebase.io
Other
11.68k stars 2.21k forks source link

Admob <Banner /> onAdLoaded called multiple times #3299

Closed Joaojuby closed 4 years ago

Joaojuby commented 4 years ago

:fire: Issue

The Banner component is triggering the onAdLoaded event multiple times. Not knowing if this is also making subsequent ad requests it becomes a worrying implementation concern. Issue happens at least in Android, can't say for iOS.

Further Info: I thought it could be because of parent components so I lifted the component up the tree and it still happens even in isolation

Here's some sample code to reproduce

import React from 'react';
import { BannerAd, BannerAdSize, TestIds } from '@react-native-firebase/admob';

class FrameWithBannerAd extends React.Component {
  componentDidMount() {
    console.log('FrameWithBannerAd did mount');
  }

  handleOnAdLoaded = (...args) => {
    console.log('ad loaded!');
    console.log(args);
    this.setState({ adLoaded: true });
  };

  render() {
    console.log('RENDER!');

    return (
      <BannerAd
        unitId={TestIds.BANNER}
        size={BannerAdSize.BANNER}
        requestOptions={{
          requestNonPersonalizedAdsOnly: true,
        }}
        onAdLoaded={this.handleOnAdLoaded}
      />
    );
  }
}

export default FrameWithBannerAd;

Project Files

Javascript

Click To Expand

#### `package.json`: ```json { "private": true, "scripts": { "android": "react-native run-android", "ios": "react-native run-ios", "start": "react-native start", "test": "jest", "lint": "eslint .", "storybook": "start-storybook -p 7007" }, "dependencies": { "@react-native-community/art": "^1.0.3", "@react-native-community/async-storage": "^1.6.3", "@react-native-community/datetimepicker": "^2.1.0", "@react-native-community/masked-view": "^0.1.6", "@react-native-firebase/admob": "^6.0.3", "@react-native-firebase/analytics": "^6.0.3", "@react-native-firebase/app": "^6.0.3", "@react-native-firebase/remote-config": "^6.0.3", "@react-navigation/bottom-tabs": "^5.0.5", "@react-navigation/compat": "^5.0.5", "@react-navigation/material-top-tabs": "^5.0.5", "@react-navigation/native": "^5.0.3", "@react-navigation/stack": "^5.0.5", "crypto-js": "^3.1.9-1", "fbjs": "^1.0.0", "giphy-js-sdk-core": "^1.0.6", "i18n-js": "^3.5.0", "isomorphic-ws": "^4.0.1", "lodash": "^4.17.15", "lottie-ios": "3.1.3", "lottie-react-native": "^3.2.1", "moment": "^2.24.0", "moment-timezone": "^0.5.27", "native-base": "^2.13.8", "parse-domain": "^2.3.4", "path-parser": "^4.2.0", "prop-types": "^15.7.2", "react": "16.9.0", "react-native": "0.61.3", "react-native-animatable": "^1.3.3", "react-native-billing": "^3.0.0", "react-native-branch": "^4.2.1", "react-native-circular-progress": "^1.3.4", "react-native-code-push": "^5.7.0", "react-native-device-info": "^5.2.1", "react-native-easy-grid": "^0.2.2", "react-native-easy-toast": "^1.2.0", "react-native-fast-image": "^7.0.2", "react-native-fbsdk": "^1.0.4", "react-native-fs": "^2.16.1", "react-native-gesture-handler": "^1.6.0", "react-native-gifted-chat": "^0.11.3", "react-native-hyperlink": "^0.0.16", "react-native-image-picker": "^1.1.0", "react-native-image-resizer": "^1.1.0", "react-native-in-app-utils": "^6.0.2", "react-native-linear-gradient": "^2.5.6", "react-native-localize": "^1.3.1", "react-native-onesignal": "^3.5.0", "react-native-parsed-text": "^0.0.21", "react-native-photo-upload": "^1.3.0", "react-native-progress": "^4.0.1", "react-native-rate": "^1.1.10", "react-native-reanimated": "^1.7.0", "react-native-responsive-dimensions": "^3.0.0", "react-native-safe-area-context": "^0.7.3", "react-native-screens": "^2.0.0-beta.4", "react-native-section-list-get-item-layout": "^2.2.3", "react-native-splash-screen": "^3.2.0", "react-native-storage": "^1.0.1", "react-native-svg": "^9.13.3", "react-native-svg-charts": "^5.3.0", "react-native-swiper": "^1.6.0-nightly.5", "react-native-switch": "^1.5.0", "react-native-tab-view": "^2.13.0", "react-native-toaster": "^1.2.2", "react-native-view-shot": "^3.0.2", "react-native-webview": "^7.5.1", "react-redux": "^7.1.3", "redux": "^4.0.4", "redux-form": "^8.2.6", "redux-logger": "^3.0.6", "redux-persist": "^6.0.0", "redux-thunk": "^2.3.0", "reselect": "^4.0.0", "simpleddp": "^2.2.4", "simpleddp-plugin-login": "^4.0.2", "ws": "^7.2.0" }, "devDependencies": { "@babel/core": "7.6.4", "@babel/runtime": "7.6.3", "@react-native-community/eslint-config": "^0.0.5", "@storybook/addon-actions": "^5.2.6", "@storybook/addon-links": "^5.2.6", "@storybook/addons": "^5.2.6", "@storybook/react-native": "^5.2.6", "@storybook/react-native-server": "^5.2.6", "babel-jest": "24.9.0", "babel-loader": "^8.0.6", "eslint": "6.6.0", "eslint-config-prettier": "^6.5.0", "eslint-plugin-prettier": "^3.1.1", "jest": "24.9.0", "metro-react-native-babel-preset": "0.51.1", "prettier": "1.18.2", "react-dom": "16.9.0", "react-test-renderer": "16.9.0", "redux-devtools": "^3.5.0" }, "jest": { "preset": "react-native" }, "resolutions": { "metro-config": "0.57.0" } } ``` #### `firebase.json` for react-native-firebase v6: ```json { "react-native": { "admob_android_app_id": "ca-app-pub-XXXXXXXXXXXXXXXXX", "admob_ios_app_id": "ca-app-pub-XXXXXXXXXXXXXXXXX" } } ```

iOS

Click To Expand

#### `ios/Podfile`: - [x] I'm not using Pods - [] I'm using Pods and my Podfile looks like: ```ruby # N/A ``` #### `AppDelegate.m`: ```objc // N/A ```


Android

Click To Expand

#### Have you converted to AndroidX? - [ ] my application is an AndroidX application? - [x] I am using `android/gradle.settings` `jetifier=true` for Android compatibility? - [ ] I am using the NPM package `jetifier` for react-native compatibility? #### `android/build.gradle`: ```groovy // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext { buildToolsVersion = "28.0.3" minSdkVersion = 16 compileSdkVersion = 28 targetSdkVersion = 28 } repositories { google() jcenter() } dependencies { classpath("com.android.tools.build:gradle:3.4.2") // react-native-firebase required modifications classpath 'com.google.gms:google-services:4.2.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { mavenLocal() maven { // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm url("$rootDir/../node_modules/react-native/android") } maven { // Android JSC is installed from npm url("$rootDir/../node_modules/jsc-android/dist") } google() jcenter() maven { url 'https://jitpack.io' } } } ``` #### `android/app/build.gradle`: ```groovy // react-native-onesignal required modifications buildscript { repositories { maven { url 'https://plugins.gradle.org/m2/' } // Gradle Plugin Portal } dependencies { classpath 'gradle.plugin.com.onesignal:onesignal-gradle-plugin:[0.12.1, 0.99.99]' } } apply plugin: 'com.onesignal.androidsdk.onesignal-gradle-plugin' // end of react-native-onesignal required modifications apply plugin: "com.android.application" import com.android.build.OutputFile /** * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets * and bundleReleaseJsAndAssets). * These basically call `react-native bundle` with the correct arguments during the Android build * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the * bundle directly from the development server. Below you can see all the possible configurations * and their defaults. If you decide to add a configuration block, make sure to add it before the * `apply from: "../../node_modules/react-native/react.gradle"` line. * * project.ext.react = [ * // the name of the generated asset file containing your JS bundle * bundleAssetName: "index.android.bundle", * * // the entry file for bundle generation * entryFile: "index.android.js", * * // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format * bundleCommand: "ram-bundle", * * // whether to bundle JS and assets in debug mode * bundleInDebug: false, * * // whether to bundle JS and assets in release mode * bundleInRelease: true, * * // whether to bundle JS and assets in another build variant (if configured). * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants * // The configuration property can be in the following formats * // 'bundleIn${productFlavor}${buildType}' * // 'bundleIn${buildType}' * // bundleInFreeDebug: true, * // bundleInPaidRelease: true, * // bundleInBeta: true, * * // whether to disable dev mode in custom build variants (by default only disabled in release) * // for example: to disable dev mode in the staging build type (if configured) * devDisabledInStaging: true, * // The configuration property can be in the following formats * // 'devDisabledIn${productFlavor}${buildType}' * // 'devDisabledIn${buildType}' * * // the root of your project, i.e. where "package.json" lives * root: "../../", * * // where to put the JS bundle asset in debug mode * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", * * // where to put the JS bundle asset in release mode * jsBundleDirRelease: "$buildDir/intermediates/assets/release", * * // where to put drawable resources / React Native assets, e.g. the ones you use via * // require('./image.png')), in debug mode * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", * * // where to put drawable resources / React Native assets, e.g. the ones you use via * // require('./image.png')), in release mode * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", * * // by default the gradle tasks are skipped if none of the JS files or assets change; this means * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to * // date; if you have any other folders that you want to ignore for performance reasons (gradle * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ * // for example, you might want to remove it from here. * inputExcludes: ["android/**", "ios/**"], * * // override which node gets called and with what additional arguments * nodeExecutableAndArgs: ["node"], * * // supply additional arguments to the packager * extraPackagerArgs: [] * ] */ project.ext.react = [ entryFile: "index.js", enableHermes: false, // clean and rebuild if changing ] apply from: "../../node_modules/react-native/react.gradle" // react-native-code-push required modifications apply from: "../../node_modules/react-native-code-push/android/codepush.gradle" /** * Set this to true to create two separate APKs instead of one: * - An APK that only works on ARM devices * - An APK that only works on x86 devices * The advantage is the size of the APK is reduced by about 4MB. * Upload all the APKs to the Play Store and people will download * the correct one based on the CPU architecture of their device. */ def enableSeparateBuildPerCPUArchitecture = false /** * Run Proguard to shrink the Java bytecode in release builds. */ def enableProguardInReleaseBuilds = false /** * The preferred build flavor of JavaScriptCore. * * For example, to use the international variant, you can use: * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` * * The international variant includes ICU i18n library and necessary data * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that * give correct results when using with locales other than en-US. Note that * this variant is about 6MiB larger per architecture than default. */ def jscFlavor = 'org.webkit:android-jsc:+' /** * Whether to enable the Hermes VM. * * This should be set on project.ext.react and mirrored here. If it is not set * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode * and the benefits of using Hermes will therefore be sharply reduced. */ def enableHermes = project.ext.react.get("enableHermes", false); android { compileSdkVersion rootProject.ext.compileSdkVersion compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } defaultConfig { applicationId "XXXXXXXXXXXXX" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion // be extremely careful with the version code number // try to increment only by 1 every new store build versionCode XXXXX versionName "XXXXX" // multidex bug required us to add this multiDexEnabled true } splits { abi { reset() enable enableSeparateBuildPerCPUArchitecture universalApk false // If true, also generate a universal APK include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" } } signingConfigs { debug { storeFile file('debug.keystore') storePassword 'android' keyAlias 'androiddebugkey' keyPassword 'android' } release { if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) { storeFile file(MYAPP_RELEASE_STORE_FILE) storePassword MYAPP_RELEASE_STORE_PASSWORD keyAlias MYAPP_RELEASE_KEY_ALIAS keyPassword MYAPP_RELEASE_KEY_PASSWORD } } } buildTypes { debug { signingConfig signingConfigs.debug } release { // Caution! In production, you need to generate your own keystore file. // see https://facebook.github.io/react-native/docs/signed-apk-android. // signingConfig signingConfigs.debug signingConfig signingConfigs.release minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" } } // applicationVariants are e.g. debug, release applicationVariants.all { variant -> variant.outputs.each { output -> // For each separate APK per architecture, set a unique version code as described here: // https://developer.android.com/studio/build/configure-apk-splits.html def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] def abi = output.getFilter(OutputFile.ABI) if (abi != null) { // null for the universal-debug, universal-release variants output.versionCodeOverride = versionCodes.get(abi) * 1048576 + defaultConfig.versionCode } } } } dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "com.facebook.react:react-native:+" // From node_modules // react-native-screens requires dependencies below implementation 'androidx.appcompat:appcompat:1.1.0-rc01' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02' // For animated GIF support implementation 'com.facebook.fresco:animated-gif:2.0.0' implementation 'com.android.support:multidex:1.0.3' // allow tls < 1.3 in Android 4.* versions implementation "org.conscrypt:conscrypt-android:2.1.0" if (enableHermes) { def hermesPath = "../../node_modules/hermes-engine/android/"; debugImplementation files(hermesPath + "hermes-debug.aar") releaseImplementation files(hermesPath + "hermes-release.aar") } else { implementation jscFlavor } } // Run this once to be able to run the application with BUCK // puts all compile dependencies into folder libs for BUCK to use task copyDownloadableDepsToLibs(type: Copy) { from configurations.compile into 'libs' } apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) // native-base icons required modifications apply from: "../../node_modules/react-native-vector-icons/fonts.gradle" // react-native-firebase required modifications apply plugin: 'com.google.gms.google-services' // compile bug required us to do this googleServices { disableVersionCheck = true } ``` #### `android/settings.gradle`: ```groovy rootProject.name = 'XXX' apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) include ':app' ``` #### `MainApplication.java`: ```java package com.builduplabs.kissmyscore; import android.app.Application; import android.content.Context; import androidx.multidex.MultiDexApplication; import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.soloader.SoLoader; import java.lang.reflect.InvocationTargetException; import java.util.List; // react-native-code-push required modifications import com.microsoft.codepush.react.CodePush; // react-native-branch required modifications import io.branch.rnbranch.RNBranchModule; // allow tls < 1.3 in Android 4.* versions import java.security.Security; public class MainApplication extends MultiDexApplication implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List packages = new PackageList(this).getPackages(); // Packages that cannot be autolinked yet can be added manually here, for example: // packages.add(new MyReactNativePackage()); return packages; } @Override protected String getJSMainModuleName() { return "index"; } // react-native-code-push required modifications @Override protected String getJSBundleFile() { return CodePush.getJSBundleFile(); } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } @Override public void onCreate() { super.onCreate(); // allow tls < 1.3 in Android 4.* versions Security.insertProviderAt(new org.conscrypt.OpenSSLProvider(), 1); SoLoader.init(this, /* native exopackage */ false); initializeFlipper(this); // Remove this line if you don't want Flipper enabled // react-native-branch required modifications RNBranchModule.getAutoInstance(this); } /** * Loads Flipper in React Native templates. * * @param context */ private static void initializeFlipper(Context context) { if (BuildConfig.DEBUG) { try { /* We use reflection here to pick up the class that initializes Flipper, since Flipper library is not available in release mode */ Class aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper"); aClass.getMethod("initializeFlipper", Context.class).invoke(null, context); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } } ``` #### `AndroidManifest.xml`: ```xml ```


Environment

Click To Expand

**`react-native info` output:** ``` info Fetching system and libraries information... System: OS: Windows 10 10.0.18363 CPU: (4) x64 Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz Memory: 1.84 GB / 7.91 GB Binaries: Node: 12.16.0 - C:\Program Files\nodejs\node.EXE Yarn: 1.19.2 - C:\Program Files (x86)\Yarn\bin\yarn.CMD npm: 6.13.6 - C:\Program Files\nodejs\npm.CMD IDEs: Android Studio: Version 3.6.0.0 AI-192.7142.36.36.6200805 npmPackages: react: 16.9.0 => 16.9.0 react-native: 0.61.3 => 0.61.3 ``` - **Platform that you're experiencing the issue on**: - [ ] iOS - [ ] Android - [ ] **iOS** but have not tested behavior on Android - [x] **Android** but have not tested behavior on iOS - [ ] Both - **`react-native-firebase` version you're using that has this issue:** - `6.0.3` - **`Firebase` module(s) you're using that has the issue:** - `Admob / BannerAd` - **Are you using `TypeScript`?** - `Y/N` & `VERSION`


Ehesp commented 4 years ago

I thought it could be because of parent components so I lifted the component up the tree and it still happens even in isolation

Are you saying that you've checked there's no re-renders happening? (e.g. the render log only happens once?)

Joaojuby commented 4 years ago

I've checked there's no parent re-renders but there is re-renders with that banner component even in isolation.

Here's what the logs in those components show

 LOG  FrameWithBannerAd did mount
 LOG  ad loaded!
 LOG  [undefined]
 LOG  RENDER!
 LOG  ad loaded!
 LOG  [undefined]
 LOG  RENDER!
 LOG  ad loaded!
 LOG  [undefined]
 LOG  RENDER!

the undefined is the args array, I tested that thinking it could be sending meta-data explaining the event triggers.

Ehesp commented 4 years ago

Could you put a console log here: https://github.com/invertase/react-native-firebase/blob/master/packages/admob/lib/ads/BannerAd.js#L64

It'd be interesting to see whether it's a native event being sent or a JS issue.

Joaojuby commented 4 years ago

I took the liberty to log the nativeEvent object, we might be getting somewhere

 LOG  RENDER!
 LOG  FrameWithBannerAd did mount
 LOG  onNativeEvent
 LOG  {"height": 50, "type": "onSizeChange", "width": 320}
 LOG  onNativeEvent
 LOG  {"height": 51, "type": "onAdLoaded", "width": 321}
 LOG  ad loaded!
 LOG  onNativeEvent
 LOG  {"height": 51, "type": "onAdLoaded", "width": 321}
 LOG  ad loaded!
 LOG  onNativeEvent
 LOG  {"height": 51, "type": "onAdLoaded", "width": 321}
 LOG  ad loaded!

I've supressed the previous args array log

Ehesp commented 4 years ago

Hm that onAdLoaded event comes directly from the native event subscriber here: https://github.com/invertase/react-native-firebase/blob/master/packages/admob/android/src/main/java/io/invertase/firebase/admob/ReactNativeFirebaseAdMobBannerAdViewManager.java#L127

I wonder why that's triggering multiple times...

Joaojuby commented 4 years ago

Tried to use a production adUnitId

 LOG  RENDER!
 LOG  FrameWithBannerAd did mount
 LOG  onNativeEvent
 LOG  {"height": 50, "type": "onSizeChange", "width": 320}
 LOG  onNativeEvent
 LOG  {"code": "error-code-no-fill", "message": "The ad request was successful, but no ad was returned due to lack of ad inventory.", "type": "onAdFailedToLoad"}
 LOG  onNativeEvent
 LOG  {"code": "error-code-no-fill", "message": "The ad request was successful, but no ad was returned due to lack of ad inventory.", "type": "onAdFailedToLoad"}
 LOG  onNativeEvent
 LOG  {"height": 51, "type": "onAdLoaded", "width": 321}
 LOG  ad loaded!

and this is exactly why it's concerning, if we do three times the amount of requests, it means we'll be three times closer to depleting them or simply get banned/blacklisted from the system as far as I've been told

Joaojuby commented 4 years ago

Update, reading into the code here https://github.com/invertase/react-native-firebase/blob/master/packages/admob/lib/ads/BannerAd.js#L64

As I understand, the listener function is declared everytime the banner is rendered. With the state changes I imagine it's possible we are binding several different listeners functions

Edit: Tested what I was saying by creating a React Class with the listener function being passed down to the BannerAd.

the class I made to test it out ```javascript class BannerAdController extends React.Component { componentDidMount() { console.log('mounted ad controller') } onNativeEvent = ({ nativeEvent }) => { console.log('onNativeEvent'); console.log(nativeEvent); const { width, height, type } = nativeEvent; if (type !== 'onSizeChanged' && isFunction(this.props[type])) { let eventPayload; if (type === 'onAdFailedToLoad') { eventPayload = NativeFirebaseError.fromEvent(nativeEvent, 'admob'); } this.props[type](eventPayload); } if (width && height && this.props.size !== 'FLUID') { // setDimensions([width, height]); console.log('would set dimensions') } } render() { return } } ```

Still received the events, I'm out of ideas myself on how to help from here :pensive:

stale[bot] commented 4 years ago

Hello 👋, to help manage issues we automatically close stale issues. This issue has been automatically marked as stale because it has not had activity for quite some time. Has this issue been fixed, or does it still require the community's attention?

This issue will be closed in 15 days if no further activity occurs. Thank you for your contributions.

JeffGuKang commented 4 years ago

I got a exactly same issue. Ad loaded three times on Android without re-render.

JeffGuKang commented 4 years ago

I think requestAd is called multiple time asyncronously. It is work well after change the code as below. Surely, you have to set size to BannerAd

React Native

<BannerAd
        unitId={__DEV__ ? TestIds.BANNER : props.bannerId || BANNER_ID}
        size={props.size || BannerAdSize.SMART_BANNER}
        onAdLoaded={() => {
          console.log('Admob loaded')
        }}
      />

Original change to

@ReactProp(name = "unitId")
  public void setUnitId(ReactViewGroup reactViewGroup, String value) {
    unitId = value;
    // requestAd(reactViewGroup); // prevent requestAd
  }

  @ReactProp(name = "request")
  public void setRequest(ReactViewGroup reactViewGroup, ReadableMap value) {
    request = ReactNativeFirebaseAdMobCommon.buildAdRequest(value);
    // requestAd(reactViewGroup); // prevent requestAd
  }

@ReactProp(name = "size")
  public void setSize(ReactViewGroup reactViewGroup, String value) {
    size = ReactNativeFirebaseAdMobCommon.stringToAdSize(value);

    int width;
    int height;
    WritableMap payload = Arguments.createMap();

    if (size == AdSize.SMART_BANNER) {
      width = (int) PixelUtil.toDIPFromPixel(size.getWidthInPixels(reactViewGroup.getContext()));
      height = (int) PixelUtil.toDIPFromPixel(size.getHeightInPixels(reactViewGroup.getContext()));
    } else {
      width = size.getWidth();
      height = size.getHeight();
    }

    payload.putDouble("width", width);
    payload.putDouble("height", height);

    if (size != AdSize.FLUID) {
      sendEvent(reactViewGroup, "onSizeChange", payload);
    }
    requestAd(reactViewGroup); // Only call requestAd here
  }
stale[bot] commented 4 years ago

Hello 👋, to help manage issues we automatically close stale issues. This issue has been automatically marked as stale because it has not had activity for quite some time. Has this issue been fixed, or does it still require the community's attention?

This issue will be closed in 15 days if no further activity occurs. Thank you for your contributions.

stale[bot] commented 4 years ago

Closing this issue after a prolonged period of inactivity. If this is still present in the latest release, please feel free to create a new issue with up-to-date information.

JeffGuKang commented 4 years ago

Is this solved? It can cause to ban admob account by google.

okcompewter commented 4 years ago

I've noticed the same in my app, every render causes 3 requests. Sure enough today I received a warning from Google:

"Ad serving limit placed on your AdMob account"

I'm on "@react-native-firebase/admob": "^6.1.0". Have you found a fix?

I would update to the latest available package, but I'm not sure how to go about that since it doesn't appear all other packages have the same version available, and the app complains if they are not all on the same version.

mikehardy commented 4 years ago

@okcompewter Updating to current highest stable version of everything works fine, and will get you firebase-ios-sdk 6.28.1 which you can override (check https://rnfirebase.io/#overriding-native-sdk-versions) to get even more up to date if desired.

okcompewter commented 4 years ago

Thanks @mikehardy , doing that now, and following the peerDependency warnings as well helped me see that even admob@7 requires app@8. So far, so good!

rohitkum28 commented 3 years ago

@mikehardy I am facing the same issue where my BannerAd component is not getting re-rendered but onAdLoaded is called multiple times. I am using

"@react-native-firebase/admob": "^10.0.0",
"@react-native-firebase/app": "^10.0.0",

Below is my banner ad component

const adUnitId = __DEV__ ? TestIds.BANNER : Constants.BANNERUID;

const BannerAdmob = () => {
    console.log('Banner Component loaded')
    return (
        <BannerAd
            unitId={adUnitId}
            size={BannerAdSize.SMART_BANNER}
            requestOptions={{
                requestNonPersonalizedAdsOnly: true,
            }}
            onAdLoaded={() => {
                console.log('Advert loaded');
            }}
            onAdFailedToLoad={(error) => {
                console.error('Advert failed to load: ', error);
            }}
        />
    )
}

I see "Banner Component loaded" once in my console and "Advert loaded" is called multiple times periodically like after every minute

[Thu Feb 11 2021 19:49:07.744]  LOG      Advert loaded
[Thu Feb 11 2021 19:50:19.560]  LOG      Advert loaded. 
[Thu Feb 11 2021 19:51:31.000]  LOG      Advert loaded
[Thu Feb 11 2021 19:52:41.962]  LOG      Advert loaded
[Thu Feb 11 2021 19:53:52.924]  LOG      Advert loaded
[Thu Feb 11 2021 19:55:04.709]  LOG      Advert loaded

This issue has caused my Admob account to receive a warning "Ad serving has been limited".

mikehardy commented 3 years ago

Versions: Attempting to reproduce on old versions is typically inefficient. @rohitkum28 update to current stable, reproduce, then attempt to reason about the code. Standard practice for any problem, any software.

Attempt to reason about the problem: Trace the code, it's all open source, which is it's strongest feature here, for you. Start at the javascript, add some console.log statements before and after every API call you are using. In node_modules trace into that code in @react-native-firebase/admob and add some console.log statements before and after every native module call. Pick a platform you're most comfortable with and go further, adding logging (e.g. for Android some System.out.println calls) before every firebase-android-sdk or firebase-ios-sdk call. At that point you should have enough information to go on to see when things are being called, so that you can figure out why they are being called.

If there is some deficiency in the code or docs at that point, post a PR and I can work with you to get it merged.

okcompewter commented 3 years ago

@rohitkum28 - This sounds like Google's "optimized refresh" of the Ad unit, which I've seen usually happens every 60 seconds for Banners. This is on by default when you set up your Ad Unit in Admob:

image

I don't think it's abnormal to see new ads requested every minute for a <BannerAd>. If Admob is saying you hit your ad serving limit, something else may be going on...?

medonagy45 commented 3 years ago

Is this solved? It can cause to ban admob account by google.

mikehardy commented 3 years ago

@medonagy45 I don't recall any PRs related to it and it's been dormant nearly a year which "smells" like a project-specific coding issue causing ad load on render and multiple renders or a config issue of some sort. The discussion above conflated setting size etc which is separate I think and has some discussion here #3185