通用的View统计曝光,可以使用在RecyclerView中 item的曝光统计

539 阅读1分钟

低版本:使用View的onViewAttachedToWindow 和 onViewDetachedFromWindow 高版本:通过onWindowfocusChangedlistener 判断如果view获取focus就开始计时,失去focus就停止计时统计。 已在日活1000万以上的线上产品使用


import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.RelativeLayout;

import androidx.annotation.RequiresApi;

import com.qisi.utils.app.LogUtils;

/**
 * 通过监听view的添加删除和focus,判断是够展示
 */
public class ExRelativeLayout extends RelativeLayout implements View.OnAttachStateChangeListener, ExViewContract {
    private long comeInTime;
    private String pkgName;
    private CustomFocusStateChangeListener focusStateChangeListener;

    public ExRelativeLayout(Context context) {
        super(context);
    }

    public ExRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ExRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    /**
     * view添加到window
     *
     * @param v
     */
    @Override
    public void onViewAttachedToWindow(View v) {
        comeInTime = System.currentTimeMillis();
    }

    /**
     * view 从window移除
     *
     * @param v
     */
    @Override
    public void onViewDetachedFromWindow(View v) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && focusStateChangeListener != null) {
            this.getViewTreeObserver().removeOnWindowFocusChangeListener(focusStateChangeListener);
        }
        this.removeOnAttachStateChangeListener(this);
        collectRootViewData();
    }

    /**
     * 设置要追踪的包名
     *
     * @param pkgName
     */
    @Override
    public void setViewData(String pkgName) {
        if (!TextUtils.isEmpty(pkgName)) {
            this.pkgName = pkgName;
        }
        initListener();
    }

    /**
     * 高版本监听focus,判断是否在前台
     */
    private void initListener() {
        this.addOnAttachStateChangeListener(this);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            this.getViewTreeObserver().addOnWindowFocusChangeListener(focusStateChangeListener = new CustomFocusStateChangeListener());
        }
    }

    /**
     * 计算展示时间,产生展示上报
     */
    private void collectRootViewData() {
        long curTime = System.currentTimeMillis();
        LogUtils.e("trackerlog", pkgName + " hasShownTime " + (curTime - comeInTime));
        CustomAdsReporter.Companion.reportThemeShown(pkgName);
    }

    /**
     * 高版本需要判断view是不是激活状态,如果被遮挡就不应该统计到曝光状态
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    private class CustomFocusStateChangeListener implements ViewTreeObserver.OnWindowFocusChangeListener {

        @Override
        public void onWindowFocusChanged(boolean hasFocus) {
            if (hasFocus) {
                comeInTime = System.currentTimeMillis();
            } else {
                collectRootViewData();
            }
        }

    }

}

收集统计曝光

class CustomAdsReporter {
    companion object {
        private const val THEME_ADS_PKGS = "theme_ads_pkgs"

        /**
         * 展示
         */
        fun reportThemeShown(pkgName: String) {
            if (!isThemeAds(pkgName)) {
                return
            }
            LogUtils.e("trackerlog", "reported shown $pkgName")
        }

        fun reportThemeClick(pkgName: String) {
            if (!isThemeAds(pkgName)) {
                return
            }
            LogUtils.e("trackerlog", "reported click $pkgName")
        }

        private fun isThemeAds(pkgName: String): Boolean {
            LogUtils.e("trackerlog",RemoteConfigHelper.getInstance().getString(THEME_ADS_PKGS))
            return RemoteConfigHelper.getInstance().getString(THEME_ADS_PKGS).contains(pkgName)
        }

        fun registerThemeTracker(pkgName: String, itemView: View) {
            if (itemView is ExViewContract) {
                itemView.setViewData(pkgName)
            }
        }
    }


}