Нативная реклама возвращается: Native Admob и RecyclerView и вкратце о правилах

от автора

С 2015 года ситуация с Admob Native ads практически не изменилась, нативная реклама по прежнему находится в бета-релизе, с лимитированным доступом для издателей. В официальных доках появились новая редакция и некоторые разъяснения по поводу того, каким образом планируется эти самые Native ads внедрять. Мы, в свою очередь, также не сидели сложа руки, копили материал для очередной статьи, и, как только появилось свободное время, слегка расширили функционал библиотеки admobadapter . А именно, реализовали в ней поддержку прокручиваемой нативной рекламы для RecyclerView, так же как мы делали это в прошлой статье для ListView.

Подружим RecyclerView с Native Ads

imageТак как предыдущий опыт показал себя неплохо, то и в случае RecyclerView решили реализовать wrapper (далее обертка) для адаптера. Это позволяет разработчику создавать свой особый и неповторимый адаптер для данных, а мы ему стараемся не мешать в этом начинании. Затем разработчик связывает обертку с адаптером, используя dependency injection, а в RecyclerView передает ссылку на обертку. Таким образом, мы не вмешиваемся в логику адаптера. Обертка при отображении коллекции вычисляет где показывать рекламный блок, а где — исходные данные. Поскольку рассматриваемая часть библиотеки никаких изящных решений и новаторских конструкций не несет, то хотелось бы подробнее остановиться на особенностях и отличиях в реализации прокручиваемой нативной рекламы для RecyclerView от ListView . Базовый класс адаптера для RecyclerView — это RecyclerView.Adapter<~>, а для ListViewBaseAdapter. Если применение ViewHolder-паттерна для ListView — это действо из разряда best-practices, то в декларации RecyclerView.Adapter<VH extends RecyclerView.ViewHolder> параметр-тип ViewHolder является обязательным. Базовый класс требует переопределение методов

abstract int getItemCount() 

возвращает общее количество элементов, хранимых адаптером. По сути аналогичен методу int getCount();

abstract void onBindViewHolder(VH holder, int position) 

выполняет биндинг вьюшки, хранимой в holder к элементу коллекции данных, взятому с индексом position. Этот и следующий методы пришли на «замену» методу View getView(int position, View convertView, ViewGroup parent) из BaseAdapter — ранее, в зависимости от результатов проверки convertView на null, либо создавали новый convertView, либо биндили текущий к соответствующим данным;

abstract VH onCreateViewHolder(ViewGroup parent, int viewType)  

создает и возвращает viewholder нужного типа viewType. Тип контейнера viewType нужно использовать в случае, если элементы вашей коллекции требуется отображать разными способами согласно определенному признаку. В этом случае также следует определить метод

int getItemViewType(int position) 

возвращает тип контейнера для элемента коллекции данных по индексу position. Для BaseAdapter также требовалось определить общее число типов контейнеров в методе getViewTypeCount, в RecyclerView.Adapter<~> это делать уже не надо. На нашем примере getViewTypeCount во wrapper вернет 3: один тип для элементов из исходной коллекции данных (для адаптера), а еще два — для рекламы с контентом и рекламы установки приложений (см. в предыдущей статье). При этом, в адаптере может быть определена своя логика отображения типов контейнеров и обертке об этом ничего знать не нужно. Итак, исходный код лучше тысячи слов 🙂

