Add Facebook Audience Network Native Ads in Recycler View

Native Ad is a form of advertising where they matches the look and style of content and view. Unlike other ad formats like banners they mix and match with the content in the view. Therefore they are more effective and overcome banner blindness. We are going to take a look at Facebook Audience Network native ads implementation in Android Recycler View.

 

Create Facebook App

  • Create a new or existing Facebook app
  • Add new product Audience Network
  • Create Native Ad placement for Android platform

Android Studio Setup

  • Create new android studio project
  • Add FAN dependency in build.gradle file
    implementation 'com.facebook.android:audience-network-sdk:5.3.0'

Open AndroidManifest.xml file and add networkSecurityConfig parameter in application tag

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.codeexa.com.nativeads">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:networkSecurityConfig="@xml/network_security_config"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">
        <activity android:name=".nativeads.NativeAdsActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Android Network Security Config

Starting Android P unencrypted HTTP is blocked by default. This will affect the caching of Facebook ads as FAN uses localhost to cache ads. To solve this issue you can whitelist localhost ip in network security config file.

Create a new xml file network_security_config.xml inside res -> xml folder.

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">127.0.0.1</domain>
    </domain-config>
</network-security-config>

Add this configuration in application tag as shown in the code snippet above.

Audience Network Ads Setup

Initialize FAN(Facebook Audience Network) ads in the main activity.

        AudienceNetworkAds.initialize(this);

Layout Files

Lets create our activity layout where we will have RecyclerView to hold our data and native ads.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".nativeads.NativeAdsActivity">

    <android.support.v7.widget.RecyclerView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        android:id="@+id/recyclerView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

native_ads_row_data.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    tools:context=".nativeads.NativeAdsActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        android:contentDescription="NativeAds"
        android:scaleType="fitCenter"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

native_ads_row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:gravity="center"
    android:id="@+id/adContainer"
    tools:context=".nativeads.NativeAdsActivity"
    android:orientation="horizontal">
</LinearLayout>

Setup RecyclerView

package com.codeexa.com.nativeads.nativeads;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import com.codeexa.com.nativeads.R;
import com.facebook.ads.AdSettings;
import com.facebook.ads.AudienceNetworkAds;

import java.util.ArrayList;

public class NativeAdsActivity extends AppCompatActivity {

    private static final String TAG = "CodeExa.Com";

    ArrayList<Object> mTargetData = new ArrayList<>();
    private RecyclerView mRecyclerView;
    private NativeAdsDataAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AudienceNetworkAds.initialize(this);
        AdSettings.addTestDevice("0c0e4ee9-ad7e-456a-b410-e00a5fd9c261");
        initView();
        fetchData();
    }

    private void initView() {
        mRecyclerView = findViewById(R.id.recyclerView);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.setLayoutManager(layoutManager);

        mAdapter = new NativeAdsDataAdapter(this, mTargetData);
        mRecyclerView.setAdapter(mAdapter);
    }

    private void fetchData() {
        for(int i =1; i< 16; i++){
            mTargetData.add("photo"+i);
        }
        mAdapter.notifyDataSetChanged();
        mAdapter.initNativeAds();

    }
}

Setup Native Ads RecyclerView.Adapter

We will use NativeAdsManager to fetch a set of native ads. NativeAdsManager can fetch upto a max of 10 native ads to be displayed in our view.

private static final String TAG = "Example";
    private NativeAdsManager fbNativeManager;
    private final Context mContext;
    private ArrayList<Object> dataList;
    private ArrayList<NativeAd> nativeAd = new ArrayList<>();

    private final int ITEM_TYPE_DATA = 0;
    private final int ITEM_TYPE_AD = 1;

    private final int AD_POSITION = 1;
    private final int AD_POSITION_EVERY_COUNT = 5;

    private static final String  FB_NATIVE_AD_ID = "2143555465960810_2145707915745565";

    public NativeAdsDataAdapter(Context context, ArrayList<Object> mTargetData) {
        dataList = mTargetData;
        mContext = context;
        fbNativeManager = new NativeAdsManager(mContext, FB_NATIVE_AD_ID,3);
    }
    
