Marmalade SDK. Интеграция с сервисами рекламы AdMob, Chartboost, Inmobi и Leadbolt на платформах iOS и Android

Подключение расширений в проект


В рамках данной статьи я расскажу как подключить межстраничные (Interstitial) баннеры нескольких рекламных сервисов для мобильных приложений, разработанных в среде Marmalade SDK.
Итак, считаем что Вы уже прочитали документацию Marmalade по подключению перечисленных сервисов, а также имеете учетки на следующих сайтах и зарегистрировали там свои приложения:
При регистрации приложения система присваивает ему некий идентификатор (или пару идентификаторов — как это сделано в Chartboost), которые будут идентифицировать ваши приложения при взаимодействии с сервисами рекламы.

На сегодня я использую Marmalade SDK версии 7.8.0p3 [439542]. Сборка уже содержит в себе следующие интересующие нас расширения:

  • %SDK install folder%/extensions/s3eGoogleAdMob
  • %SDK install folder%/extensions/s3eInMobiAds
  • %SDK install folder%/extensions/s3eChartboost


Расширения для Leadbolt в стандартный дистрибутив не входят. Документация Marmalade рекомендует их с сайта help.leadbolt.com. В отличие от AdMob, Inmobi и Chartboost, Leadbolt предоставляет отдельные расширения под Android и iOS. Расширения эти написаны не самым удобным образом, а именно — некоторые функции в них называются одинаково, что приводит к коллизиям при попытке подключить сразу два расширения в один проект. Поэтому я немного переделал расширения для iOS под себя, добавив суффикс iOS ко всем методам, где это необходимо, чтобы избежать коллизий. Расширения, которые я использую, доступны на github github.com/akk0rd87/Marmalade-Leabolt-SDK. Скачиваем их и копируем в /extensions/, чтобы получилось

%SDK install folder%/extensions/AppTrackerAndroid
%SDK install folder%/extensions/AppTrackerIOS

Включаем в секцию subprojects mkb-файла наши расширения:

Subprojects
{
    ...
    s3eInMobiAds
    s3eGoogleAdMob
    s3eChartBoost
    AppTrackerAndroid
    AppTrackeriOS
}


В секцию deployment добавляем ссылку AppTracker.jar (по умолчанию он находится в AppTrackerAndroid, я вынес его в папку common/jar) и Chartboost-идентификаторы для Anrdoid-приложения:

deployment
{
   ...   
   # THIS NEED FOR LEADBOLT ON ANDROID
   android-external-jars='../common/jar/AppTracker.jar'  
   
   android-extra-strings='(gps_app_id,0), (chartboost_appid, *********),(chartboost_appsig, ***************)'
}

Взаимодействие приложения с сервисом рекламы


Общий принцип взаимодействия приложения с рекламным сервисом:

  • в момент инициализации приложения инициализируется сессия взаимодействия с рекламным сервисом;
  • в определенный разработчиком момент осуществляется запрос на кеширование баннера;
  • через некоторое время после запроса на кеширование баннера вызывается соответствующая callback функция-обработчик, параметры которого указывают на то, выполнен ли запрос кеширования или упал с ошибкой;
  • в определенный разработчиком момент осуществляется проверка результата последнего запроса на кеширование, если таковой был выполнен успешно, то вызывается метод показа рекламы;
  • в зависимости от действий пользователя (например клик, полный просмотр или закрытие видео-объявления) также могут вызываться соответствующие callback функции-обработчики;
  • при закрытии приложения закрываем сессию.


Из вышеописанного принципа вытекают следующие состояния сессии, описанные в adengine_constants.h

#define AD_ENGINE_TEMPORARY  0 // временное состояние сессии. ожидание callback-a;
#define AD_ENGINE_NOT_INITED 1 // сессия не инициализирована
#define AD_ENGINE_INITED     2 // сессия инициирована;
#define AD_ENGINE_LOAD_OK    3 // загрузка баннера прошла успешно;
#define AD_ENGINE_LOAD_ERR   4 // загрузка баннера упала с ошибкой;
#define AD_ENGINE_SHOW_OK    5 // запрос на показ баннера выполнился успешно;
#define AD_ENGINE_TERMINATED 6 // сессия закрыта;

CPP-файл для каждого расширения


