Unity mediation refresh rate and connection interuption handing

Hi, I first need to state my ads are initialized by a persistent singleton AdManager GameObject that only calls InitServices(); once in its entire lifetime. Before this, I used an Admanager per level bases. I did not get reconnection issues, because it would just initialize again automatically on scene load.

Okay, so I am wondering what the refresh rate for mediation is because often I get no fill item (even when I’m using test ads, that’s fine though since I am getting test ads nontheless). I would like to know how often ads are fetched, and if there is a way to customize ad fetching/refreshing. 2nd, I want to know how connection interupts are handled. I ended up getting this error for disrupts, and I was unable to get ads in the same session. [quote]
Unity Exception raised while retrieving the Ad Unit Configuration: com.unity3d.mediation.h0: Failed to fetch Ad Unit due to connectivity issues: Request to https://mediation-instantiation.unityads.unity3d.com/v1/waterfall failed due to java.io.InterruptedIOException: timeout
[/quote]
I want to know 2 things about connection interrupts, and how the ads sdk handles the situation;

  • when ads are initialized at the begining when the user has internet turned off, but later during the session the internet is reconnected
  • when internet connection is abruptly lost mid session after initialization, but is reconnected later on

I tried runing the above senario myself a few times, once the connection is lost, I was unable to reconnect and load new ads. I additionally have questions on how I can customize these behaviors;

  • how to fetch new ads when ads have been shows and no new ad is loaded
  • how to check for internet, and send a call to reconnect if connection was disrupted.

EDIT: I tested out a few of issues myself and what happens with the default mediation sample code.

  • First issue: what happens when initialization call is made when the internet is not connected? I have a test device that completly disconnects from the internet because when wifi mode is shut off, it doesn’t connect to a carrier. The test can also be carried out with airplane mode and wifi on/off conditions. The result was that mediation does not initialize, and will not try to initialize.

  • Second issue: what happens if it is initialized, and the connection is disrupted? Result for the sample code was that it does not try to reconnect. Additionally, once initialized, even after disconnection, a frustrating bug I encountered is that UnityServices.State returns Initialized.

  • Third issue: What is the ads’ refresh rate, is it automatic, and how often it gets called? Result I found that it is only automatically loaded after a successful ad show event is registered. There is no way to customize it.

  • Forth, how can I create custom errors on my own to test out whether my code can handle it? I was unable to find a way to customize the exeception. There is a way, that is when internet is disconnected, to change to my own error type I want to pass to the switch statement, seen later in AdFailedLoad(); but this is still very much hacky, so I did not try it.

So, the solution I typed up is a bit hacky, but it did seem to solve all the connection and disruption issues, and kept the ads coming consistently. Here it is:

using System;
using Unity.Services.Core;
using Unity.Services.Mediation;
using UnityEngine;
using UnityEngine.SceneManagement;

public class AndroidRewardedAd : MonoBehaviour, IDisposable
{
    [SerializeField] WorkShopManager workShopManager;
    [SerializeField] GameManager gameManager;
    private IRewardedAd ad;
    private string adUnitId = "Rewarded_Android";
    private string gameId = "4444444";
    public bool HasAd;
    int errorLoadingAdCount = 0;
    string adLoadErrorE;

    /** This is my own in game script. Most of the code is dirrectly from the sample. The changes are in Awake(); my custom TryInitServices(); InitializationFailed(); AdFailedLoaded(); **/

    private void Awake()
    {

        TryInitServices();
        InvokeRepeating(nameof(InvokedDebug), 0f, 5f);
    }

    private void InvokedDebug() // a quick hacky debug to let me see some the stats
    {
        Debug.Log("AndroidRewardedAd: Connection == " + Application.internetReachability + ". UnityServices.State == " + UnityServices.State +
                  ", HasAd: " + HasAd + ", Sequencial Failed AdLoad Error Count: " + errorLoadingAdCount + "\n Last AdLoadError Message type: " + adLoadErrorE);
    }
    private void OnEnable() //  my scripts custom settings on scene load
    {
        SceneManager.sceneLoaded += InitConfigs;
    }
    private void OnDisable() // my custom
    {
        SceneManager.sceneLoaded -= InitConfigs;
    }