Развернуть

   public class AdmobRecyclerAdapterWrapper<T, V extends View>         extends RecyclerView.Adapter<ViewWrapper<V>>         implements AdmobFetcher.AdmobListener {         //...          @Override     public void onBindViewHolder(ViewWrapper<V> viewHolder, int position) {         if (viewHolder==null)             return;          switch (viewHolder.getItemViewType()) { //тип контейнера - реклама установки приложения             case VIEW_TYPE_AD_INSTALL: //берем из viewHolder.getView() и используем ее повторно (recycling)                 NativeAppInstallAdView lvi1 = (NativeAppInstallAdView) viewHolder.getView(); //берем рекламу по индексу. getItem в свою очередь возьмет из AdmobFetcher закешированную рекламу, либо даст команду на загрузку новой.                 NativeAppInstallAd ad1 = (NativeAppInstallAd) getItem(position); //биндим рекламу во вьюшку                 AdViewHelper.bindInstallAdView(lvi1, ad1);                 break; //тип контейнера - реклама с контентом             case VIEW_TYPE_AD_CONTENT:                 NativeContentAdView lvi2 = (NativeContentAdView) viewHolder.getView();                 NativeContentAd ad2 = (NativeContentAd) getItem(position);                 AdViewHelper.bindContentAdView(lvi2, ad2);                 break;             default: //трансформируем индекс из системы индексации обертки(с учетом рекламных блоков), в индекс адаптера (исходной коллекции данных)                 int origPos = getOriginalContentPosition(position); //делегируем биндинг адаптеру данных                 mAdapter.onBindViewHolder(viewHolder, origPos);         }     }      @Override     public final ViewWrapper<V> onCreateViewHolder(ViewGroup parent, int viewType) {         switch (viewType) {             case VIEW_TYPE_AD_INSTALL:             case VIEW_TYPE_AD_CONTENT: //создаем новый viewholder в случае если viewType - это любой рекламный блок                 return new ViewWrapper<V>(onCreateItemView(parent, viewType));             default: //иначе делегируем операцию адаптеру данных                 return mAdapter.onCreateViewHolder(parent, viewType);         }     } //просто утилитарный метод для создания вьюшки рекламных контейнеров (см. подробнее в предыдущей статье)     private V onCreateItemView(ViewGroup parent, int viewType) {         switch (viewType) {             case VIEW_TYPE_AD_INSTALL:                 NativeAppInstallAdView lvi1 = getInstallAdView(parent);                 return (V)lvi1;             case VIEW_TYPE_AD_CONTENT:                 NativeContentAdView lvi2 = getContentAdView(parent);                 return (V)lvi2;             default:                 return null;         }     }         //прочие методы были приведены в предыдущей статье, изменились не существенно         //...    } 

Немного бюрократии

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

  1. Разработка и получение одобрения у Вашего аккаунт-менеджера на mockup вашей будущей рекламы, и ее представления в вашем UI, еще до имплементации тестовой версии приложения (Добровольно)
  2. Тестирование шаблонов рекламы в закрытом режиме и с тестовым admob publish id
  3. Получение официального одобрения, что у вас с шаблоном и с правилами размещения все в порядке. Наше предположение, что эта процедура будет доступна непосредственно из вашей консоли разработчика в режиме альфа/бета тестирования, либо из admob-дэшборда
  4. Публикация одобренного приложения

Формальная проверка на внешний вид рекламы в Вашем UI будет выполняться согласно уже опубликованному чек-листу.
Также в developer-guide был добавлен комментарий, суть которого в том, чтобы предостеречь разработчиков от многопоточной загрузки блоков рекламы вызовом метода loadAd в контексте единственного объекта AdLoader. То есть, в основном, остается немногим более двух вариантов 🙂

  • Создавать для каждого вызова loadAd отдельную сущность AdLoader.
  • Перед каждым вызовом loadAd в контексте единственного объекта AdLoader, проверять, что загрузка предыдущего блока была завершена.

Пример реализации второго варианта можно посмотреть, опять же, в нашей библиотеке admobadapter (без претензий на каноничность). Так как более подробно эти методы уже были описаны в предыдущей статье, рассмотрим фрагменты кода AdmobFetcher, отвечающие за синхронизацию loadAd. С этой целью используется флаг

AtomicBoolean lockFetch = new AtomicBoolean(); 

Перед вызовом loadAd проверяем значение этого флага и если он выставлен в true — в данный момент загружается другой блок,- выходим. Иначе — выставляем его в true и пытаемся загрузить блок рекламы

Развернуть

    private synchronized void fetchAd() {         Context context = mContext.get();          if (context != null) {             if(lockFetch.getAndSet(true))                 return;             adLoader.loadAd(getAdRequest());         } else {             mFetchFailCount++;         }     } 

Чтобы позволить загружать блоки рекламы, нам следует выставить флаг в false, это можно сделать подписавшись на завершение loadAd при создании сущности AdLoader, как показано далее

Развернуть

   adLoader = new AdLoader.Builder(mContext.get(), admobUnitId)                 .forAppInstallAd(new NativeAppInstallAd.OnAppInstallAdLoadedListener() {                     @Override                     public void onAppInstallAdLoaded(NativeAppInstallAd appInstallAd) {                         lockFetch.set(false);                         //...                     }                 })                 .forContentAd(new NativeContentAd.OnContentAdLoadedListener() {                     @Override                     public void onContentAdLoaded(NativeContentAd contentAd) {                         lockFetch.set(false);                         //...                     }                 })                 .withAdListener(new AdListener() {                     @Override                     public void onAdFailedToLoad(int errorCode) {                         lockFetch.set(false);                         mFetchFailCount++;                          //...                     }                 }).build(); 

Вместо выводов

Итак, в новой обертке для RecyclerView претерпели изменения:

  1. Создание views для отображения данных/рекламы
  2. Биндинг views к данным/рекламе
  3. Переопределение прочих абстрактных методов базового класса.

Остались в прежнем виде:

  1. Механизм загрузки рекламы с сервера AdMob (класс AdmobFetcher)
  2. Структура рекламных views (xml) и ее заполнение при биндинге
  3. Калькуляция индексов и количества рекламных блоков / блоков с данными
  4. Суть методов getItemCount(), getItemId(int position) и getItemViewType(int position) осталась прежней, но изменились их названия.

Сухой остаток из второй части статьи — официальная документация по Admob native ads продолжает развиваться, как бы намекая нам, что процедура интеграции нативной рекламы в приложение может оказаться нетривиальной и количество затраченного времени будет зависеть не только от нашей скорости разработки, но и от скорости модерации / адекватности персонала Admob/Google.
Будем признательны за статистику, если проголосуете в прикрепленных опросах! По доброй традиции, желаем Вам красивой рекламы в Ваших приложениях!

Ждете ли релиза Admob Native Ads?

Никто ещё не голосовал. Воздержавшихся нет.

Какой контрол чаще используете?

Никто ещё не голосовал. Воздержавшихся нет.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

ссылка на оригинал статьи https://habrahabr.ru/post/282427/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *