Skip to content

Commit

Permalink
Improve memory management for Native ads #662 (#666)
Browse files Browse the repository at this point in the history
  • Loading branch information
ValentinPostindustria authored Aug 15, 2023
1 parent d554321 commit 220f6af
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,11 @@ class InAppNativeActivity : BaseAdActivity() {

adWrapperView.addView(nativeContainer)

ad.registerViewList(
ad.registerView(
adWrapperView,
listOf(icon, title, image, description, cta),
object : PrebidNativeAdEventListener {
override fun onAdClicked() {}

override fun onAdImpression() {}

override fun onAdExpired() {}
})
null
)
}

override fun onDestroy() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.IdRes
import androidx.constraintlayout.widget.ConstraintLayout
import org.prebid.mobile.PrebidNativeAd
import org.prebid.mobile.PrebidNativeAdEventListener
import org.prebid.mobile.api.data.FetchDemandResult
Expand All @@ -36,7 +31,6 @@ import org.prebid.mobile.renderingtestapp.databinding.FragmentNativeBinding
import org.prebid.mobile.renderingtestapp.plugplay.config.AdConfiguratorDialogFragment
import org.prebid.mobile.renderingtestapp.utils.BaseEvents
import org.prebid.mobile.renderingtestapp.utils.loadImage
import org.prebid.mobile.renderingtestapp.widgets.EventCounterView

