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