Tuesday, February 12, 2013

Creating Ads Manger for Android - Part 1 - First part of SBCAdManager




 This is first post on series of articles on how to implement your own simple Ad Manager to monetize your game or app. After lots of dreamless nights you finally finished your app and now you would be glad if it made some money for you. This is where ads come into play. There is plenty of advertising companies in the internet, but usually it is not good to rely only on one of them. If you choose only one you may encounter various unpleasant facts like: the chosen ads provider is focused only on small geographical locality or he has not enough ads for region where your player is located or for all regions at all and the fill rate is thus very low. These are the reasons why you should consider to build some system that will rotate the ads provides based on some rules to be sure that some ad is displayed every time.

 The Ad Manger that I will describe later in the first two parts is written in Java but through JNI you can start and stop it from your native app as well. The system may not be the best one in the world but it works for me and you can see it in our games Deadly Abyss 2 and Mahjong Tris in action. Beside this I have some ideas how to enhance it in future. In next parts I will also describe how I implemented ads from AdMob, Madvertise and Leadbolt.

 On the following picture you can see structure of final package that takes care of managing ads:


 First three classes are concrete implementations of general SBCAdSystem interface. SBCAdManager is the core class that is managing all the rest. It also implements SBCAdEventListener interface that gets notified on some events from particular ad systems. These events are information on whether the ad was loaded or whether the loading failed and so on. Based on these events the manager can change ad system to other. Last file, the SBCAdsConstants is file where we will store and set some parameters for the manager like percentage distribution of requests among available systems.


 Layout

  Our games are based on combination of NDK and Java using GLSurfaceView. Our layout file main.xml looks like this:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.sbcgames.sbcengine.SBCGLView
        android:id="@+id/sbcglview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/adsholder"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:orientation="vertical" >
    </LinearLayout>

</FrameLayout>

 The first part is SBCGLView which is class derived from GLSurfaceView and second part is "adsholder" which is element that will hold the ads in case some is to be visible. If no ads are visible this element is invisible.


 Constants

 First here is listing of SBCAdsConstants.java. these constants will be referred by the SBCAdManager later. I will explain it when we encounter the usage first time:
package com.sbcgames.sbcads; 
 
public class SBCAdsConstants
{
 // Common
 public static final int ADMOB         = 0;
 public static final int MADVERTISE    = 1;
 public static final int LEADBOLT      = 2;

 // systems big
 public static final int systemsBig[]         = { MADVERTISE ,  ADMOB ,  LEADBOLT };
 public static final int probabilityBig[]     = {25     , 45          , 30        };
 public static final String systemsNamesBig[] = {"Madvertise", "Admob", "LeadBolt"};
 // systems small
 public static final int systemsSmall[]         = {LEADBOLT };
 public static final int probabilitySmall[]     = {100};
 public static final String systemsNamesSmall[] = {"LeadBolt"};
 
 // 2 minutes
 public static final long REVIVE_TIME = 1000 * 60 * 2;
 // 2 minutes if all ad systems fialed
 public static final long NEXT_TRY_TIME = 1000 * 60 * 2;
 
 // AdMob
 public static final String ADMOB_AD_UNIT_ID = "your admob unit id here";
 
 // Leadbolt
 public static final String LEADBOLT_UNIT_ID = "your leadbolt unit id here";
 public static final long LEADBOLT_REFRESH_RATE = 1000 * 90 * 1;
  
 // Madvertise
 // !!! change ID in manifest !!!
 // your madvertise unit id here
}


 EventListener

The SBCAdEventListener interface that the manager will implement is very simple:

package com.sbcgames.sbcads;

public interface SBCAdEventListener
{
 public void onClick(SBCAdSystem aSystem);
 public void onReceived(SBCAdSystem aSystem);
 public void onFailed(SBCAdSystem aSystem);
}
 So, our Ads Manager will get informed when ad is received, clicked or when request for ad failed for currently active ad system.