Подключаем файлы, в которых описана логика взаимодействия с конкретным рекламным сервисом. Для Leadbolt подключаем два файла: отдельно по iOS и Android, так как для этих ОС у нас отдельные расширения. Также подключаем файл adengine.cpp, который будет управлять этой логикой. Также не забываем создать соответствующие .h-header-ы, в которых будут описаны соответствующие API-фукнции. Фактически adengine.cpp — это мой движок для работы с рекламой, и я использую его в нескольких приложениях. Чтобы не хардкодить в нем идентификаторы приложений, для этой цели в каждый проект я дополнительно включаю файл local.cpp, имеющий свою реализацию для каждого отдельного приложения.
Секция files mkb-файла:

{
    ...
    adengine.cpp  
    googleadmob.cpp
    inmobi.cpp
    leadbolt_ios.cpp
    leadbolt_android.cpp  
    charboost.cpp
    local.cpp
}

googleadmob.cpp
#include "s3eGoogleAdMob.h"
#include "adengine_constants.h"

int googlead_mob_status = AD_ENGINE_NOT_INITED;

s3eGoogleAdMobId m_Id = 0;

void DestroyAdMobAd()
{
    s3eResult res = s3eGoogleAdMobDestroyAd(m_Id);
}

static int32 onAdMobLoad(void* systemData, void* userData)
{
    googlead_mob_status = AD_ENGINE_LOAD_OK;

    return 0;
}

static int32 onAdMobAction(void* systemData, void* userData)
{
    DestroyAdMobAd();

    googlead_mob_status = AD_ENGINE_INITED;

    return 0;
}

static int32 onAdMobError(void* systemData, void* userData)
{
    DestroyAdMobAd();

    googlead_mob_status = AD_ENGINE_LOAD_ERR;   

    return 0;
}

static int32 onAdMobFiledToLoad(void* systemData, void* userData)
{
    DestroyAdMobAd();

    googlead_mob_status = AD_ENGINE_LOAD_ERR;

    return 0;
}


////////////////////////////////////////////
//////// API
////////////////////////////////////////////
void AdMob_Init(char AppCode[])
{
    s3eGoogleAdMobRegister(S3E_GOOGLEADMOB_CALLBACK_AD_LOADED, onAdMobLoad  , NULL);
    s3eGoogleAdMobRegister(S3E_GOOGLEADMOB_CALLBACK_AD_ACTION, onAdMobAction, NULL);
    s3eGoogleAdMobRegister(S3E_GOOGLEADMOB_CALLBACK_AD_ERROR , onAdMobError , NULL);

    s3eResult res0 = s3eGoogleAdMobSetAdUnitId(AppCode, S3E_TRUE);

    googlead_mob_status = AD_ENGINE_INITED;
}

void AdMob_Load()
{    
    googlead_mob_status = AD_ENGINE_TEMPORARY;

    s3eResult res1 = s3eGoogleAdMobPrepareAd(&m_Id);

    s3eGoogleAdMobAdInfo info;
    s3eGoogleAdMobInspectAd(m_Id, &info);

    s3eResult res2 = s3eGoogleAdMobLoadInterstitialAd(m_Id);
}

void AdMob_Show()
{
    googlead_mob_status = AD_ENGINE_TEMPORARY;
    
    s3eResult res = s3eGoogleAdMobShowAd(m_Id);
}

void AdMob_Terminate()
{       
    DestroyAdMobAd();
    
    googlead_mob_status = AD_ENGINE_TERMINATED;    

    s3eGoogleAdMobUnRegister(S3E_GOOGLEADMOB_CALLBACK_AD_LOADED, onAdMobLoad  );
    s3eGoogleAdMobUnRegister(S3E_GOOGLEADMOB_CALLBACK_AD_ACTION, onAdMobAction);
    s3eGoogleAdMobUnRegister(S3E_GOOGLEADMOB_CALLBACK_AD_ERROR , onAdMobError );    
}

int  AdMob_Status()
{
    return googlead_mob_status;
}

s3eBool AdMob_Avaliable()
{
    return s3eGoogleAdMobAvailable();
}



inmobi.cpp
#include "s3eInMobiAds.h"
#include "s3e.h"
#include "adengine_constants.h"

int InMobi_ad_state = AD_ENGINE_NOT_INITED; 


static int int_request_completed(void *systemData, void *userData)
{    
    InMobi_ad_state = AD_ENGINE_LOAD_OK;
    return S3E_RESULT_SUCCESS;
}

static int int_request_failed(void *systemData, void *userData)
{
    InMobi_ad_state = AD_ENGINE_LOAD_ERR;
    return S3E_RESULT_SUCCESS;
}