public void initNativeAds(){
        fbNativeManager.setListener(new NativeAdsManager.Listener() {
            @Override
            public void onAdsLoaded() {
                Log.i(TAG, "onAdsLoaded!" + fbNativeManager.getUniqueNativeAdCount());

                int count = fbNativeManager.getUniqueNativeAdCount();
                for(int i=0; i< count; i ++) {
                    NativeAd ad = fbNativeManager.nextNativeAd();
                    addNativeAd(i, ad);
                }
            }

            @Override
            public void onAdError(AdError adError) {

            }
        });
        fbNativeManager.loadAds();
    }

Once ads are loaded we will add these native ads in our data array.

public void addNativeAd(int i, NativeAd ad) {
        if (ad == null) {
            return;
        }
        if (this.nativeAd.size() > i && this.nativeAd.get(i) != null) {
            this.nativeAd.get(i).unregisterView();
            this.dataList.remove(AD_POSITION + (i * AD_POSITION_EVERY_COUNT));
            this.nativeAd = null;
            this.notifyDataSetChanged();
        }
        this.nativeAd.add(i, ad);

        if(dataList.size() > (AD_POSITION + (i * AD_POSITION_EVERY_COUNT))) {
            dataList.add(AD_POSITION + (i * AD_POSITION_EVERY_COUNT), ad);
            notifyItemInserted(AD_POSITION + (i * AD_POSITION_EVERY_COUNT));
        }
    }

Time to create our viewholders and bind data with the views.

@NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        if(i == ITEM_TYPE_AD)
        {
            View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.native_ads_row, viewGroup, false);
            return new NativeAdViewHolder( v );
        }else {
            View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.native_ads_data_row, viewGroup, false);
            return new TargetViewHolder(v);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int i) {
        if(holder instanceof TargetViewHolder) {
            TargetViewHolder viewHolder = (TargetViewHolder) holder;
            String data = (String) dataList.get(i);
            viewHolder.imageView.setImageResource(getDrawableName(data));
        }else{
            NativeAdViewHolder viewHolder = (NativeAdViewHolder) holder;
            NativeAd ad = (NativeAd) this.dataList.get(i);

            View mNativeView = NativeAdView.render(mContext, ad, NativeAdView.Type.HEIGHT_300);
            viewHolder.nativeAdContainer.removeAllViews();
            viewHolder.nativeAdContainer.addView(mNativeView);
        }
    }

We will create two viewholders for our data and another for native ad view.

    public static class TargetViewHolder extends RecyclerView.ViewHolder {
        ImageView imageView;
        TargetViewHolder(@NonNull View itemView) {
            super(itemView);
            imageView= itemView.findViewById(R.id.imageView);
        }
    }

    public static class NativeAdViewHolder extends RecyclerView.ViewHolder {
        LinearLayout nativeAdContainer;
        NativeAdViewHolder(@NonNull View itemView) {
            super(itemView);
            nativeAdContainer = itemView.findViewById(R.id.adContainer);
        }
    }

We need to differentiate between our data item and native ad item. The item view type returns the type of item being created.

    @Override
    public int getItemViewType(int position) {
        if( dataList.get(position) instanceof NativeAd)
            return ITEM_TYPE_AD;
        else
            return ITEM_TYPE_DATA;
    }

Full source code of adapter file.

package com.codeexa.com.nativeads.nativeads;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.codeexa.com.nativeads.R;
import com.facebook.ads.AdError;
import com.facebook.ads.NativeAd;
import com.facebook.ads.NativeAdView;
import com.facebook.ads.NativeAdsManager;

import java.util.ArrayList;

public class NativeAdsDataAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{

    private static final String TAG = "Example";
    private NativeAdsManager fbNativeManager;
    private final Context mContext;
    private ArrayList<Object> dataList;
    private ArrayList<NativeAd> nativeAd = new ArrayList<>();

    private final int ITEM_TYPE_DATA = 0;
    private final int ITEM_TYPE_AD = 1;

    private final int AD_POSITION = 1;
    private final int AD_POSITION_EVERY_COUNT = 5;

