AFS Native Implementation for Android

Prerequisites

This implementation guide assumes you are familiar with the following:

Import the AFS Native SDK

Google Play Services compatibility

The AFS Native SDK 1.0.0 has a dependency on play-services-basement:12.0.1. Including multiple different versions of Google Play Services in your build is not supported, so if your app has dependencies on other versions of Google Play Services, you must update those dependencies to match. For example, including both com.google.ads.afsn:afs-native:1.0.0 and com.google.android.gms:play-services-ads:11.8.0 in your app may cause runtime crashes or prevent ads from being successfully loaded by the AFS Native SDK. Update your AdMob dependency to com.google.android.gms:play-services-ads:12.0.1 to prevent these problems and ensure both the AFS Native SDK and the AdMob SDK work as expected.

You don't have to explicitly include a dependency on play-services for the AFS Native SDK to work.

Add the SDK

To add the AFS Native SDK to your app, do the following:

Open the build.gradle file inside your application module directory. Add a new build rule under dependencies for the latest version of the SDK:

dependencies {
  implementation 'com.google.ads.afsn:afs-native:1.0.0'
}

Ensure that your top-level build.gradle contains a reference to the google() repo or to maven { url "https://maven.google.com" }.

Follow these instructions to include the Google Play Standalone Version Matcher Plugin in your project. Applying this plugin causes a gradle build error when the AFS Native SDK is used with an incompatible version of Google Play Services instead of allowing the app to build but potentially causing runtime crashes. Or, apply the failOnVersionConflict() ResolutionStrategy to your project to cause a build error when incompatible versions of Google Play Services are used in your project. Save the changes and click Sync Project with Gradle Files in the toolbar.

Include network permissions in your Android Manifest

In your application's AndroidManifest.xml, make sure you have the INTERNET and ACCESS_NETWORK_STATE permissions. Your manifest should look something like this:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.networkusage"
    ...>
    ...
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application>...</application>
</manifest>

Classes

To serve AFS native ads in your app, implement the following classes:

SearchAdController

  • This class is responsible for asynchronously requesting ads, caching and retrieving ads, and rendering ads.
  • Each ad context needs a separate SearchAdController; for example, if you have a screen which shows ads alongside a list of search results and another screen which shows ads alongside details for a specific product, you should create two separate instances of SearchAdController, one for each case.
  • The constructor needs to be provided your web property code (publisher id), style ID to apply to returned ads, and SearchAdOptions. The Context provided in the constructor must be the Activity which contains the SearchAdController and where you will place the ad View.
  • Call loadAds to indicate a new user search and initiate an asynchronous ad request. Any ads loaded from previous calls to loadAds are cleared from the internal ad cache when a new call is made.
  • Create a View with createAdView to display ad creatives.
  • Once ads have loaded, call populateAdView with a View previously generated with createAdView to render a cached ad into that View. In addition to the View which is to be populated, provide an adKey, an arbitrary string to uniquely identify the ad. This associates the specific ad creative returned from the cache with that adKey, so when the same adKey is passed to a future call to populateAdView, the same ad will be returned. For example, if populateAdView is called for the first time with adKey="keyA" and renders an ad for hiking boots, each subsequent call to populateAdView with adKey="keyA" will populate the same ad for hiking boots. (Making a new call to loadAds clears all cached ads and associated ad keys.)

SearchAdOptions

  • Pass this object to the SearchAdController constructor to customize how ads are requested and displayed. Call build() on a SearchAdOptions.Builder to create a SearchAdOptions object.

View

  • Create a View object to hold ads by calling createAdView() on the SearchAdController. Displays at most one ad at a time, but the same View can be recycled to display different ads over time.

SearchAdRequest

  • Call the loadAds method on the SearchAdController with a SearchAdRequest to initiate an asynchronous ad request. Call build() on a SearchAdRequest.Builder to create a SearchAdRequest object.

AdListener

  • Implement this interface and pass it to the SearchAdController constructor to register callbacks for several states.
  • Note: AdListener callbacks will not be called on a canceled request (a call to loadAds that was preempted by another call to loadAds before the first call resolved).

Example implementation

The example below demonstrates creating a SearchAdController in a sample Activity.

//  MainActivity.java implementation
//  (MainActivity is a subclass of Activity)

SearchAdController adController;
// adContainer where we will place our ads in this example.
ViewGroup adContainer;

protected void onCreate(Bundle bundle){
  super.onCreate(bundle);
  adContainer = (ViewGroup) findById(...);
  // Specify ad options (not required).
  SearchAdOptions.Builder adOptionsBuilder = new SearchAdOptions.Builder();
  adOptionsBuilder.setAdType(SearchAdOptions.AD_TYPE_TEXT);
  adOptionsBuilder.setPrefetch(true);
  adOptionsBuilder.setNumAdsRequested(3);
  // Provide a callback to trigger when ads are loaded.
  AdListener adListener = new AdListener() {
    public void onAdLoaded() {
      createAndShowAd();
    }
  });
  // Instantiate the SearchAdController.
  adController = new SearchAdController(this, "your-client-id", "your-style-id",
                                        adOptionsBuilder.build(), adListener);
}

When the user initiates a query, create a SearchAdRequest and call loadAds on the SearchAdController to start an asynchronous ad request.

// Create the request.
SearchAdRequest.Builder requestBuilder = new SearchAdRequest.Builder();
requestBuilder.setQuery("user query here");
// Load the ads.
adController.loadAds(requestBuilder.build());

Implement your onAdLoaded callback to populate a loaded ad into an ad view.

private void createAndShowAd() {
  // Create a new view that will contain the ad.
  View adView = adController.createAdView();
  // Attach the new view to the view hierarchy.
  adContainer.addView(adView);
  // Display the ad inside the adView. We need to provide an adKey to
  // indicate which ad is to be displayed in the adView. In this example, 
  // since we only have one ad, we can provide any constant string. However, 
  // if you intend to display multiple ads, each ad you wish to display
  // should be given a unique adKey of your choosing.
  adController.populateAdView(adView, "demoAd");
}

An ad related to the given query will now appear in the adView.

Investigating errors

The SearchAdController requires an AdListener object with the onAdLoaded() method to notify your app that ads are ready to display. You should also implement the onAdFailedToLoad() method so you can detect and correct errors. For example, you might use the following AdListener to debug your implementation:

AdListener adListener = new AdListener() {
    public void onAdLoaded() {
        // Called when an ad is loaded.
        Toast.makeText(MainActivity.this, "Ad Loaded",
                Toast.LENGTH_SHORT).show();
        Log.d(MainActivity.class.getSimpleName(), "Ad Loaded");
    }

    public void onAdLeftApplication() {
        // Called when an ad leaves the application
        // (to go to the browser for example).
        Toast.makeText(MainActivity.this, "Ad Left Application",
                Toast.LENGTH_SHORT).show();
        Log.d(MainActivity.class.getSimpleName(), "Ad Left Application");
    }

    @Override
    public void onAdFailedToLoad(int errorCode) {
        // Called when an ad request failed.
        Toast.makeText(MainActivity.this, "Ad Failed to Load: " + errorCode,
                Toast.LENGTH_SHORT).show();
        Log.e(MainActivity.class.getSimpleName(), "Ad Failed to Load: " +
                errorCode);
    }
});

Constants used in the onAdFailedToLoad() callback method are defined in the AdListener.