static int int_show_adscreen(void *systemData, void *userData) 
{    
    return S3E_RESULT_SUCCESS;
}
static int int_dismiss_adscreen(void *systemData, void *userData)
{
    InMobi_ad_state = AD_ENGINE_INITED;
    return S3E_RESULT_SUCCESS;
}
static int int_leave_application(void *systemData, void *userData) 
{    
    InMobi_ad_state = AD_ENGINE_INITED;
    return S3E_RESULT_SUCCESS;
}
static int int_ad_interacted(void *systemData, void *userData) 
{    
    InMobi_ad_state = AD_ENGINE_INITED;
    return S3E_RESULT_SUCCESS;
}

static int DeviceStateChangeCallback(void *systemData, void *userData)
{
    InMobi_ad_state = AD_ENGINE_INITED;
    return S3E_RESULT_SUCCESS;
}

void InMobi_Init(char appcode[50])
{

    InMobiAdsRegisterIntCallback(INMOBIADS_CALLBACK_INT_REQUEST_COMPLETED, int_request_completed, NULL);
    InMobiAdsRegisterIntCallback(INMOBIADS_CALLBACK_INT_REQUEST_FAILED, int_request_failed, NULL);
    InMobiAdsRegisterIntCallback(INMOBIADS_CALLBACK_INT_SHOW_ADSCREEN, int_show_adscreen, NULL);
    InMobiAdsRegisterIntCallback(INMOBIADS_CALLBACK_INT_DISMISS_ADSCREEN, int_dismiss_adscreen, NULL);
    InMobiAdsRegisterIntCallback(INMOBIADS_CALLBACK_INT_LEAVE_APPLICATION, int_leave_application, NULL);
    InMobiAdsRegisterIntCallback(INMOBIADS_CALLBACK_INT_INTERACTED, int_ad_interacted, NULL);

    s3eDeviceRegister(S3E_DEVICE_UNPAUSE, DeviceStateChangeCallback, NULL);

    inmobi_initialize(appcode);
    inmobi_interstitial_init(appcode);

    InMobi_ad_state = AD_ENGINE_INITED;

}

void InMobi_Load()
{
    InMobi_ad_state = AD_ENGINE_TEMPORARY;
    inmobi_interstitial_load("");     
}

void InMobi_Show()
{
    InMobi_ad_state = AD_ENGINE_TEMPORARY;
    inmobi_interstitial_show();
}

void InMobi_Release()
{
    InMobi_ad_state = AD_ENGINE_TERMINATED;
    inmobi_interstitial_release();

    InMobiAdsUnRegisterIntCallback(INMOBIADS_CALLBACK_INT_REQUEST_COMPLETED, int_request_completed);
    InMobiAdsUnRegisterIntCallback(INMOBIADS_CALLBACK_INT_REQUEST_FAILED, int_request_failed);    
    InMobiAdsUnRegisterIntCallback(INMOBIADS_CALLBACK_INT_SHOW_ADSCREEN, int_show_adscreen);
    InMobiAdsUnRegisterIntCallback(INMOBIADS_CALLBACK_INT_DISMISS_ADSCREEN, int_dismiss_adscreen);
    InMobiAdsUnRegisterIntCallback(INMOBIADS_CALLBACK_INT_LEAVE_APPLICATION, int_leave_application);
    InMobiAdsUnRegisterIntCallback(INMOBIADS_CALLBACK_INT_INTERACTED, int_ad_interacted);

    s3eDeviceUnRegister(S3E_DEVICE_UNPAUSE, DeviceStateChangeCallback);        
}

int InMobi_Status()
{
    return InMobi_ad_state;
}

s3eBool InMobi_Avaliable()
{
    return s3eInMobiAdsAvailable();
}



leadbolt_ios.cpp
#include "AppTrackeriOS.h"
#include "adengine_constants.h"

int ldb_ios_ad_state = AD_ENGINE_NOT_INITED;

void LBD_IOS_DoDestroy()
{
    AppTrackeriOS_destroyModule();
}

int32 LBD_IOS_onModuleFailedEvent(void* system, void* user){
    ldb_ios_ad_state = AD_ENGINE_LOAD_ERR;
    return 0;
}
 
int32 LBD_IOS_onModuleClosedEvent(void* system, void* user){
    LBD_IOS_DoDestroy();
    ldb_ios_ad_state = AD_ENGINE_INITED;
    return 0;
}
 
int32 LBD_IOS_onModuleClickedEvent(void* system, void* user){
    return 0;
}
 
int32 LBD_IOS_onModuleLoadedEvent(void* system, void* user){
    ldb_ios_ad_state = AD_ENGINE_SHOW_OK;
    return 0;
}
int32 LBD_IOS_onModuleCacheEvent(void* system, void* user){
    ldb_ios_ad_state = AD_ENGINE_LOAD_OK;
    return 0;
}
int32 LBD_IOS_onMediaFinishedEvent(void* system, void* user){
    LBD_IOS_DoDestroy();
    ldb_ios_ad_state = AD_ENGINE_INITED;
    return 0;
}