    private static final String  FB_NATIVE_AD_ID = "2143555465960810_2145707915745565";

    public NativeAdsDataAdapter(Context context, ArrayList<Object> mTargetData) {
        dataList = mTargetData;
        mContext = context;
        fbNativeManager = new NativeAdsManager(mContext, FB_NATIVE_AD_ID,3);
    }

    public void initNativeAds(){
        Log.i(TAG, "Size of adapter!" + dataList.size());
        fbNativeManager.setListener(new NativeAdsManager.Listener() {
            @Override
            public void onAdsLoaded() {
                Log.i(TAG, "onAdsLoaded!" + fbNativeManager.getUniqueNativeAdCount());

                int count = fbNativeManager.getUniqueNativeAdCount();
                for(int i=0; i< count; i ++) {
                    NativeAd ad = fbNativeManager.nextNativeAd();
                    addNativeAd(i, ad);
                }
            }

            @Override
            public void onAdError(AdError adError) {

            }
        });
        fbNativeManager.loadAds();
    }

    public void addNativeAd(int i, NativeAd ad) {
        if (ad == null) {
            return;
        }
        if (this.nativeAd.size() > i && this.nativeAd.get(i) != null) {
            this.nativeAd.get(i).unregisterView();
            this.dataList.remove(AD_POSITION + (i * AD_POSITION_EVERY_COUNT));
            this.nativeAd = null;
            this.notifyDataSetChanged();
        }
        this.nativeAd.add(i, ad);

        if(dataList.size() > (AD_POSITION + (i * AD_POSITION_EVERY_COUNT))) {
            dataList.add(AD_POSITION + (i * AD_POSITION_EVERY_COUNT), ad);
            notifyItemInserted(AD_POSITION + (i * AD_POSITION_EVERY_COUNT));
        }
    }


    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        if(i == ITEM_TYPE_AD)
        {
            View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.native_ads_row, viewGroup, false);
            return new NativeAdViewHolder( v );
        }else {
            View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.native_ads_data_row, viewGroup, false);
            return new TargetViewHolder(v);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int i) {
        if(holder instanceof TargetViewHolder) {
            TargetViewHolder viewHolder = (TargetViewHolder) holder;
            String data = (String) dataList.get(i);
            viewHolder.imageView.setImageResource(getDrawableName(data));
        }else{
            NativeAdViewHolder viewHolder = (NativeAdViewHolder) holder;
            NativeAd ad = (NativeAd) this.dataList.get(i);

            View mNativeView = NativeAdView.render(mContext, ad, NativeAdView.Type.HEIGHT_300);
            viewHolder.nativeAdContainer.removeAllViews();
            viewHolder.nativeAdContainer.addView(mNativeView);
        }
    }

    public int getDrawableName(String str){
        return mContext.getResources().getIdentifier(str,"drawable",mContext.getPackageName());
    }

    @Override
    public int getItemCount() {
        if(dataList == null)
            return 0;
        return dataList.size();
    }

    @Override
    public int getItemViewType(int position) {
        if( dataList.get(position) instanceof NativeAd)
            return ITEM_TYPE_AD;
        else
            return ITEM_TYPE_DATA;
    }


    public static class TargetViewHolder extends RecyclerView.ViewHolder {
        ImageView imageView;
        TargetViewHolder(@NonNull View itemView) {
            super(itemView);
            imageView= itemView.findViewById(R.id.imageView);

        }
    }

    public static class NativeAdViewHolder extends RecyclerView.ViewHolder {
        LinearLayout nativeAdContainer;

        NativeAdViewHolder(@NonNull View itemView) {
            super(itemView);
            nativeAdContainer = itemView.findViewById(R.id.adContainer);
        }
    }
}

If you have any suggestion or query do comment below. You can also check my other guide on how to add Facebook Account Kit in Android for user authentication.

4 Comments

  1. BhaumikMistry June 24, 2019
  2. Lucas Sensor September 14, 2019
  3. Ahnaf October 16, 2019
  4. Irfan Tahir July 3, 2020

Leave a Reply