open class PpmNativeFragment : AdFragment() {

Expand All @@ -57,7 +51,7 @@ open class PpmNativeFragment : AdFragment() {
if (layoutRes == R.layout.fragment_native) {
layoutInflater.inflate(
getEventButtonViewId(),
binding.contentFragmentNative!!,
binding.contentFragmentNative,
true
)
}
Expand Down Expand Up @@ -100,7 +94,7 @@ open class PpmNativeFragment : AdFragment() {
protected open fun getEventButtonViewId(): Int = R.layout.lyt_native_in_app_events

protected open fun inflateViewContent(nativeAd: PrebidNativeAd) {
nativeAd.registerViewList(
nativeAd.registerView(
binding.adContainer,
listOf(
binding.tvNativeTitle,
Expand All @@ -110,7 +104,7 @@ open class PpmNativeFragment : AdFragment() {
binding.ivNativeMain,
binding.ivNativeIcon
),
createNativeListener()
NativeListener(events)
)

binding.tvNativeTitle.text = nativeAd.title
Expand All @@ -126,27 +120,22 @@ open class PpmNativeFragment : AdFragment() {
}
}

protected fun createNativeListener(): PrebidNativeAdEventListener {
return object : PrebidNativeAdEventListener {
override fun onAdClicked() {
events.clicked(true)
}
protected class NativeListener(private val events: Events) : PrebidNativeAdEventListener {

override fun onAdImpression() {
doInMainThread {
events.impression(true)
}
}
override fun onAdClicked() {
events.clicked(true)
}

override fun onAdExpired() {
events.expired(true)
override fun onAdImpression() {
Handler(Looper.getMainLooper()).post {
events.impression(true)
}
}
}

private fun doInMainThread(function: () -> Unit) {
val handler = Handler(Looper.getMainLooper())
handler.postAtFrontOfQueue(function)
override fun onAdExpired() {
events.expired(true)
}

}

protected class Events(parentView: View) : BaseEvents(parentView) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class PpmNativeLinksFragment : PpmNativeFragment() {
findView(R.id.btnNativeDeeplinkFallback),
findView(R.id.btnNativeLinkUrl)
),
createNativeListener()
NativeListener(events)
)

findView<Button>(R.id.btnNativeLinkRoot).text = nativeAd.callToAction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ package org.prebid.mobile.renderingtestapp.utils
import android.view.View
import androidx.annotation.IdRes
import org.prebid.mobile.renderingtestapp.widgets.EventCounterView
import java.lang.ref.WeakReference

abstract class BaseEvents(private val parentView: View) {
abstract class BaseEvents(parentView: View) {

private val parentViewReference = WeakReference(parentView)

protected fun enable(@IdRes idRes: Int, value: Boolean) {
val event = parentView.findViewById<EventCounterView>(idRes)
val view = parentViewReference.get() ?: return
val event = view.findViewById<EventCounterView>(idRes)
event?.isEnabled = value
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import android.os.Looper;
import android.text.TextUtils;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import java.util.HashMap;
Expand Down Expand Up @@ -63,6 +64,10 @@ public static void clear() {
expiryIntervalMap.clear();
}

/**
* Return cached ad content by cache id.
*/
@Nullable
protected static String get(String cacheId) {
return savedValues.remove(cacheId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ static ClickTracker createAndFire(String url, Context context, ClickTrackerListe

private ClickTracker(String url, Context context, ClickTrackerListener clickTrackerListener) {
this.url = url;
this.context = context;
this.context = context.getApplicationContext();
this.clickTrackerListener = clickTrackerListener;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private ImpressionTracker(String url, VisibilityDetector visibilityDetector, Con
this.url = url;
this.visibilityDetector = visibilityDetector;
this.listener = new ImpressionListener();
this.context = context;
this.context = context.getApplicationContext();
this.impressionTrackerListener = impressionTrackerListener;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;
Expand All @@ -32,6 +33,7 @@
import org.prebid.mobile.rendering.bidding.events.EventsNotifier;
import org.prebid.mobile.rendering.utils.helpers.ExternalViewerUtils;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

Expand All @@ -40,7 +42,6 @@ public class PrebidNativeAd {
private static final String TAG = "PrebidNativeAd";

private boolean impressionIsNotNotified = true;
private boolean clickIsNotNotified = true;

private final ArrayList<NativeTitle> titles = new ArrayList<>();
private final ArrayList<NativeImage> images = new ArrayList<>();
Expand All @@ -52,7 +53,7 @@ public class PrebidNativeAd {
private ArrayList<String> click_trackers;
private VisibilityDetector visibilityDetector;
private boolean expired;
private View registeredView;
private WeakReference<View> registeredView;
private PrebidNativeAdEventListener listener;
private ArrayList<ImpressionTracker> impressionTrackers;
private ArrayList<ClickTracker> clickTrackers;
Expand All @@ -69,24 +70,7 @@ public static PrebidNativeAd create(String cacheId) {
JSONObject adm = new JSONObject(admStr);
JSONArray asset = adm.getJSONArray("assets");
final PrebidNativeAd ad = new PrebidNativeAd();
CacheManager.registerCacheExpiryListener(cacheId, new CacheManager.CacheExpiryListener() {
@Override
public void onCacheExpired() {
if (ad.registeredView == null) {
if (ad.listener != null) {
ad.listener.onAdExpired();
}
ad.expired = true;
if (ad.visibilityDetector != null) {
ad.visibilityDetector.destroy();
ad.visibilityDetector = null;
}
ad.impressionTrackers = null;
ad.clickTrackers = null;
ad.listener = null;
}
}
});
CacheManager.registerCacheExpiryListener(cacheId, new CacheExpireListenerImpl(ad));
for (int i = 0; i < asset.length(); i++) {
JSONObject adObject = asset.getJSONObject(i);
if (adObject.has("title")) {
Expand Down Expand Up @@ -300,11 +284,9 @@ public String getSponsoredBy() {
}

/**
* This API is used to register the view for Ad Events (#onAdClicked(), #onAdImpression, #onAdExpired)
*
* @param view
* @param listener
* @deprecated use {@link PrebidNativeAd#registerView(View, List, PrebidNativeAdEventListener)}
*/
@Deprecated
public boolean registerView(View view, final PrebidNativeAdEventListener listener) {
if (!expired && view != null) {
this.listener = listener;
Expand All @@ -314,7 +296,7 @@ public boolean registerView(View view, final PrebidNativeAdEventListener listene
}

if (imp_trackers != null) {
impressionTrackers = new ArrayList<ImpressionTracker>(imp_trackers.size());
impressionTrackers = new ArrayList<>(imp_trackers.size());
for (String url : imp_trackers) {
ImpressionTracker impressionTracker = ImpressionTracker.create(url, visibilityDetector, view.getContext(), new ImpressionTrackerListener() {
@Override
Expand All @@ -329,28 +311,31 @@ public void onImpressionTrackerFired() {
}
}

this.registeredView = view;
registeredView = new WeakReference<>(view);

view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handleClick(v, listener);
}
});
view.setOnClickListener(v -> handleClick(v, listener));
return true;
}
return false;
}

/**
* This API is used to register a list of views for Ad Events (#onAdClicked(), #onAdImpression, #onAdExpired)
*
* @param container
* @param viewList
* @param listener
* @deprecated use {@link PrebidNativeAd#registerView(View, List, PrebidNativeAdEventListener)}
*/
@Deprecated
public boolean registerViewList(View container, List<View> viewList, final PrebidNativeAdEventListener listener) {
if (container == null || viewList == null || viewList.isEmpty()) {
return registerView(container, viewList, listener);
}

/**
* This API is used to register the view for Ad Events (#onAdClicked(), #onAdImpression, #onAdExpired).
*
* @param container the native ad container used to track impression
* @param clickableViews list of views that should handle click
* @return true if views registered successfully
*/
public boolean registerView(View container, List<View> clickableViews, final PrebidNativeAdEventListener listener) {
if (container == null || clickableViews == null || clickableViews.isEmpty()) {
return false;
}
if (!expired && container != null) {
Expand All @@ -361,7 +346,7 @@ public boolean registerViewList(View container, List<View> viewList, final Prebi
}

if (imp_trackers != null) {
impressionTrackers = new ArrayList<ImpressionTracker>(imp_trackers.size());
impressionTrackers = new ArrayList<>(imp_trackers.size());
for (String url : imp_trackers) {
ImpressionTracker impressionTracker = ImpressionTracker.create(url, visibilityDetector, container.getContext(), new ImpressionTrackerListener() {
@Override
Expand All @@ -376,24 +361,14 @@ public void onImpressionTrackerFired() {
}
}

this.registeredView = container;
registeredView = new WeakReference<>(container);

container.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handleClick(v, listener);
}
});
container.setOnClickListener(v -> handleClick(v, listener));

if (viewList != null && viewList.size() > 0) {
for (View views : viewList) {
if (clickableViews != null && clickableViews.size() > 0) {
for (View views : clickableViews) {
if (views != null) {
views.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handleClick(v, listener);
}
});
views.setOnClickListener(v -> handleClick(v, listener));
}
}
}
Expand Down Expand Up @@ -461,4 +436,36 @@ private void fireClickTrackers(Context context) {
}
}

static class CacheExpireListenerImpl implements CacheManager.CacheExpiryListener {

private PrebidNativeAd ad;

public CacheExpireListenerImpl(PrebidNativeAd ad) {
this.ad = ad;
}

@Override
public void onCacheExpired() {
Log.e(TAG, "onCacheExpired");
WeakReference<View> weakReference = ad.registeredView;
if (weakReference == null) return;

View view = weakReference.get();
if (view != null) return;

if (ad.listener != null) {
ad.listener.onAdExpired();
}
ad.expired = true;
if (ad.visibilityDetector != null) {
ad.visibilityDetector.destroy();
ad.visibilityDetector = null;
}
ad.impressionTrackers = null;
ad.clickTrackers = null;
ad.listener = null;
}

}

}
Loading

0 comments on commit 220f6af

Please sign in to comment.