/////////////////////////////////////////////
///////// API
/////////////////////////////////////////////

int LDB_IOS_Status()
{
    return ldb_ios_ad_state;
}

void LDB_IOS_Init(char AppCode[])
{

    AppTrackeriOSRegister(APPTRACKERIOS_MODULEFAILED, &LBD_IOS_onModuleFailedEvent, NULL);
    AppTrackeriOSRegister(APPTRACKERIOS_MODULELOADED, &LBD_IOS_onModuleLoadedEvent, NULL);
    AppTrackeriOSRegister(APPTRACKERIOS_MODULECLOSED, &LBD_IOS_onModuleClosedEvent, NULL);
    AppTrackeriOSRegister(APPTRACKERIOS_MODULECLICKED, &LBD_IOS_onModuleClickedEvent, NULL);
    AppTrackeriOSRegister(APPTRACKERIOS_MODULECACHED, &LBD_IOS_onModuleCacheEvent, NULL);
    AppTrackeriOSRegister(APPTRACKERIOS_MEDIAFINISHED, &LBD_IOS_onMediaFinishedEvent, NULL);
        
    AppTrackeriOS_startSession(AppCode);
        
    ldb_ios_ad_state = AD_ENGINE_INITED;
}

void LDB_IOS_Load()
{
    ldb_ios_ad_state = AD_ENGINE_TEMPORARY;
    AppTrackeriOS_loadModuleToCache("inapp");
}

void LDB_IOS_Show()
{
    ldb_ios_ad_state = AD_ENGINE_TEMPORARY;
    AppTrackeriOS_loadModule("inapp");
}

void LDB_IOS_Terminate()
{
    ldb_ios_ad_state = AD_ENGINE_TERMINATED;
    
    AppTrackeriOS_closeSession();

    AppTrackeriOSUnRegister(APPTRACKERIOS_MODULEFAILED, &LBD_IOS_onModuleFailedEvent);
    AppTrackeriOSUnRegister(APPTRACKERIOS_MODULELOADED, &LBD_IOS_onModuleLoadedEvent);
    AppTrackeriOSUnRegister(APPTRACKERIOS_MODULECLOSED, &LBD_IOS_onModuleClosedEvent);
    AppTrackeriOSUnRegister(APPTRACKERIOS_MODULECLICKED, &LBD_IOS_onModuleClickedEvent);
    AppTrackeriOSUnRegister(APPTRACKERIOS_MODULECACHED, &LBD_IOS_onModuleCacheEvent);
    AppTrackeriOSUnRegister(APPTRACKERIOS_MEDIAFINISHED, &LBD_IOS_onMediaFinishedEvent);
}

s3eBool LDB_IOS_Avaliable()
{
    return AppTrackeriOSAvailable();
}



leadbolt_android.cpp
#include "AppTrackerAndroid.h"
#include "adengine_constants.h"

int ldb_ad_state = AD_ENGINE_NOT_INITED;


void DoDestroy()
{
    destroyModule();
}

int32 onModuleFailedEvent(void* system, void* user){
    ldb_ad_state = AD_ENGINE_LOAD_ERR;
    return 0;
}
 
int32 onModuleClosedEvent(void* system, void* user){
    DoDestroy();
    ldb_ad_state = AD_ENGINE_INITED;
    return 0;
}
 
int32 onModuleClickedEvent(void* system, void* user){
    return 0;
}
 
int32 onModuleLoadedEvent(void* system, void* user){
    ldb_ad_state = AD_ENGINE_SHOW_OK;
    return 0;
}
int32 onModuleCacheEvent(void* system, void* user){
    ldb_ad_state = AD_ENGINE_LOAD_OK;
    return 0;
}
int32 onMediaFinishedEvent(void* system, void* user){
    DoDestroy();
    ldb_ad_state = AD_ENGINE_INITED;
    return 0;
}

/////////////////////////////////////////////
///////// API
/////////////////////////////////////////////

int LDB_Android_Status()
{
    return ldb_ad_state;
}

