You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native Expo中Admob Rewarded Ad仅能展示一次的问题修复

React Native Expo Admob激励广告重复展示问题修复方案

问题现象

集成Admob激励广告后,广告仅能展示一次,再次调用时触发错误:

Error: RewardedAd.show() The requested RewardedAd has not loaded and could not be shown.

关闭并重新打开模态框后可再次观看,但用户体验不佳,需实现无需关闭模态框即可重复观看广告的效果。

问题根源

  1. 全局创建的广告实例仅加载一次,展示后未重新加载新广告
  2. loaded状态在广告展示后未重置,导致后续调用watchAd时误判广告已加载
  3. 未监听广告关闭、展示失败等事件,无法及时触发新广告的加载

修复方案

1. 重构useRewardedAd钩子

修改钩子逻辑,支持广告展示后自动重新加载,并管理广告实例生命周期:

import { useEffect, useState } from "react";
import {
  RewardedAd,
  RewardedAdEventType,
  TestIds,
} from "react-native-google-mobile-ads";

const adUnitId = __DEV__
  ? TestIds.REWARDED
  : "ca-app-pub-xxxxxxxxxxxxx/yyyyyyyyyyyyyy";

export const useRewardedAd = () => {
  const [loaded, setLoaded] = useState(false);
  const [rewardedAd, setRewardedAd] = useState<RewardedAd | null>(null);

  // 创建新的广告实例
  const createAdInstance = () => {
    const ad = RewardedAd.createForAdRequest(adUnitId);
    setRewardedAd(ad);
    return ad;
  };

  // 加载广告
  const loadAd = () => {
    const ad = rewardedAd || createAdInstance();
    ad.load();
  };

  // 展示广告
  const watchAd = () => {
    if (loaded && rewardedAd) {
      rewardedAd.show();
    }
  };

  useEffect(() => {
    let currentAd = rewardedAd || createAdInstance();

    // 监听广告加载完成
    const unsubscribeLoaded = currentAd.addAdEventListener(
      RewardedAdEventType.LOADED,
      () => setLoaded(true)
    );

    // 监听用户获得奖励
    const unsubscribeEarned = currentAd.addAdEventListener(
      RewardedAdEventType.EARNED_REWARD,
      (reward) => console.log("用户获得奖励: ", reward)
    );

    // 监听广告关闭,重置状态并加载新广告
    const unsubscribeClosed = currentAd.addAdEventListener(
      RewardedAdEventType.CLOSED,
      () => {
        setLoaded(false);
        loadAd();
      }
    );

    // 监听广告展示失败,重置状态并加载新广告
    const unsubscribeFailed = currentAd.addAdEventListener(
      RewardedAdEventType.FAILED_TO_SHOW,
      () => {
        setLoaded(false);
        loadAd();
      }
    );

    // 初始化加载广告
    loadAd();

    // 组件卸载时取消所有监听
    return () => {
      unsubscribeLoaded();
      unsubscribeEarned();
      unsubscribeClosed();
      unsubscribeFailed();
    };
  }, [rewardedAd]);

  return { watchAd, isAdLoaded: loaded };
};

2. 更新组件调用逻辑

在Earn组件中使用新的钩子返回值,优化用户提示:

import { View, Text, Alert } from "react-native";
import React, { useContext } from "react";
import { Entypo, FontAwesome, FontAwesome5 } from "@expo/vector-icons";
import EarnRow from "./EarnRow/EarnRow";
import { PlayerInformationContext } from "@/context/PlayerInformation";
import { useRewardedAd } from "@/hooks/useRewardedAd/useRewardedAd";

const Earn = () => {
  const { watchAd, isAdLoaded } = useRewardedAd();

  const {
    money,
    seenAdsCount,
    health,
    setHealth,
    setMoney,
    handleSeenAdsCount,
  } = useContext(PlayerInformationContext);
  
  const handleEarn = async (type: string) => {
    switch (type) {
      case "getMoney":
        if (seenAdsCount < 10) {
          handleSeenAdsCount(seenAdsCount + 1);
        }
        if (isAdLoaded) {
          watchAd();
        } else {
          Alert.alert("广告加载中", "请稍等片刻再尝试");
        }
        break;
      case "getHealth":
        if (money >= 100) {
          setHealth(health + 12);
          setMoney(money - 100);
          Alert.alert("获得能量!", "你获得了12点能量值!");
        } else {
          Alert.alert("金币不足", "进行此操作需要100金币");
        }
        break;

      default:
        break;
    }
  };

  return (
    <View className="w-full h-full items-start justify-start">
      <Text className="w-full mt-10 text-center text-white font-bold text-4xl">
        赚取
      </Text>

      <EarnRow
        icon={
          <FontAwesome5
            name="bitcoin"
            size={40}
            className="-rotate-12 bg-white rounded-full"
            color="orange"
          />
        }
        coinAmount="+30"
        rewardText="免费"
        rewardAction={() => handleEarn("getMoney")}
        buttonIcon={<Entypo name="video" size={24} color="white" />}
      />

      <EarnRow
        icon={<FontAwesome name="heart" size={40} color="red" />}
        coinAmount="+12"
        rewardText="100"
        rewardAction={() => handleEarn("getHealth")}
        buttonIcon={
          <FontAwesome5
            name="bitcoin"
            size={24}
            className="-rotate-12 bg-white rounded-full"
            color="orange"
          />
        }
      />
    </View>
  );
};

export default Earn;

核心优化点

  • 广告实例内部管理,避免全局实例复用导致的状态混乱
  • 监听广告关闭、展示失败事件,自动重置加载状态并触发新广告加载
  • 返回isAdLoaded状态,组件可实时判断广告是否可用,给出友好提示
  • 每次广告展示完成后立即加载新广告,确保用户下次点击时已有可用广告

内容的提问来源于stack exchange,提问作者Ozan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 21:29:53