using System; using System.Collections; using System.Collections.Generic; using System.Linq; using EFSDK; using GoogleMobileAds.Api; using UnityEngine; namespace WZ { class ShowNativePosition { public NativeOverlayAd NativeOverlayAd; public NativeAdPosition Position; public ShowNativePosition(NativeOverlayAd nativeOverlayAd, NativeAdPosition position) { NativeOverlayAd = nativeOverlayAd; Position = position; } } public class AdmobNativeAdManager { private Dictionary _nativeAds = new Dictionary(); private Dictionary _adRevenueCache = new Dictionary(); private Dictionary _retryCounters = new Dictionary(); private Dictionary _adStartLoadTimes = new Dictionary(); private Dictionary showingNativeAds = new(); public void InitializeAdUnits(List adUnitIds) { foreach (var adUnitId in adUnitIds) { CreateNativeAd(adUnitId); } } private void CreateNativeAd(string adUnitId) { if (string.IsNullOrEmpty(adUnitId)) return; if (_nativeAds.ContainsKey(adUnitId)) { LoggerUtils.Debug($"[Admob] Native Ad unit {adUnitId} already exists"); return; } LoadAd(adUnitId); _adRevenueCache[adUnitId] = 0; } public void LoadAd(string adUnitId, bool timingRefresh = false) { LoggerUtils.Debug($"[Admob] Native Ad unit {adUnitId} load start , timingRefresh {timingRefresh}"); //判断在线参数是否包含这个id if (!AdmobAdsManager.Instance.FindAdsID(AdsType.Native, adUnitId)) { return; } NativeOverlayAd.Load(adUnitId, new AdRequest(), new NativeAdOptions(), (NativeOverlayAd ad, LoadAdError error) => { _adStartLoadTimes[adUnitId] = Time.realtimeSinceStartup; LoggerUtils.Debug($"[Admob] Native Ad unit {adUnitId} load end, timingRefresh {timingRefresh}. {ad} error {error}"); if (error != null || ad == null) { if (!timingRefresh) { if (!_retryCounters.TryAdd(adUnitId, 0)) { _retryCounters[adUnitId]++; } } var adSource = ""; if (ad != null) { adSource = ad.GetResponseInfo().GetLoadedAdapterResponseInfo().AdSourceName; } var reason = ""; if (error != null) { reason = error.GetMessage(); } AdsActionEvents.TrackAdFailToLoad(PlatformType.Admob, adSource, adUnitId, AdsType.Native, Time.realtimeSinceStartup - _adStartLoadTimes[adUnitId], reason); // 定时任务的刷新,由定时任务自己去刷新 if (timingRefresh) { if (!showingNativeAds.ContainsKey(adUnitId)) { LoggerUtils.Debug($"[Admob] Native Ad unit {adUnitId}, 已经隐藏了,失败了也不需要在重新load了"); return; } TimingRefresh(adUnitId); return; } var retryDelay = Math.Pow(2, Math.Min(6, _retryCounters[adUnitId])); TimerUtils.Instance.DelayExecute((float)retryDelay, () => { LoadAd(adUnitId); }); LoggerUtils.Error($"[Admob] Native Ad unit {adUnitId}, timingRefresh {timingRefresh} ad failed to load an ad with error : " + error + " \n retryDelay :" + retryDelay); return; } if (!AdmobAdsManager.Instance.FindAdsID(AdsType.Native, adUnitId)) { return; } AdsActionEvents.TrackAdLoaded(PlatformType.Admob, ad.GetResponseInfo().GetLoadedAdapterResponseInfo().AdSourceName, adUnitId, AdsType.Native, Time.realtimeSinceStartup - _adStartLoadTimes[adUnitId]); _retryCounters[adUnitId] = 0; // 临时缓存上一次的native ad,以便于刷新的时候,显示了新的,隐藏老的。 var tempAd = _nativeAds.GetValueOrDefault(adUnitId, null); var nativeEcpm = AdmobUtils.GetNativeEcpm(ad); _nativeAds[adUnitId] = ad; _adRevenueCache[adUnitId] = nativeEcpm; LoggerUtils.Debug($"Admob Native ad loaded with, timingRefresh {timingRefresh} nativeEcpm = {nativeEcpm} response : " + ad.GetResponseInfo().ToString()); AdsKeyEvents.Instance.LogAdFPUEvents(AdsType.Native); ad.OnAdPaid += (AdValue adValue) => { LoggerUtils.Debug($"[Admob] Native ad paid {adValue.Value} {adValue.CurrencyCode}."); AdmobAdsManager.Instance.TrackAdImpression(ad.GetResponseInfo().GetLoadedAdapterResponseInfo(), adValue, AdsType.Native, adUnitId); }; ad.OnAdImpressionRecorded += () => { LoggerUtils.Debug("[Admob] Native ad recorded an impression."); }; ad.OnAdClicked += () => { AdsActionEvents.TrackAdClicked(PlatformType.Admob, ad.GetResponseInfo().GetLoadedAdapterResponseInfo().AdSourceName, adUnitId, AdsType.Native, "", AdmobUtils.GetNativeEcpm(ad)); LoggerUtils.Debug("[Admob] Native ad was clicked."); }; ad.OnAdFullScreenContentOpened += () => { LoggerUtils.Debug("[Admob] Native ad full screen content opened."); }; ad.OnAdFullScreenContentClosed += () => { AdsActionEvents.TrackAdClicked(PlatformType.Admob, ad.GetResponseInfo().GetLoadedAdapterResponseInfo().AdSourceName, adUnitId, AdsType.Native, "", AdmobUtils.GetNativeEcpm(ad)); LoggerUtils.Debug("[Admob] Native ad full screen content closed."); }; if (timingRefresh && tempAd != null) { if (showingNativeAds.TryGetValue(adUnitId, out var showing)) { var showingNativeOverlayAd = showing.NativeOverlayAd; if (showingNativeOverlayAd != tempAd) { LoggerUtils.Error("[Admob] Native ad Error!!!! showing.NativeOverlayAd != tempAd"); } LoggerUtils.Warning("[Admob] Native ad timing refresh , show ad"); ShowAd(showing.Position, adUnitId, tempAd, showingNativeOverlayAd); } else { LoggerUtils.Warning($"[Admob] Native ad timing refresh , show fail , showing native ads not ad unit id , {adUnitId} , 广告已经隐藏了,刷新成功,不需要再去显示出来"); } } }); } // 显示价格最高的广告 public void ShowHighestPayingAd(NativeAdPosition position) { LoggerUtils.Debug($"[Admob] Native ad ShowHighestPayingAd {position}"); var highestPayingAdUnit = GetHighestPayingAdUnit(); LoggerUtils.Debug($"[Admob] Native ad ShowHighestPayingAd {position} , highestPayingAdUnit = {highestPayingAdUnit}"); if (!string.IsNullOrEmpty(highestPayingAdUnit)) { ShowAd(position, highestPayingAdUnit); } } // 显示特定广告位的广告 public void ShowAd(NativeAdPosition position, string adUnitId, NativeOverlayAd lastAd = null, NativeOverlayAd lastShowAd = null) { if (!AdmobAdsManager.Instance.FindAdsID(AdsType.Native, adUnitId)) { return; } LoggerUtils.Debug($"[Admob] Native ad ShowAd start {adUnitId} , {position}"); if (_nativeAds.TryGetValue(adUnitId, out var ad)) { LoggerUtils.Debug($"[Admob] Native ad ShowAd end {adUnitId} , {position}"); // Define a native template style with a custom style. var style = position.NativeTemplateStyle ?? new NativeTemplateStyle() { TemplateId = NativeTemplateId.Medium, // TemplateId = "big" // MainBackgroundColor = Color.green, CallToActionText = new NativeTemplateTextStyle() { FontSize = 9, Style = NativeTemplateFontStyle.Bold } }; // Renders a native overlay ad at the default size // and anchored to the bottom of the screne. ad.RenderTemplate(style, new AdSize(position.Width, position.Height), position.X, position.Y); showingNativeAds[adUnitId] = new ShowNativePosition(ad, position); ad.Show(); lastAd?.Hide(); lastShowAd?.Hide(); TimingRefresh(adUnitId); } } private void TimingRefresh(string adUnitId) { var nativeReflashGap = int.Parse(FireBaseRemoteConfigManager.Instance.GetRemoteConfigString("Native_Reflash_Gap", "0")); if (nativeReflashGap <= 0) { LoggerUtils.Debug($"[Admob] Native ad ({adUnitId}) timing refresh failed. nativeReflashGap = 0"); return; } if (adUnitId == StaticValue.AdmobFullNativeId) { LoggerUtils.Debug($"[Admob] Native ad ({adUnitId}) timing refresh finished. ad unit id is full native id."); return; } if (!showingNativeAds.ContainsKey(adUnitId)) { LoggerUtils.Debug($"[Admob] Native ad ({adUnitId}) timing refresh finished. ad unit is not show."); return; } WLoom.QueueOnMainThread(o => { var refreshAdUnitId = (string)o; LoggerUtils.Debug($"[Admob] Native ad ({adUnitId}) timing refresh load start refreshAdUnitId : {refreshAdUnitId}."); if (showingNativeAds.ContainsKey(refreshAdUnitId)) { LoadAd(refreshAdUnitId, true); } else { LoggerUtils.Warning($"[Admob] Native ad ({adUnitId}) timing refresh finished. ad unit is not show. refreshAdUnitId : {refreshAdUnitId}."); } }, adUnitId, nativeReflashGap); } private float GetLoadedTime(string adUnitId) { return _adStartLoadTimes.TryGetValue(adUnitId, out var time) ? time : 0; } // 检查特定广告位是否可用 public bool IsAdAvailable(string adUnitId) { return _nativeAds.TryGetValue(adUnitId, out _); } // 获取所有可用的广告位 public List GetAvailableAdUnits() { return _nativeAds.Select(kvp => kvp.Key).ToList(); } // 获取价格最高的广告位ID public string GetHighestPayingAdUnit() { string highestPayingAdUnit = null; double highestRevenue = -1; LoggerUtils.Debug($"[Admob] Native ad GetHighestPayingAdUnit {_adRevenueCache.Count}"); foreach (var kvp in _adRevenueCache) { var adUnitId = kvp.Key; var revenue = kvp.Value; // 确保广告确实已加载并且价格更高 if (IsAdAvailable(adUnitId) && revenue > highestRevenue) { highestRevenue = revenue; highestPayingAdUnit = adUnitId; } } return highestPayingAdUnit; } // 获取价格最高的广告收益信息 public double GetHighestPayingAdRevenue() { var highestPayingAdUnit = GetHighestPayingAdUnit(); if (!string.IsNullOrEmpty(highestPayingAdUnit) && _adRevenueCache.TryGetValue(highestPayingAdUnit, out var revenue)) { return revenue; } return 0; } // 获取广告收益信息 public double GetAdRevenue(string adUnit) { if (!string.IsNullOrEmpty(adUnit) && _adRevenueCache.TryGetValue(adUnit, out var revenue)) { return revenue; } return -1; } // 清理资源 public void Destroy() { foreach (var ad in _nativeAds.Values) { ad.Destroy(); } _nativeAds.Clear(); _adRevenueCache.Clear(); } public IEnumerator RemoveNative(string adUnitId) { // 不需要等待了 // yield return new WaitForSeconds(0.2f); if (adUnitId == null || string.IsNullOrEmpty(adUnitId)) { foreach (var key in showingNativeAds.Keys.ToList()) { LoggerUtils.Debug($"[Admob] Native ad removing NativeAd {adUnitId}"); showingNativeAds[key].NativeOverlayAd.Hide(); // 从字典中删除元素 showingNativeAds.Remove(key); LoadAd(key); } yield break; } if (showingNativeAds.TryGetValue(adUnitId, out var tempAd)) { LoggerUtils.Debug($"[Admob] Native ad removing NativeAd {adUnitId}"); tempAd.NativeOverlayAd.Hide(); showingNativeAds.Remove(adUnitId); LoadAd(adUnitId); } else { LoggerUtils.Debug($"[Admob] Native ad removing NativeAd {adUnitId} , failed to remove NativeAd"); } } public void ClearAds(string[] adUnitIds) { // 将数组转换为HashSet以提高查找性能 HashSet validKeys = new HashSet(adUnitIds); // 收集需要移除的key List keysToRemove = new List(); foreach (var key in _nativeAds.Keys) { if (!validKeys.Contains(key)) { keysToRemove.Add(key); } } // 移除不在数组中的key foreach (string key in keysToRemove) { _nativeAds.Remove(key); _retryCounters.Remove(key); _adStartLoadTimes.Remove(key); _adRevenueCache.Remove(key); } } } }