void LDB_Android_Init(char AppCode[])
{

    AppTrackerAndroidRegister(APPTRACKERANDROID_MODULEFAILED , &onModuleFailedEvent , NULL);
    AppTrackerAndroidRegister(APPTRACKERANDROID_MODULELOADED , &onModuleLoadedEvent , NULL);
    AppTrackerAndroidRegister(APPTRACKERANDROID_MODULECLOSED , &onModuleClosedEvent , NULL);
    AppTrackerAndroidRegister(APPTRACKERANDROID_MODULECLICKED, &onModuleClickedEvent, NULL);
    AppTrackerAndroidRegister(APPTRACKERANDROID_MODULECACHED , &onModuleCacheEvent  , NULL);
    AppTrackerAndroidRegister(APPTRACKERANDROID_MEDIAFINISHED, &onMediaFinishedEvent, NULL);            
    
    startSession(AppCode);
    
    ldb_ad_state = AD_ENGINE_INITED;
}

void LDB_Android_Load()
{
    ldb_ad_state = AD_ENGINE_TEMPORARY;
    loadModuleToCache("inapp", "");
}

void LDB_Android_Show()
{
    ldb_ad_state = AD_ENGINE_TEMPORARY;
    loadModule("inapp", "");
}

void LDB_Android_Terminate()
{
    ldb_ad_state = AD_ENGINE_TERMINATED;
    
    closeSession();

    AppTrackerAndroidUnRegister(APPTRACKERANDROID_MODULEFAILED , &onModuleFailedEvent);
    AppTrackerAndroidUnRegister(APPTRACKERANDROID_MODULELOADED , &onModuleLoadedEvent);
    AppTrackerAndroidUnRegister(APPTRACKERANDROID_MODULECLOSED , &onModuleClosedEvent);
    AppTrackerAndroidUnRegister(APPTRACKERANDROID_MODULECLICKED, &onModuleClickedEvent);
    AppTrackerAndroidUnRegister(APPTRACKERANDROID_MODULECACHED , &onModuleCacheEvent);
    AppTrackerAndroidUnRegister(APPTRACKERANDROID_MEDIAFINISHED, &onMediaFinishedEvent);                        
}

s3eBool LDB_Android_Avaliable()
{
    return AppTrackerAndroidAvailable();
}



charboost.cpp
#include "s3e.h"
#include "s3eChartBoost.h"
#include "adengine_constants.h"

int charboost_ad_state = AD_ENGINE_NOT_INITED;

void RequestCB(void* systemData, void* userData)
{
    charboost_ad_state = AD_ENGINE_LOAD_OK;
}
void AdvertisementClosed(void* System, void* User)
{
    charboost_ad_state = AD_ENGINE_INITED;      
}
void AdvertisementDismissed(void* System, void* User)
{
    charboost_ad_state = AD_ENGINE_INITED;
}

void AdvertisementClicked(void* System, void* User)
{
    charboost_ad_state = AD_ENGINE_INITED;
}

void ErrorCallback(void* System, void* User)
{
    charboost_ad_state = AD_ENGINE_LOAD_ERR;
}


//////////////////////////////
//////////////// API
//////////////////////////////

s3eBool CharBoost_Avaliable()
{       
    return s3eChartBoostAvailable();
}

void CharBoost_Init(char AppCode[], char Signature[])
{       
    s3eChartBoostRegister(S3E_CHARTBOOST_CALLBACK_REQUEST_RESPONSE, (s3eCallback) RequestCB             , NULL);
    s3eChartBoostRegister(S3E_CHARTBOOST_CALLBACK_AD_CLOSED       , (s3eCallback) AdvertisementClosed   , NULL);
    s3eChartBoostRegister(S3E_CHARTBOOST_CALLBACK_AD_DISMISSED    , (s3eCallback) AdvertisementDismissed, NULL);
    s3eChartBoostRegister(S3E_CHARTBOOST_CALLBACK_AD_CLICKED      , (s3eCallback) AdvertisementClicked  , NULL);
    s3eChartBoostRegister(S3E_CHARTBOOST_CALLBACK_ERROR           , (s3eCallback) ErrorCallback         , NULL);

    s3eChartBoostSetAppID(AppCode);
    s3eChartBoostSetAppSignature(Signature);

    s3eChartBoostStartSession();                

    charboost_ad_state = AD_ENGINE_INITED;
}

void CharBoost_Load()
{
    charboost_ad_state = AD_ENGINE_LOAD_OK;
}

void CharBoost_Show()
{
    charboost_ad_state = AD_ENGINE_TEMPORARY;

    s3eChartBoostShowInterstitial(S3E_CHARTBOOST_LOCATION(HOME_SCREEN));
}

