> ## Documentation Index
> Fetch the complete documentation index at: https://docs.adrop.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Banner Ads

> Learn how to implement banner ads in your Android app.

## Overview

Banner ads are rectangular ads displayed in a portion of the screen. They can be implemented using both XML layout and code.

### Key Features

* Can be fixed at the top, bottom, or middle of the screen
* Supports image and video ads
* Supports both XML layout and programmatic implementation
* Handle ad events through listeners

<Note>
  Use test unit ID during development: `PUBLIC_TEST_UNIT_ID_320_100`
</Note>

***

## XML Layout Implementation

Define `AdropBanner` in XML layout and load it in Activity or Fragment.

### 1. Define XML Layout

Place `AdropBanner` in a `FrameLayout` or other container.

```xml activity_main.xml theme={null}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Main content -->
    <TextView
        android:id="@+id/content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Main Content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <!-- Banner ad container -->
    <FrameLayout
        android:id="@+id/banner_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">

        <io.adrop.ads.banner.AdropBanner
            android:id="@+id/adrop_banner"
            android:layout_width="320dp"
            android:layout_height="100dp"
            android:layout_gravity="center"
            app:adrop_unit_id="YOUR_UNIT_ID" />

    </FrameLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
```

### 2. Load in Activity

<CodeGroup>
  ```kotlin Kotlin theme={null}
  import android.os.Bundle
  import androidx.appcompat.app.AppCompatActivity
  import io.adrop.ads.banner.AdropBanner
  import io.adrop.ads.banner.AdropBannerListener
  import io.adrop.ads.model.AdropErrorCode

  class MainActivity : AppCompatActivity() {
      private var banner: AdropBanner? = null

      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_main)

          // 1. Find banner view defined in XML
          banner = findViewById(R.id.adrop_banner)

          // 2. Set listener
          banner?.listener = object : AdropBannerListener {
              override fun onAdReceived(banner: AdropBanner) {
                  println("Banner ad received successfully")
              }

              override fun onAdFailedToReceive(banner: AdropBanner, errorCode: AdropErrorCode) {
                  println("Banner ad failed to receive: $errorCode")
              }

              override fun onAdImpression(banner: AdropBanner) {
                  println("Banner ad impression")
              }

              override fun onAdClicked(banner: AdropBanner) {
                  println("Banner ad clicked")
              }
          }

          // 3. Load ad
          banner?.load()
      }

      override fun onDestroy() {
          super.onDestroy()
          // 4. Release memory
          banner?.destroy()
          banner = null
      }
  }
  ```

  ```java Java theme={null}
  import android.os.Bundle;
  import androidx.appcompat.app.AppCompatActivity;
  import io.adrop.ads.banner.AdropBanner;
  import io.adrop.ads.banner.AdropBannerListener;
  import io.adrop.ads.model.AdropErrorCode;
  import org.jetbrains.annotations.NotNull;

  public class MainActivity extends AppCompatActivity {
      private AdropBanner banner;

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);

          // 1. Find banner view defined in XML
          banner = findViewById(R.id.adrop_banner);

          // 2. Set listener
          banner.setListener(new AdropBannerListener() {
              @Override
              public void onAdReceived(@NotNull AdropBanner banner) {
                  System.out.println("Banner ad received successfully");
              }

              @Override
              public void onAdFailedToReceive(@NotNull AdropBanner banner, @NotNull AdropErrorCode errorCode) {
                  System.out.println("Banner ad failed to receive: " + errorCode);
              }

              @Override
              public void onAdImpression(@NotNull AdropBanner banner) {
                  System.out.println("Banner ad impression");
              }

              @Override
              public void onAdClicked(@NotNull AdropBanner banner) {
                  System.out.println("Banner ad clicked");
              }
          });

          // 3. Load ad
          banner.load();
      }

      @Override
      protected void onDestroy() {
          super.onDestroy();
          // 4. Release memory
          if (banner != null) {
              banner.destroy();
              banner = null;
          }
      }
  }
  ```
</CodeGroup>

### XML Attributes

