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


В рамках данной статьи я расскажу как подключить межстраничные (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 <string.h>
#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
<!-- inmobi -->
    <activity
android:name="com.inmobi.androidsdk.IMBrowserActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|smallestScreenSize|screenSize"
android:theme="@android:style/Theme.Translucent.NoTitleBar">
    </activity>   
    <activity android:name="com.inmobi.android.sample.app.AdBannerActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|smallestScreenSize|screenSize" >
    </activity>
    <activity android:name="com.inmobi.android.sample.app.AdInterstitialActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|smallestScreenSize|screenSize">
    </activity>
    <service
android:name="com.inmobi.commons.internal.ActivityRecognitionManager"
android:enabled="true">
    </service>    

<!-- Admob -->
  <activity android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
android:theme="@android:style/Theme.Translucent" />    
<meta-data android:name="com.google.android.gms.games.APP_ID" android:value="@string/gps_app_id" />
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />

<!-- CharBoost -->	
<activity android:name="com.chartboost.sdk.CBImpressionActivity"
   android:excludeFromRecents="true" 
   android:theme="@android:style/Theme.Translucent.NoTitleBar" />

<!-- Leadbolt -->	
<activity
    android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
    android:name="com.apptracker.android.module.AppModuleActivity"
    android:label="ModuleActivity"
    android:theme="@android:style/Theme.Translucent"
    >  
</activity>

<!-- Leadbolt. Required for Google Referrer  --> 
<receiver android:name="com.apptracker.android.track.AppTrackerReceiver" android:exported="true">
	<intent-filter> 
	<action android:name="com.android.vending.INSTALL_REFERRER" /> 
	</intent-filter> 
</receiver>	        


Пополняем список разрешений
<!-- Доступ к интернету и параметрам сети -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

<!-- Использование разрешений локации может повысить доход -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>  
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>    

<!-- Следующие два нужны для Chartboost, чтобы кешировать баннер на storage. Без этого разрешения частота успешных показов существенно падает -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> 
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>


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


  • При инициализации приложения вызываем 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 — если все пройдет удачно, то обязательно дополню статью.
Поделиться с друзьями
-->

Комментарии (0)