Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve memory management for Native ads #666

Merged
merged 1 commit into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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