void CharBoost_Terminate()
{
    charboost_ad_state = AD_ENGINE_TERMINATED;

    s3eChartBoostUnRegister( S3E_CHARTBOOST_CALLBACK_REQUEST_RESPONSE, (s3eCallback)RequestCB);
    s3eChartBoostUnRegister( S3E_CHARTBOOST_CALLBACK_AD_CLOSED, (s3eCallback)AdvertisementClosed);
    s3eChartBoostUnRegister( S3E_CHARTBOOST_CALLBACK_AD_DISMISSED, (s3eCallback)AdvertisementDismissed);
    s3eChartBoostUnRegister( S3E_CHARTBOOST_CALLBACK_AD_CLICKED,(s3eCallback) AdvertisementClicked);
    s3eChartBoostUnRegister(S3E_CHARTBOOST_CALLBACK_ERROR, (s3eCallback)ErrorCallback);
}

int  CharBoost_Status()
{
    return charboost_ad_state;
}



local.cpp — идентификаторы приложений маскированы
#include 
#include "s3e.h"

void GetInMobiAppAdIdentifier(char code[])
{
        int os = s3eDeviceGetInt(S3E_DEVICE_OS);

        switch (os)
        {
                case S3E_OS_ID_ANDROID:
                        strcpy(code, "********************************");
                        break;
                case S3E_OS_ID_IPHONE:
                        strcpy(code, "********************************");
                        break;
                //      case S3E_OS_ID_WINDOWS:
                //      break;
        }       
}

void GetLDBAppAdIdentifier(char code[])
{
        int os = s3eDeviceGetInt(S3E_DEVICE_OS);

        switch (os)
        {
                case S3E_OS_ID_ANDROID:
                        strcpy(code, "********************************");
                        break;
                case S3E_OS_ID_IPHONE:
                        strcpy(code, "********************************");
                        break;
                //      case S3E_OS_ID_WINDOWS:
                //      break;
        }       
}

void GetCharBoostIdentifiers (char app[], char signature[])
{
        int os = s3eDeviceGetInt(S3E_DEVICE_OS);

        switch (os)
        {
                case S3E_OS_ID_ANDROID:
                        strcpy(app      , "************************");
                        strcpy(signature, "****************************************");
                        break;
                case S3E_OS_ID_IPHONE:
                        strcpy(app      , "************************");
                        strcpy(signature, "****************************************");                        
                        break;
                //      case S3E_OS_ID_WINDOWS:
                //      break;
        }       
}

void GetAdMobAdIdentifier(char code[])
{
        int os = s3eDeviceGetInt(S3E_DEVICE_OS);

        switch (os)
        {
                case S3E_OS_ID_ANDROID:
                        strcpy(code, "ca-app-pub-***************************");
                        break;
                case S3E_OS_ID_IPHONE:
                        strcpy(code, "ca-app-pub-***************************");
                        break;
                //      case S3E_OS_ID_WINDOWS:
                //      break;
        }       
}



AdEngine — API для приложения


Путем тестирования выявлено (и найдено подтверждение на answers.madewithmarmalade.com), что ChartBoost-API фактически не вызывает Callbackoв — это баг. В связи с этим:

  • у нас нет возможности проверить, удачно ли выполнился запрос кеширования баннера;
  • ставим ChartBoost последним по приоритету.


Логика работы движка рекламы такова:
— за некоторое время до предполагаемого момента показа рекламы запрашиванием кеширование баннера до тех пор, пока не произошло кеширование баннера хотя бы по одному из сервисов;
— в момент когда нужно показать рекламу, из тех сервисов, по которым что-то закешировалось, выбираем наиболее приоритетный и показываем его баннер.
— если ни по одному из сервисов ничего не закешировано, то вызываем метод показа баннера ChartBoost. Будет ли показ рекламы в этом случае — тут уж как повезет.

adengine.cpp
#include "local.h"
#include "s3e.h"
#include "inmobi.h"
#include "leadbolt_ios.h"
#include "leadbolt_android.h"
#include "googleadmob.h"
#include "charboost.h"

#define DELAY_MS 1000           // минимальное время между запросами на кеширование рекламы (в миллисекундах)
#define DELAY_4_SHOW_MS 60000   // 1 minute. миниальное время между показами рекламы 

// время последнего запроса на кеширование рекламы (отдельно по каждому сервису)
int64 InMobiPrevTm, LDB_AndroidPrevTm, LDB_IOSPrevTm, CharBoostPrevTm, AdMobPrevTm;
int64 LastShowTm; // время последнего показа рекламы

struct Struct_AdAvaliable
{
        s3eBool InMobi;
        s3eBool LeadBolt_Android;
        s3eBool LeadBolt_IOS;
        s3eBool CharBoost;
        s3eBool AdMob;
};