| Attribute               | Description                         | Required |
| ----------------------- | ----------------------------------- | :------: |
| `app:adrop_unit_id`     | Ad unit ID                          |     O    |
| `app:adrop_context_id`  | Context ID for contextual targeting |          |
| `android:layout_width`  | Banner width (dp)                   |     O    |
| `android:layout_height` | Banner height (dp)                  |     O    |

***

## Programmatic Implementation

Create `AdropBanner` instance directly in code and add it to the view.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  import android.os.Bundle
  import android.widget.FrameLayout
  import androidx.appcompat.app.AppCompatActivity
  import io.adrop.ads.banner.AdropBanner
  import io.adrop.ads.banner.AdropBannerListener
  import io.adrop.ads.model.AdropErrorCode

  class MainActivity : AppCompatActivity() {
      private var banner: AdropBanner? = null

      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_main)

          // 1. Create banner instance
          banner = AdropBanner(this, "YOUR_UNIT_ID").apply {
              // 2. Set listener
              listener = object : AdropBannerListener {
                  override fun onAdReceived(banner: AdropBanner) {
                      println("Banner ad received successfully")
                  }

                  override fun onAdFailedToReceive(banner: AdropBanner, errorCode: AdropErrorCode) {
                      println("Banner ad failed to receive: $errorCode")
                  }

                  override fun onAdImpression(banner: AdropBanner) {
                      println("Banner ad impression")
                  }

                  override fun onAdClicked(banner: AdropBanner) {
                      println("Banner ad clicked")
                  }
              }
          }

          // 3. Add to view hierarchy
          val container = findViewById<FrameLayout>(R.id.banner_container)
          val params = FrameLayout.LayoutParams(
              FrameLayout.LayoutParams.MATCH_PARENT,
              FrameLayout.LayoutParams.WRAP_CONTENT
          )
          container.addView(banner, params)

          // 4. Load ad
          banner?.load()
      }

      override fun onDestroy() {
          super.onDestroy()
          // 5. Release memory
          banner?.destroy()
          banner = null
      }
  }
  ```

  ```java Java theme={null}
  import android.os.Bundle;
  import android.widget.FrameLayout;
  import androidx.appcompat.app.AppCompatActivity;
  import io.adrop.ads.banner.AdropBanner;
  import io.adrop.ads.banner.AdropBannerListener;
  import io.adrop.ads.model.AdropErrorCode;
  import org.jetbrains.annotations.NotNull;

  public class MainActivity extends AppCompatActivity {
      private AdropBanner banner;

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);

          // 1. Create banner instance (unitId is required in constructor)
          banner = new AdropBanner(this, "YOUR_UNIT_ID");

          // 2. Set listener
          banner.setListener(new AdropBannerListener() {
              @Override
              public void onAdReceived(@NotNull AdropBanner banner) {
                  System.out.println("Banner ad received successfully");
              }

              @Override
              public void onAdFailedToReceive(@NotNull AdropBanner banner, @NotNull AdropErrorCode errorCode) {
                  System.out.println("Banner ad failed to receive: " + errorCode);
              }

              @Override
              public void onAdImpression(@NotNull AdropBanner banner) {
                  System.out.println("Banner ad impression");
              }

              @Override
              public void onAdClicked(@NotNull AdropBanner banner) {
                  System.out.println("Banner ad clicked");
              }
          });

          // 3. Add to view hierarchy
          FrameLayout container = findViewById(R.id.banner_container);
          FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
              FrameLayout.LayoutParams.MATCH_PARENT,
              FrameLayout.LayoutParams.WRAP_CONTENT
          );
          container.addView(banner, params);

          // 4. Load ad
          banner.load();
      }

      @Override
      protected void onDestroy() {
          super.onDestroy();
          // 5. Release memory
          if (banner != null) {
              banner.destroy();
              banner = null;
          }
      }
  }
  ```
</CodeGroup>

***

## AdropBanner Initialization

### Constructor

<ParamField body="context" type="Context" required>
  Activity or Application Context
</ParamField>

<CodeGroup>
  ```kotlin Kotlin theme={null}
  val banner = AdropBanner(context, "YOUR_UNIT_ID")
  ```

  ```java Java theme={null}
  AdropBanner banner = new AdropBanner(context, "YOUR_UNIT_ID");
  ```
</CodeGroup>

### Set Unit ID

<ParamField body="unitId" type="String" required>
  Unit ID created in Ad Control Console
</ParamField>

<CodeGroup>
  ```kotlin Kotlin theme={null}
  banner.setUnitId("YOUR_UNIT_ID")
  ```

  ```java Java theme={null}
  banner.setUnitId("YOUR_UNIT_ID");
  ```
</CodeGroup>

### Load Ad

Call `load()` method after adding the banner to the screen to request ads.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  banner.load()
  ```

  ```java Java theme={null}
  banner.load();
  ```
