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