Struct_AdAvaliable AdAvaliable;

/////////////////////////////////////////////////
/////////////////// API
/////////////////////////////////////////////////
void AdEngine_Init()
{
        char AppCode  [50];
        char Signature[50];

        AdAvaliable.InMobi              = S3E_FALSE;
        AdAvaliable.LeadBolt_Android    = S3E_FALSE;
        AdAvaliable.LeadBolt_IOS        = S3E_FALSE;
        AdAvaliable.CharBoost           = S3E_FALSE;
        AdAvaliable.AdMob               = S3E_FALSE;

        InMobiPrevTm        = 0;
        LDB_AndroidPrevTm   = 0;
        CharBoostPrevTm     = 0;
        AdMobPrevTm         = 0;        
        LDB_IOSPrevTm       = 0;
        
        LastShowTm              = 0;            
        
        AdAvaliable.LeadBolt_Android    = LDB_Android_Avaliable();
        AdAvaliable.LeadBolt_IOS        = LDB_IOS_Avaliable();
        AdAvaliable.InMobi              = InMobi_Avaliable();
        AdAvaliable.CharBoost           = CharBoost_Avaliable();
        AdAvaliable.AdMob               = AdMob_Avaliable();

        if (AdAvaliable.AdMob)
        {               
                GetAdMobAdIdentifier(AppCode);          // подтягиваем идентификатор из файла local.cpp
                AdMob_Init(AppCode);            
        }       
                
        if (AdAvaliable.InMobi)
        {
                GetInMobiAppAdIdentifier(AppCode);      // подтягиваем идентификатор из файла local.cpp
                InMobi_Init(AppCode);
        }
        
        if (AdAvaliable.LeadBolt_Android)
        {
                GetLDBAppAdIdentifier(AppCode);         // подтягиваем идентификатор из файла local.cpp         
                LDB_Android_Init(AppCode);
        }       
        
        if (AdAvaliable.LeadBolt_IOS)
        {
                GetLDBAppAdIdentifier(AppCode);         // подтягиваем идентификатор из файла local.cpp
                LDB_IOS_Init(AppCode);
        }       

        if (AdAvaliable.CharBoost)
        {               
                GetCharBoostIdentifiers(AppCode, Signature); // подтягиваем идентификаторы из файла local.cpp
                CharBoost_Init(AppCode, Signature);     
        }       
}


void AdEngine_Load() // Делаем запросы рекламы 
{                       
        int status;     
        int64 NewTm;

        NewTm = s3eTimerGetMs();        
        
        if (AdAvaliable.AdMob == S3E_TRUE)
        {
                status = AdMob_Status();
                switch (status)
                {
                        case AD_ENGINE_INITED: AdMob_Load(); break;
                        case AD_ENGINE_LOAD_ERR:                                
                                if (NewTm - AdMobPrevTm > DELAY_MS)
                                {
                                        AdMobPrevTm = NewTm;
                                        AdMob_Load();
                                }
                                break;
                        
                        case AD_ENGINE_LOAD_OK: return; // сейчас по приоритету AdMob - первый
                }
        }
        
        if (AdAvaliable.InMobi == S3E_TRUE)
        {
                status = InMobi_Status();
                switch (status)
                {
                case AD_ENGINE_INITED: InMobi_Load(); break;
                case AD_ENGINE_LOAD_ERR:
                        if (NewTm - InMobiPrevTm > DELAY_MS)
                        {
                                InMobiPrevTm = NewTm;
                                InMobi_Load();
                        }
                        break;
                case AD_ENGINE_LOAD_OK: return;
                }
        }

        if (AdAvaliable.LeadBolt_IOS == S3E_TRUE)
        {
                status = LDB_IOS_Status();
                switch (status)
                {
                        case AD_ENGINE_INITED: LDB_IOS_Load(); break;
                        case AD_ENGINE_LOAD_ERR:
                                if (NewTm - LDB_IOSPrevTm > DELAY_MS)
                                {
                                        LDB_IOSPrevTm = NewTm;
                                        LDB_IOS_Load();
                                }
                                break;
                        case AD_ENGINE_LOAD_OK: return; 
                }
        }
                
        if (AdAvaliable.LeadBolt_Android == S3E_TRUE)
        {               
                status = LDB_Android_Status();
                switch (status)
                {
                        case AD_ENGINE_INITED: LDB_Android_Load(); break;
                        case AD_ENGINE_LOAD_ERR:                                                   
                                if (NewTm - LDB_AndroidPrevTm > DELAY_MS)
                                {
                                        LDB_AndroidPrevTm = NewTm;
                                   LDB_Android_Load();
                                }
                                break;
                        case AD_ENGINE_LOAD_OK: return; 
                }               
        }               

        // комментируем AdAvaliable.CharBoost, так как его Callback-и не вызываются
        /*
        if (AdAvaliable.CharBoost)
        {
                status = CharBoost_Status();
                switch (status)
                {
                        case AD_ENGINE_INITED: CharBoost_Load(); break;
                        case AD_ENGINE_LOAD_ERR:
                                if (NewTm - CharBoostPrevTm > DELAY_MS)
                                {
                                        CharBoostPrevTm = NewTm;
                                        CharBoost_Load();
                                }
                                break;
                }
        }
        */      
}