    private void InitConfigs(Scene scene, LoadSceneMode mode) // my custom method
    {
        gameManager = FindObjectOfType<GameManager>();
        workShopManager = FindObjectOfType<WorkShopManager>();
        if (gameManager != null && HasAd)
        {
            gameManager.ActivateRewardButton();
        }
    }

    void TryInitServices() // my custom method to check for internet, and connect automatically
    {
        // Check if I have internet
        if (Application.internetReachability != NetworkReachability.NotReachable)
        {
            // If I have internet, am I initialized?
            InitServices();
        }
        else // start the process again
        {
            Debug.Log("Internet is NOT REACHABLE for REWARDED AD, trying again in 20 seconds");
            Invoke(nameof(TryInitServices), 20f);
        }
    }
    public async void InitServices()
    {
        try
        {
            InitializationOptions initializationOptions = new InitializationOptions();
            initializationOptions.SetGameId(gameId);
            await UnityServices.InitializeAsync(initializationOptions);
            InitializationComplete();
        }
        catch (Exception e)
        {
            InitializationFailed(e);
        }
    }

    public void SetupAd()
    {
        //Create
        ad = MediationService.Instance.CreateRewardedAd(adUnitId);

        //Subscribe to events
        ad.OnClosed += AdClosed;
        ad.OnClicked += AdClicked;
        ad.OnLoaded += AdLoaded;
        ad.OnFailedLoad += AdFailedLoad;
        ad.OnUserRewarded += UserRewarded;

        // Impression Event
        MediationService.Instance.ImpressionEventPublisher.OnImpression += ImpressionEvent;
    }
    public void Dispose() => ad?.Dispose();
    public async void ShowAd()
    {
        if (ad.AdState == AdState.Loaded)
        {
            try
            {
                RewardedAdShowOptions showOptions = new RewardedAdShowOptions();
                showOptions.AutoReload = true;
                await ad.ShowAsync(showOptions);
                AdShown();
            }
            catch (ShowFailedException e)
            {
                AdFailedShow(e);
            }
        }
    }

    private void InitializationComplete()
    {
        SetupAd();
        LoadAd();
    }

    private async void LoadAd()
    {
        try
        {
            await ad.LoadAsync();
        }
        catch (LoadFailedException)
        {
            // We will handle the failure in the OnFailedLoad callback
        }
    }



    private void InitializationFailed(Exception e)
    {
        Debug.Log("REWAREDED AD Initialization Failed: " + e.Message);
        Debug.Log("REWAREDED AD Initialization Failed: TryInitServices will be called again after 20 secs");
        // now start the loops to check if internet is connected.
        Invoke(nameof(TryInitServices), 20f);
    }

    private void AdLoaded(object sender, EventArgs e)
    {
        Debug.Log("REWARDED Ad loaded");
        HasAd = true;
        errorLoadingAdCount = 0;
        if (gameManager != null) gameManager.ActivateRewardButton();
    }