</CodeGroup>

<Warning>
  Call `load()` when the banner is visible on screen. Loading when not visible may result in inaccurate impression measurement.
</Warning>

***

## Listener Implementation

The `AdropBannerListener` interface handles ad lifecycle events.

### onAdReceived (Required)

Called when ad is successfully received.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  override fun onAdReceived(banner: AdropBanner) {
      println("Banner ad received successfully")
      // Hide loading indicator, etc.
  }
  ```

  ```java Java theme={null}
  @Override
  public void onAdReceived(@NotNull AdropBanner banner) {
      System.out.println("Banner ad received successfully");
      // Hide loading indicator, etc.
  }
  ```
</CodeGroup>

### onAdFailedToReceive (Required)

Called when ad fails to receive.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  override fun onAdFailedToReceive(banner: AdropBanner, errorCode: AdropErrorCode) {
      println("Banner ad failed to receive: $errorCode")
      // Handle error and show fallback content
  }
  ```

  ```java Java theme={null}
  @Override
  public void onAdFailedToReceive(@NotNull AdropBanner banner, @NotNull AdropErrorCode errorCode) {
      System.out.println("Banner ad failed to receive: " + errorCode);
      // Handle error and show fallback content
  }
  ```
</CodeGroup>

<ParamField body="errorCode" type="AdropErrorCode">
  Code indicating error type. See [Reference](/sdk/android/reference#error-codes) for details.
</ParamField>

### onAdImpression (Optional)

Called when ad is displayed on screen.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  override fun onAdImpression(banner: AdropBanner) {
      println("Banner ad impression")
      // Log impression analytics, etc.
  }
  ```

  ```java Java theme={null}
  @Override
  public void onAdImpression(@NotNull AdropBanner banner) {
      System.out.println("Banner ad impression");
      // Log impression analytics, etc.
  }
  ```
</CodeGroup>

### onAdClicked (Required)

Called when user clicks the ad.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  override fun onAdClicked(banner: AdropBanner) {
      println("Banner ad clicked")
      // Log click analytics, etc.
  }
  ```

  ```java Java theme={null}
  @Override
  public void onAdClicked(@NotNull AdropBanner banner) {
      System.out.println("Banner ad clicked");
      // Log click analytics, etc.
  }
  ```
</CodeGroup>

### onAdVideoStart (Optional)

Called when video playback starts in a video banner ad.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  override fun onAdVideoStart(banner: AdropBanner) {
      println("Banner video started")
  }
  ```

  ```java Java theme={null}
  @Override
  public void onAdVideoStart(@NotNull AdropBanner banner) {
      System.out.println("Banner video started");
  }
  ```
</CodeGroup>

### onAdVideoEnd (Optional)

Called when video playback ends in a video banner ad.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  override fun onAdVideoEnd(banner: AdropBanner) {
      println("Banner video ended")
  }
  ```

  ```java Java theme={null}
  @Override
  public void onAdVideoEnd(@NotNull AdropBanner banner) {
      System.out.println("Banner video ended");
  }
  ```
</CodeGroup>

***

## Context ID Setting

You can set Context ID for [contextual targeting](/sdk/android/targeting#contextual-targeting).

<CodeGroup>
  ```kotlin Kotlin theme={null}
  // contextId is read-only — set via constructor
  val banner = AdropBanner(this, "YOUR_UNIT_ID", "article_123")
  ```

  ```java Java theme={null}
  // contextId is read-only — set via constructor
  AdropBanner banner = new AdropBanner(this, "YOUR_UNIT_ID", "article_123");
  ```
</CodeGroup>

<Note>
  Context ID must be set before loading the ad.
</Note>

***

## Lifecycle Management

### destroy()

Always call `destroy()` to release resources when the banner is no longer used.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  override fun onDestroy() {
      super.onDestroy()
      banner?.destroy()
      banner = null
  }
  ```

  ```java Java theme={null}
  @Override
  protected void onDestroy() {
      super.onDestroy();
      if (banner != null) {
          banner.destroy();
          banner = null;
      }
  }
  ```
</CodeGroup>

<Warning>
  Memory leaks may occur if `destroy()` is not called.
</Warning>

***

## Ad Sizes

Banner ads should have view size matching the unit settings.

### Common Banner Sizes

| Size       | Usage         |
| ---------- | ------------- |
| 320 x 50   | Small banner  |
| 320 x 100  | Medium banner |
| 16:9 ratio | Video banner  |

### Specify Size in XML

```xml theme={null}
<io.adrop.ads.banner.AdropBanner
    android:id="@+id/adrop_banner"
    android:layout_width="320dp"
    android:layout_height="100dp"
    app:adrop_unit_id="YOUR_UNIT_ID" />
```

### Specify Size in Code

<CodeGroup>
  ```kotlin Kotlin theme={null}
  val params = FrameLayout.LayoutParams(
      dpToPx(320), // width
      dpToPx(100)   // height
  )
  container.addView(banner, params)

  // Utility function to convert dp to px
  fun dpToPx(dp: Int): Int {
      val density = resources.displayMetrics.density
      return (dp * density).toInt()
  }
  ```

  ```java Java theme={null}
  FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
      dpToPx(320), // width
      dpToPx(100)   // height
  );
  container.addView(banner, params);

  // Utility function to convert dp to px
  private int dpToPx(int dp) {
      float density = getResources().getDisplayMetrics().density;
      return (int) (dp * density);
  }
  ```
</CodeGroup>

***

## Test Unit IDs

Use the following test unit IDs during development and testing.

| Ad Type             | Test Unit ID                            | Size       |
| ------------------- | --------------------------------------- | ---------- |
| Banner (320x50)     | `PUBLIC_TEST_UNIT_ID_320_50`            | 320 x 50   |
| Banner (320x100)    | `PUBLIC_TEST_UNIT_ID_320_100`           | 320 x 100  |
| Carousel Banner     | `PUBLIC_TEST_UNIT_ID_CAROUSEL`          | Variable   |
| Banner Video (16:9) | `PUBLIC_TEST_UNIT_ID_BANNER_VIDEO_16_9` | 16:9 ratio |
| Banner Video (9:16) | `PUBLIC_TEST_UNIT_ID_BANNER_VIDEO_9_16` | 9:16 ratio |

### Usage Example

<CodeGroup>
  ```kotlin Kotlin theme={null}
  val banner = AdropBanner(this, "PUBLIC_TEST_UNIT_ID_320_100")
  ```

  ```java Java theme={null}
  AdropBanner banner = new AdropBanner(this, "PUBLIC_TEST_UNIT_ID_320_100");
  ```

  ```xml XML theme={null}
  <io.adrop.ads.banner.AdropBanner
      android:id="@+id/adrop_banner"
      android:layout_width="320dp"
      android:layout_height="100dp"
      app:adrop_unit_id="PUBLIC_TEST_UNIT_ID_320_100" />
  ```
</CodeGroup>

***

## Video Playback Control

For video banner ads, you can manually control video playback using `play()` and `pause()`.

### play()

Resumes video playback. Call this when the banner becomes visible again — for example, after a popup is dismissed or the app returns from the background.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  banner?.play()
  ```

  ```java Java theme={null}
  banner.play();
  ```
</CodeGroup>

### pause()

Pauses video playback. Call this when the banner is hidden by a popup or the app enters the background.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  banner?.pause()
  ```

  ```java Java theme={null}
  banner.pause();
  ```
</CodeGroup>

### Example: Background/Foreground Handling

<CodeGroup>
  ```kotlin Kotlin theme={null}
  override fun onResume() {
      super.onResume()
      banner?.play()
  }

  override fun onPause() {
      super.onPause()
      banner?.pause()
  }
  ```

  ```java Java theme={null}
  @Override
  protected void onResume() {
      super.onResume();
      if (banner != null) {
          banner.play();
      }
  }

  @Override
  protected void onPause() {
      super.onPause();
      if (banner != null) {
          banner.pause();
      }
  }
  ```
</CodeGroup>

<Note>
  `play()` and `pause()` only affect video banner ads. They have no effect on image banners.
</Note>

***

## Custom Click Handling

By default, the SDK automatically opens the destination URL when a user clicks the banner ad. If you want to handle click events yourself (e.g., to show a confirmation dialog or track analytics before opening), you can use `useCustomClick`.

<ParamField path="useCustomClick" type="Boolean" default="false">
  When `true`, the SDK will not automatically open the destination URL on click. Only the `onAdClicked` event is fired. You must call `open()` manually to open the destination.
</ParamField>

<CodeGroup>
  ```kotlin Kotlin theme={null}
  // 1. Enable custom click handling
  banner?.useCustomClick = true

  // 2. Handle click in listener
  banner?.listener = object : AdropBannerListener {
      override fun onAdClicked(banner: AdropBanner) {
          // Perform custom logic (e.g., show dialog, log analytics)
          println("Ad clicked — opening destination")
          banner.open()
      }

      // ... other listener methods
  }
  ```

  ```java Java theme={null}
  // 1. Enable custom click handling
  banner.setUseCustomClick(true);

  // 2. Handle click in listener
  banner.setListener(new AdropBannerListener() {
      @Override
      public void onAdClicked(@NotNull AdropBanner banner) {
          // Perform custom logic (e.g., show dialog, log analytics)
          System.out.println("Ad clicked — opening destination");
          banner.open();
      }

      // ... other listener methods
  });
  ```
</CodeGroup>

### open()

Opens the ad's destination URL. You can also pass a custom URL to open instead.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  // Open the default destination URL
  banner?.open()

  // Open a custom URL
  banner?.open("https://example.com")
  ```

  ```java Java theme={null}
  // Open the default destination URL
  banner.open();

  // Open a custom URL
  banner.open("https://example.com");
  ```
</CodeGroup>

<Note>
  `open()` only works when `useCustomClick` is set to `true`. If `useCustomClick` is `false` (default), the SDK handles URL opening automatically.
</Note>

***

## Best Practices

### 1. Memory Management

Always release the banner when Activity or Fragment is destroyed.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  override fun onDestroy() {
      super.onDestroy()
      banner?.destroy()
      banner = null
  }
  ```

  ```java Java theme={null}
  @Override
  protected void onDestroy() {
      super.onDestroy();
      if (banner != null) {
          banner.destroy();
          banner = null;
      }
  }
  ```
</CodeGroup>

### 2. Screen Visibility

Load ads when the banner is visible on screen.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  override fun onResume() {
      super.onResume()
      banner?.load()
  }
  ```

  ```java Java theme={null}
  @Override
  protected void onResume() {
      super.onResume();
      if (banner != null) {
          banner.load();
      }
  }
  ```
</CodeGroup>

### 3. Error Handling

Implement proper error handling when ad load fails.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  override fun onAdFailedToReceive(banner: AdropBanner, errorCode: AdropErrorCode) {
      when (errorCode) {
          AdropErrorCode.ERROR_CODE_NETWORK -> {
              println("Network error: Check connection")
          }
          AdropErrorCode.ERROR_CODE_AD_NO_FILL -> {
              println("No ads available")
          }
          else -> {
              println("Ad load failed: $errorCode")
          }
      }
  }
  ```

  ```java Java theme={null}
  @Override
  public void onAdFailedToReceive(@NotNull AdropBanner banner, @NotNull AdropErrorCode errorCode) {
      if (errorCode == AdropErrorCode.ERROR_CODE_NETWORK) {
          System.out.println("Network error: Check connection");
      } else if (errorCode == AdropErrorCode.ERROR_CODE_AD_NO_FILL) {
          System.out.println("No ads available");
      } else {
          System.out.println("Ad load failed: " + errorCode);
      }
  }
  ```
</CodeGroup>

### 4. Using in RecyclerView

Properly manage banners in ViewHolder when using in RecyclerView.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  class BannerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
      private val banner: AdropBanner = view.findViewById(R.id.adrop_banner)

      fun bind() {
          banner.listener = object : AdropBannerListener {
              // Implement listener
          }
          banner.load()
      }

      fun recycle() {
          banner.destroy()
      }
  }

  // In adapter
  override fun onViewRecycled(holder: BannerViewHolder) {
      holder.recycle()
  }
  ```

  ```java Java theme={null}
  class BannerViewHolder extends RecyclerView.ViewHolder {
      private AdropBanner banner;

      public BannerViewHolder(View view) {
          super(view);
          banner = view.findViewById(R.id.adrop_banner);
      }

      public void bind() {
          banner.setListener(new AdropBannerListener() {
              // Implement listener
          });
          banner.load();
      }

      public void recycle() {
          banner.destroy();
      }
  }

  // In adapter
  @Override
  public void onViewRecycled(BannerViewHolder holder) {
      holder.recycle();
  }
  ```
</CodeGroup>

***

## Complete Example

### Kotlin Example

```kotlin theme={null}
import android.os.Bundle
import android.view.View
import android.widget.FrameLayout
import android.widget.ProgressBar
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import io.adrop.ads.banner.AdropBanner
import io.adrop.ads.banner.AdropBannerListener
import io.adrop.ads.model.AdropErrorCode

class BannerActivity : AppCompatActivity() {
    private var banner: AdropBanner? = null
    private lateinit var progressBar: ProgressBar

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_banner)

        progressBar = findViewById(R.id.progress_bar)
        setupBanner()
    }

    private fun setupBanner() {
        // 1. Create banner instance
        // contextId is set via constructor (read-only property)
        banner = AdropBanner(this, "YOUR_UNIT_ID", "article_123").apply {
            // 2. Set listener
            listener = object : AdropBannerListener {
                override fun onAdReceived(banner: AdropBanner) {
                    println("Banner ad received successfully")
                    progressBar.visibility = View.GONE
                }

                override fun onAdFailedToReceive(banner: AdropBanner, errorCode: AdropErrorCode) {
                    println("Banner ad failed to receive: $errorCode")
                    progressBar.visibility = View.GONE

                    // Handle based on error
                    when (errorCode) {
                        AdropErrorCode.ERROR_CODE_NETWORK -> {
                            showToast("Please check network connection")
                        }
                        AdropErrorCode.ERROR_CODE_AD_NO_FILL -> {
                            println("No ads available")
                        }
                        else -> {
                            showToast("Unable to load ad")
                        }
                    }
                }

                override fun onAdImpression(banner: AdropBanner) {
                    println("Banner ad impression")
                }

                override fun onAdClicked(banner: AdropBanner) {
                    println("Banner ad clicked")
                }
            }
        }

        // 3. Add to view hierarchy
        val container = findViewById<FrameLayout>(R.id.banner_container)
        val params = FrameLayout.LayoutParams(
            dpToPx(320),
            dpToPx(100)
        )
        container.addView(banner, params)

        // 4. Load ad
        progressBar.visibility = View.VISIBLE
        banner?.load()
    }

    private fun dpToPx(dp: Int): Int {
        val density = resources.displayMetrics.density
        return (dp * density).toInt()
    }

    private fun showToast(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }

    override fun onDestroy() {
        super.onDestroy()
        // 5. Release memory
        banner?.destroy()
        banner = null
    }
}
```

### Java Example

```java theme={null}
import android.os.Bundle;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import io.adrop.ads.banner.AdropBanner;
import io.adrop.ads.banner.AdropBannerListener;
import io.adrop.ads.model.AdropErrorCode;
import org.jetbrains.annotations.NotNull;

public class BannerActivity extends AppCompatActivity {
    private AdropBanner banner;
    private ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_banner);

        progressBar = findViewById(R.id.progress_bar);
        setupBanner();
    }

    private void setupBanner() {
        // 1. Create banner instance (with context ID)
        banner = new AdropBanner(this, "YOUR_UNIT_ID", "article_123");

        // 2. Set listener
        banner.setListener(new AdropBannerListener() {
            @Override
            public void onAdReceived(@NotNull AdropBanner banner) {
                System.out.println("Banner ad received successfully");
                progressBar.setVisibility(View.GONE);
            }

            @Override
            public void onAdFailedToReceive(@NotNull AdropBanner banner, @NotNull AdropErrorCode errorCode) {
                System.out.println("Banner ad failed to receive: " + errorCode);
                progressBar.setVisibility(View.GONE);

                // Handle based on error
                if (errorCode == AdropErrorCode.ERROR_CODE_NETWORK) {
                    showToast("Please check network connection");
                } else if (errorCode == AdropErrorCode.ERROR_CODE_AD_NO_FILL) {
                    System.out.println("No ads available");
                } else {
                    showToast("Unable to load ad");
                }
            }

            @Override
            public void onAdImpression(@NotNull AdropBanner banner) {
                System.out.println("Banner ad impression");
            }

            @Override
            public void onAdClicked(@NotNull AdropBanner banner) {
                System.out.println("Banner ad clicked");
            }
        });

        // 3. Add to view hierarchy
        FrameLayout container = findViewById(R.id.banner_container);
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
            dpToPx(320),
            dpToPx(100)
        );
        container.addView(banner, params);

        // 4. Load ad
        progressBar.setVisibility(View.VISIBLE);
        banner.load();
    }

    private int dpToPx(int dp) {
        float density = getResources().getDisplayMetrics().density;
        return (int) (dp * density);
    }

    private void showToast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 5. Release memory
        if (banner != null) {
            banner.destroy();
            banner = null;
        }
    }
}
```

### XML Layout

```xml activity_banner.xml theme={null}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Banner Ad Example"
        android:textSize="24sp"
        android:textStyle="bold"
        android:layout_marginTop="32dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <ProgressBar
        android:id="@+id/progress_bar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <FrameLayout
        android:id="@+id/banner_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

***

## Batch Loading (loads)

Use `AdropBanner.loads()` to request multiple banner ads in a single batched call. This is useful for building carousels, paginated feeds, or prefetching a pool of ads.

### Signature

<CodeGroup>
  ```kotlin Kotlin theme={null}
  AdropBanner.loads(
      context: Context,
      unitId: String,
      contextId: String? = null,
      listener: AdropBannerListener,
  )
  ```

  ```java Java theme={null}
  AdropBanner.loads(context, "YOUR_UNIT_ID", null, listener);
  ```
</CodeGroup>

### Usage

<CodeGroup>
  ```kotlin Kotlin theme={null}
  AdropBanner.loads(
      context = this,
      unitId = "YOUR_UNIT_ID",
      contextId = null,
      listener = object : AdropBannerListener {
          // Batch success — deliver up to 5 ready-to-use banners
          override fun onAdsReceived(banners: List<AdropBanner>) {
              banners.forEach { banner ->
                  // Attach to your view hierarchy (ViewPager2, RecyclerView, etc.)
                  container.addView(banner)
              }
          }

          // Batch failure
          override fun onAdsFailedToReceive(errorCode: AdropErrorCode) {
              println("Batch load failed: $errorCode")
          }

          // Single-load callbacks are NOT invoked on this path.
          override fun onAdReceived(banner: AdropBanner) { /* batch path */ }
          override fun onAdFailedToReceive(banner: AdropBanner, errorCode: AdropErrorCode) {}

          // Click / impression / video callbacks still fire per instance.
          override fun onAdClicked(banner: AdropBanner) {
              println("Banner clicked: ${banner.creativeId}")
          }

          override fun onAdImpression(banner: AdropBanner) {
              println("Banner impression: ${banner.creativeId}")
          }
      },
  )
  ```

  ```java Java theme={null}
  AdropBanner.loads(this, "YOUR_UNIT_ID", null, new AdropBannerListener() {
      @Override
      public void onAdsReceived(@NotNull List<AdropBanner> banners) {
          for (AdropBanner banner : banners) {
              container.addView(banner);
          }
      }

      @Override
      public void onAdsFailedToReceive(@NotNull AdropErrorCode errorCode) {
          System.out.println("Batch load failed: " + errorCode);
      }

      // Required single-load callbacks — unused on the batch path.
      @Override
      public void onAdReceived(@NotNull AdropBanner banner) { }

      @Override
      public void onAdFailedToReceive(@NotNull AdropBanner banner, @NotNull AdropErrorCode errorCode) { }

      @Override
      public void onAdClicked(@NotNull AdropBanner banner) {
          System.out.println("Banner clicked: " + banner.getCreativeId());
      }

      @Override
      public void onAdImpression(@NotNull AdropBanner banner) {
          System.out.println("Banner impression: " + banner.getCreativeId());
      }
  });
  ```
</CodeGroup>

### Listener Callbacks

| Callback                                                             | When it fires                                                                                  |
| -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `onAdsReceived(banners)`                                             | Batch delivered. Each `AdropBanner` already has the listener attached and its ad data applied. |
| `onAdsFailedToReceive(errorCode)`                                    | Request rejected, network failure, or no fillable ads were returned (`ERROR_CODE_AD_NO_FILL`). |
| `onAdClicked` / `onAdImpression` / `onAdVideoStart` / `onAdVideoEnd` | Fire on each returned banner just like a single `load()`.                                      |

<Warning>
  The singular `onAdReceived` / `onAdFailedToReceive` callbacks are **not** invoked on the `loads()` path. Use `onAdsReceived` / `onAdsFailedToReceive` as the sole batch signals.
</Warning>

### Constraints

* **Maximum 5 ads per call.** If the server returns more, only the first 5 are delivered.
* **Backfill is not applied.** If no direct ads fill, `onAdsFailedToReceive` is called with `ERROR_CODE_AD_NO_FILL` regardless of backfill configuration.
* **Do not call `destroy()` inside `onAdsReceived`.** Attach each banner to a view hierarchy first, then `destroy()` on the parent `Activity.onDestroy()` (or equivalent).
* `onAdsReceived` fires as soon as ad data is applied, matching the single `load()` contract — the creative paints shortly after attachment.

***

## Backfill Ads

When backfill ads are enabled, backfill ads are automatically loaded when no direct ads are available. You can check if an ad is a backfill ad using the `isBackfilled` property.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  banner.listener = object : AdropBannerListener {
      override fun onAdReceived(banner: AdropBanner) {
          if (banner.isBackfilled) {
              println("Backfill ad loaded")
          } else {
              println("Direct ad loaded")
          }
      }

      override fun onAdFailedToReceive(banner: AdropBanner, errorCode: AdropErrorCode) {
          when (errorCode) {
              AdropErrorCode.ERROR_CODE_AD_NO_FILL -> {
                  println("No direct ad, requesting backfill...")
              }
              AdropErrorCode.ERROR_CODE_AD_BACKFILL_NO_FILL -> {
                  println("Backfill ad also not available")
              }
              else -> {
                  println("Ad load failed: $errorCode")
              }
          }
      }
  }
  ```

  ```java Java theme={null}
  banner.setListener(new AdropBannerListener() {
      @Override
      public void onAdReceived(@NotNull AdropBanner banner) {
          if (banner.isBackfilled()) {
              System.out.println("Backfill ad loaded");
          } else {
              System.out.println("Direct ad loaded");
          }
      }

      @Override
      public void onAdFailedToReceive(@NotNull AdropBanner banner, @NotNull AdropErrorCode errorCode) {
          if (errorCode == AdropErrorCode.ERROR_CODE_AD_NO_FILL) {
              System.out.println("No direct ad, requesting backfill...");
          } else if (errorCode == AdropErrorCode.ERROR_CODE_AD_BACKFILL_NO_FILL) {
              System.out.println("Backfill ad also not available");
          } else {
              System.out.println("Ad load failed: " + errorCode);
          }
      }
  });
  ```
</CodeGroup>

<Note>
  To use backfill ads, you must add the `io.adrop:adrop-ads-backfill` dependency.
</Note>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Native Ads" href="/sdk/android/native">
    Implement customizable native ads that match your UI
  </Card>

  <Card title="Interstitial Ads" href="/sdk/android/interstitial">
    Implement full-screen interstitial ads
  </Card>

  <Card title="Targeting" href="/sdk/android/targeting">
    Set up user attributes and contextual targeting
  </Card>

  <Card title="Reference" href="/sdk/android/reference">
    View classes, listeners, and error codes
  </Card>
</CardGroup>