bool AdEngine_Show() // показываем рекламу в порядке приоритета
{               
        int status;     

        int64 NewTm = s3eTimerGetMs();

        // не показываем рекламу чаще чем через некий промежуток времени
        if ((NewTm - LastShowTm) < DELAY_4_SHOW_MS && LastShowTm != 0) return true;  
        
        if (AdAvaliable.AdMob)
        {
                status = AdMob_Status();
                if (status == AD_ENGINE_LOAD_OK)
                {
                        AdMob_Show();
                        LastShowTm = s3eTimerGetMs();
                        return true;
                }
        }       
        
        if (AdAvaliable.LeadBolt_IOS)
        {
                status = LDB_IOS_Status();
                if (status == AD_ENGINE_LOAD_OK)
                {
                        LDB_IOS_Show();
                        LastShowTm = s3eTimerGetMs();
                        return true;
                }
        }
                
        if (AdAvaliable.LeadBolt_Android)
        {       
                status = LDB_Android_Status();
                if (status == AD_ENGINE_LOAD_OK)
                {
                        LDB_Android_Show();
                        LastShowTm = s3eTimerGetMs();
                        return true;
                }               
        }               

        if (AdAvaliable.InMobi)
        {
                status = InMobi_Status();
                if (status == AD_ENGINE_LOAD_OK)
                {
                        InMobi_Show();
                        LastShowTm = s3eTimerGetMs();
                        return true;
                }
        }
        
        if (AdAvaliable.CharBoost)
        {
                CharBoost_Show();
                LastShowTm = s3eTimerGetMs();
                return true;
        }       

        return false;
}

void AdEngine_Terminate()
{               
        if (AdAvaliable.InMobi)
        {
                InMobi_Release();
        }
                
        if (AdAvaliable.LeadBolt_Android)
        {
                LDB_Android_Terminate();
        }

        if (AdAvaliable.LeadBolt_IOS)
        {
                LDB_IOS_Terminate();
        }       

        if (AdAvaliable.AdMob)
        {
                AdMob_Terminate();
        }       

        if (AdAvaliable.CharBoost)
        {
                CharBoost_Terminate();
        }
}



Правки AndroidManifest.xml


Дополняем список activity

    
       
    
    
    
    
    
        


      



        


 
  


 

         
         
         
               



Пополняем список разрешений

    
    


      
        


     
    



Сценарий использования


  • При инициализации приложения вызываем AdEngine_Init ();
  • В момент, когда надо закешировать баннер, вызывается AdEngine_Load (). Технически ничто не мешает вам вызывать его несколько раз, перед тем, как вызвать метод AdEngine_Show (). В методе AdEngine_Load () используется константа DELAY_MS, которая регулирет фактическую частоту вызовов. Также вызов кеширования фактически не произойдет, если по данному сервису баннер уже закеширован. Не рекомендуется вызывать сервисы слишком часто, так они могут заблокировать ваше приложение;
  • В момент, когда надо показать баннер — вызов AdEngine_Show ();
  • Закрытие сессии при закрытии приложения AdEngine_Terminate ().

Опыт использования


Основная территория распространения моих приложений — это Россия, Украина, Казахстан и Белоруссия. Есть и в других странах, но этот процент незначителен. В результате года использования этих сервисов я отказался от Inmobi и Leadbolt по следующим причинам:

  • Leadbolt показываем очень низкий средний показатель стоимости клика (в сравнении, например, с AdMob);
  • Inmobi имеет низкий FillRate и также низкий показатель стоимости клика.


Так как ChartBoost имеет неплохой FillRate, остановился пока на паре AdMob (как основной сервис) и ChartBoost (для случая, когда по AdMob запрос на кеширование вернул ошибку).
В планах есть интеграция с Appodeal — если все пройдет удачно, то обязательно дополню статью.

© Habrahabr.ru