    private void AdFailedLoad(object sender, LoadErrorEventArgs e) // Customized method
    {
        Debug.Log("Failed to ANDROID REWARDED ad");
        Debug.Log(e.Message);
        HasAd = false;
        if (gameManager != null) gameManager.DeactivateRewardButton();
        Debug.LogWarning("Failed to LOAD REWARDED ad: ");
        adLoadErrorE = e.Message + "\nLoadError: " +e.Error;
        switch (e.Error)
        {
            // when it is not connected or initialized, let it break out and try again.
            case LoadError.SdkNotInitialized:
            case LoadError.NetworkError:
            case LoadError.MissingMandatoryMemberValues:
            case LoadError.Unknown:
                Debug.Log("Detected a not connected or unitialized type of error during REWARDED ad load call. Invoking TryInitServices in 20 secs");
                Invoke(nameof(TryInitServices), 20f);
                break;

            // it is connected.
            case LoadError.TooManyLoadRequests:
                Debug.Log("Detected Too many ad load requests type error during REWARDED ad load. Canceling all invoke calls, and trying to load ad again after period of 60 seconds");
                CancelInvoke();
                Invoke(nameof(LoadAd), 60f);
                break;

            case LoadError.NoFill:
                Debug.Log("Detected REWARDED No-Fill, trying a REWARDED load call in 20 seconds");
                Invoke(nameof(LoadAd), 20f);
                break;

            default:
                Debug.Log("Detected default condition of 2 possible REWARDED AdUnit Showing Or REWARDED ad Unit loading, returning");
                return;
        }
        errorLoadingAdCount++;
        // for extra precaustion that is probably not needed, if there are 20 faulty ad calls, initiate
        // TryingInitServices(); again.
        if (errorLoadingAdCount >= 20f)
        {
            Debug.Log("Serveral REWARDED Ad Load failed calls for unknown reasons. 20x in row, entering TryInitServices Loop in 20 seconds");
            TryInitServices();
        }
    }

    private void AdShown()
    {
        Debug.Log("REWARDED Ad shown!");
    }

    private void AdClosed(object sender, EventArgs e)
    {
        Debug.Log("REWARDED Ad has closed");
        // Execute logic after an ad has been closed.
    }

    private void AdClicked(object sender, EventArgs e)
    {
        Debug.Log("REWARDED Ad has been clicked");
        // Execute logic after an ad has been clicked.
    }

    private void AdFailedShow(ShowFailedException e)
    {
        Debug.Log(e.Message);
    }

    private void ImpressionEvent(object sender, ImpressionEventArgs args)
    {
        var impressionData = args.ImpressionData != null ? JsonUtility.ToJson(args.ImpressionData, true) : "null";
        Debug.Log("Impression event from REWARDEDED ad unit id " + args.AdUnitId + " " + impressionData);
    }

    private void UserRewarded(object sender, RewardEventArgs e)
    {
        Debug.Log($"Received reward: type:{e.Type}; amount:{e.Amount}");
        workShopManager?.OnAdShown();
        gameManager?.RewardForWatching();
    }
}

Note*, I’d like to mention that re-initializing may break other parts of the game, such as unity authentication and unity netcode. So this is not the correct solution. As Declas wrote under, once it is initialized, there is no need to initialize again and again, so it is probably best just call loadad calls in this case.

1 Like

Hi @maxkcy ,

First off, this is great work, great research piece.

Okay, so I am wondering what the refresh rate for mediation is because often I get no fill item (even when I’m using test ads, that’s fine though since I am getting test ads nontheless).

This is a known issue currently happening with Unity Ads, we will let you know as soon as this is fixed on Unity Ads.

I want to know 2 things about connection interrupts, and how the ads sdk handles the situation;

  • when ads are initialized at the begining when the user has internet turned off, but later during the session the internet is reconnected

If there is no internet connection and Initialize is called, the Initialize attempt should fail, no retry will occur on the SDK side (the same goes for load attempts).

  • when internet connection is abruptly lost mid session after initialization, but is reconnected later on

This depends, I believe. But generally once the internet connection is restored, you should be able to load and show ads again without reinitializing.

  • how to fetch new ads when ads have been shows and no new ad is loaded

The Load call itself is the only option when attempting to fetch ads, if I understand your question correctly

  • how to check for internet, and send a call to reconnect if connection was disrupted.

I believe you’ve answered this yourself in your snippet (check for internet), as for reconnecting, did you experience any issues loading and showing ads after successfully reconnecting back to the internet?