Ad Manager

 Now comes the most important part - the manager itself. This class is quite a long one so it will be divided into parts and described separately. It is so long that in this article we will only start with the implementation and will describe all the features in detail in the next article. The main idea behind the manager is:
  • check if player is online,
  • if not, do nothing; if yes choose randomly ad system with probability based on your settings in file with constants
  • ask for ad,
  • if ad is delivered from ad provider, continue with this provider until it fails. If it fails at first try, mark this provider as dead for time defined in constants (REVIVE_TIME) and try different ad provider
  • if all providers are dead do nothing until NEXT_TRY_TIME expires and then try again.
 Status of every ad system is kept in class SBCAdSystemStatus that is in the same file as the SBCAdManager. The class is very simple and it is more or less a bunch of getters / setters:

class SBCAdSystemStatus
{
 private boolean mFailed;
 private long mFailedTime;
 
 // ------------------------------------------------------------------------
 public SBCAdSystemStatus()
 {
  clear(); 
 }

 // ------------------------------------------------------------------------
 public void clear()
 {
  mFailed = false;
  mFailedTime = 0;
 }
 
 // ------------------------------------------------------------------------
 public boolean isFailed()
 {
  return mFailed;
 }

 // ------------------------------------------------------------------------
 public void setFailed(boolean aFailed)
 {
  mFailed = aFailed;
 }

 // ------------------------------------------------------------------------
 public long getFailedTime()
 {
  return mFailedTime;
 }

 // ------------------------------------------------------------------------
 public void setFailedTime(long aFailedTime)
 {
  mFailedTime = aFailedTime;
 }
}
 the real function of the class is to say in its field mFailed whether the system is live (sending ads) or dead (request for ad failed) and when it failed (filed mFailedTime).

 Now, really to the core class. It starts with some imports and definition of variables:

package com.sbcgames.sbcads;

import java.util.Random;

import com.sbcgames.sbcengine.SBCEngine;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.ViewGroup;

public class SBCAdManager implements SBCAdEventListener
{
 // references to app context and xml layout
 private enum eState {STOPPED, RUNNING};
 private eState mStatus = eState.STOPPED;
 private final ViewGroup mLayout;
 private SBCEngine mSBCEngine;
 private Handler mHandler = new Handler();
 private Random mRnd = new Random();
 // actual ads system
 private SBCAdSystem mSBCAdSystem;
 private int mLastSBCAdSystem = -1;
 private SBCAdSystemStatus mSystemStatus[] = null;
 // systems
 private int mSystems[] = null;
 private int mProbability[] = null;
 private String mSystemsNames[] = null;

 First, there is defined enum that simply says whether the SBCAdManager is running or stopped. The current state is stored in mStatus field. In ViewGroup the reference to our "adsholder" - element defined in main.xml layout - will be stored. SBCEngine is reference to my game engine. The class is derived from Activity class. But I have to use here SBCEngine class instead of Activity as Leadbolt implementation needs some communication with engine.
 Handler will be used to post some requests with delay later and mRnd is there to generate random numbers when choosing ad system.
 Currently used ad system is stored in mSBCAdSystem. ID of last chosen system is in mLastSBCAdSystem and mSystemStatus array will contain as many statuses as we have systems (3 in our case - AdMob, Madvertise, Leadblot)
 Last three variables will hold arrays defined in file with constants. Look at the listing of constants. there are two blocks commented as "system big", "system small". The variables will refer to the first or second. This is not necessary, but it is exact implementation as we had in Deadly Abyss 2. The game was port from bada OS where only paid version without ads existed. For standard 480x800 screens the ads from AdMob and Madvertise were to big so we could use only Leadbolt with smaller ads. For tablets with big screens we can use all of them. I will repeat here the block commented as "system big":

// systems big
public static final int systemsBig[]         = { MADVERTISE ,  ADMOB ,  LEADBOLT };
public static final int probabilityBig[]     = {25          , 45     , 30        };
public static final String systemsNamesBig[] = {"Madvertise", "Admob", "LeadBolt"};
  • the first array is list of systems used for this "configuration",
  • the second array sums up to 100. And the numbers are probability that the system will be randomly chosen,
  • last array is list of names of the systems in easy to read form
 So, this is the place where you can choose how much requests will be in average sent to each implemented ads provider.

Next part

 As rest of the manager contains still a lot of code it will be finished in the next part. As for now you should have your layout prepared, you should know how to create settings in your constants class to distribute requests later.