Android中实现首页图片/视频广告

Android中实现首页图片/视频广告

如何实现

在首页把广告下载到本地,然后启动界面显示,提前下载的目的是,显示本地更快。

参考文档:

Android中封装OkHttp,处理网络请求-优快云博客

我的工具类专栏

下载依赖

//给任意控件添加闪光效果
//这里主要用到广告界面,打开广告按钮
//https://github.com/facebook/shimmer-android
implementation 'com.facebook.shimmer:shimmer:0.5.0'

//腾讯官方播放器,可以播放其他地址视频,不是说一定要播放腾讯点播服务的视频
//com.tencent.liteav:LiteAVSDK_Player:播放器核心
//super-player-tencent:更上层的接口和功能,例如:弹幕,滑动进度,滑动调整音量
//https://cloud.tencent.com/document/product/881/20213
implementation project(path: ':super-player-tencent')

实现下载广告数据到本地

private void loadSplashAd() {
    DefaultRepository.getInstance().splashAd()
            .to(autoDisposable(AndroidLifecycleScopeProvider.from(this)))
            .subscribe(new HttpObserver<ListResponse<Ad>>() {
                @Override
                public boolean onFailed(ListResponse<Ad> data, Throwable e) {
                    endRefresh();
                    return super.onFailed(data, e);
                }

                @Override
                public void onSucceeded(ListResponse<Ad> data) {
                    List<Ad> results = data.getData().getData();
                    if (CollectionUtils.isNotEmpty(results)) {
                        downloadAd(results.get(0));
                    } else {
                        //删除本地广告数据
                        deleteSplashAd();
                    }
                }
            });
}

private void downloadAd(Ad data) {
    if (SuperNetworkUtil.isWifiConnected(getHostActivity())) {
        //wifi才下载
        sp.setSplashAd(data);

        //判断文件是否存在,如果存在就不下载
        File targetFile = FileUtil.adFile(getHostActivity(), data.getIcon());
        if (targetFile.exists()) {
            return;
        }

        new Thread(
                new Runnable() {
                    @Override
                    public void run() {

                        try {
                            //FutureTarget会阻塞
                            //所以需要在子线程调用
                            FutureTarget<File> target = Glide.with(getHostActivity().getApplicationContext())
                                    .asFile()
                                    .load(ResourceUtil.resourceUri(data.getIcon()))
                                    .submit();

                            //获取下载的文件
                            File file = target.get();

                            //将文件拷贝到我们需要的位置
                            FileUtils.moveFile(file, targetFile);

                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
        ).start();
    }
}

/**
 * 删除启动界面广告
 */
private void deleteSplashAd() {
    //获取广告信息
    Ad ad = sp.getSplashAd();
    if (ad != null) {
        //删除配置文件
        sp.setSplashAd(null);

        //删除文件
        FileUtils.deleteQuietly(FileUtil.adFile(getHostActivity(), ad.getIcon()));
    }
}

图片广告

布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?android:attr/colorBackground"
    tools:context=".component.ad.activity.AdActivity">

    <!--图片广告-->
    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop" />

    <!--广告控制层-->
    <RelativeLayout
        android:id="@+id/ad_control"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="visible">

        <TextView
            android:id="@+id/preload"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/padding_meddle"
            android:layout_marginTop="@dimen/d50"
            android:layout_marginBottom="@dimen/d50"
            android:background="@drawable/shape_button_transparent_radius_small"
            android:gravity="center"
            android:padding="@dimen/d5"
            android:text="@string/wifi_preload"
            android:textColor="?attr/colorLightWhite"
            android:textSize="@dimen/text_small"
            android:visibility="gone" />

        <!--跳过广告按钮-->
        <TextView
            android:id="@+id/skip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_marginTop="@dimen/d50"
            android:layout_marginRight="@dimen/padding_large"
            android:layout_marginBottom="@dimen/d50"
            android:background="@drawable/shape_button_transparent_radius_small"
            android:gravity="center"
            android:padding="@dimen/padding_meddle"
            android:textColor="?attr/colorLightWhite"
            android:textSize="@dimen/text_meddle"
            app:cornerRadius="@dimen/d30"
            tools:text="@string/skip_ad_count" />

        <com.facebook.shimmer.ShimmerFrameLayout
            android:id="@+id/shimmer"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:layout_marginHorizontal="50dp"
            android:layout_marginTop="@dimen/padding_large"
            android:layout_marginBottom="@dimen/d50">
            <!--打开广告按钮-->
            <TextView
                android:id="@+id/primary"
                android:layout_width="match_parent"
                android:layout_height="@dimen/d60"
                android:background="@drawable/shape_button_transparent_radius_large"
                android:gravity="center"
                android:text="@string/ad_click_tip"
                android:textColor="?attr/colorLightWhite"
                android:textSize="@dimen/text_large"
                app:cornerRadius="@dimen/d30" />
        </com.facebook.shimmer.ShimmerFrameLayout>
    </RelativeLayout>
</RelativeLayout>

逻辑

@Override
protected void initViews() {
    super.initViews();
    //设置沉浸式状态栏
    QMUIStatusBarHelper.translucent(this);
}

@Override
protected void initDatum() {
    super.initDatum();

    //获取广告信息
    data = sp.getSplashAd();
    if (data == null) {
        next();
        return;
    }

    //显示广告信息
    show();

    binding.shimmer.startShimmer();
}

private void next() {
    //创建意图
    Intent intent = new Intent(getHostActivity(), MainActivity.class);
    IntentUtil.cloneIntent(getIntent(), intent);

    if (data != null) {
        //添加广告
        intent.putExtra(Constant.AD, data);
    }

    if (action != null) {
        //要跳转到广告界面
        //先启动主界面的
        //好处是
        //用户在广告界面
        //返回正好看到的主界面
        //这样才符合应用逻辑
        intent.setAction(action);
    }

    startActivity(intent);

    //关闭当前界面
    finish();
}

@Override
protected void initListeners() {
    super.initListeners();
    //跳过广告按钮
    binding.skip.setOnClickListener(v -> {
        //取消倒计时
        cancelCountDown();

        next();
    });

    //点击广告按钮
    binding.primary.setOnClickListener(v -> {
        //取消倒计时
        cancelCountDown();

        action = Constant.ACTION_AD;

        next();
    });
}

private void cancelCountDown() {
    if (adCountDownTimer != null) {
        adCountDownTimer.cancel();
        adCountDownTimer = null;
    }
}

private void show() {
    File targetFile = FileUtil.adFile(getHostActivity(), data.getIcon());
    if (!targetFile.exists()) {
        //记录日志,因为正常来说,只要保存了,文件不能丢失
        next();
        return;
    }

    SuperViewUtil.show(binding.adControl);

    switch (data.getStyle()) {
        case Constant.VALUE0:
            showImageAd(targetFile);
            break;
        case Constant.VALUE10:
            showVideoAd(targetFile);
            break;
    }
}

/**
 * 显示图片广告
 *
 * @param data
 */
private void showImageAd(File data) {
    ImageUtil.showLocalImage(getHostActivity(), binding.image, data.getAbsolutePath());

    startCountDown(5000);
}

实现视频广告

添加控件

<!--视频播放器
VideoView默认没法设置视频填充整个控件,所以不用他-->
<com.tencent.rtmp.ui.TXCloudVideoView
    android:id="@+id/video"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:visibility="gone" />
<!--/播放器-->

逻辑

/**
 * 显示视频广告
 *
 * @param data
 */
private void showVideoAd(File data) {
    SuperViewUtil.show(binding.video);
    SuperViewUtil.show(binding.preload);

    //在要用到的时候在初始化,更节省资源,当然播放器控件也可以在这里动态创建
    //设置播放监听器

    //创建 player 对象
    player = new TXVodPlayer(getHostActivity());

    //静音,当然也可以在界面上添加静音切换按钮
    player.setMute(true);

    //关键 player 对象与界面 view
    player.setPlayerView(binding.video);

    //设置播放监听器
    player.setVodListener(this);

    //铺满
    binding.video.setRenderMode(TXLiveConstants.RENDER_MODE_FULL_FILL_SCREEN);

    //开启硬件加速
    player.enableHardwareDecode(true);

    player.startPlay(data.getAbsolutePath());
}

完整代码

AdActivity.java

package com.ixuea.courses.mymusic.component.ad.activity;

import android.content.Intent;
import android.os.Bundle;
import android.os.CountDownTimer;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import com.ixuea.courses.mymusic.MainActivity;
import com.ixuea.courses.mymusic.R;
import com.ixuea.courses.mymusic.activity.BaseViewModelActivity;
import com.ixuea.courses.mymusic.component.ad.model.Ad;
import com.ixuea.courses.mymusic.databinding.ActivityAdBinding;
import com.ixuea.courses.mymusic.util.Constant;
import com.ixuea.courses.mymusic.util.FileUtil;
import com.ixuea.courses.mymusic.util.ImageUtil;
import com.ixuea.courses.mymusic.util.IntentUtil;
import com.ixuea.superui.util.SuperViewUtil;
import com.qmuiteam.qmui.util.QMUIStatusBarHelper;
import com.tencent.rtmp.ITXVodPlayListener;
import com.tencent.rtmp.TXLiveConstants;
import com.tencent.rtmp.TXVodPlayer;

import java.io.File;

/**
 * 自己实现的广告启动界面,支持图片,视频全屏显示
 * 只是实现这样广告相关流程,没有像真正广告sdk那样有收益
 */
public class AdActivity extends BaseViewModelActivity<ActivityAdBinding> implements ITXVodPlayListener {
    private Ad data;
    private String action;
    private CountDownTimer adCountDownTimer;
    private TXVodPlayer player;

    @Override
    protected void initViews() {
        super.initViews();
        //设置沉浸式状态栏
        QMUIStatusBarHelper.translucent(this);
    }

    @Override
    protected void initDatum() {
        super.initDatum();

        //获取广告信息
        data = sp.getSplashAd();
        if (data == null) {
            next();
            return;
        }

        //显示广告信息
        show();

        binding.shimmer.startShimmer();
    }

    @Override
    protected void initListeners() {
        super.initListeners();
        //跳过广告按钮
        binding.skip.setOnClickListener(v -> {
            //取消倒计时
            cancelCountDown();

            next();

//            AnalysisUtil.onSkipAd(getHostActivity(), sp.getUserId());
        });

        //点击广告按钮
        binding.primary.setOnClickListener(v -> {
            //取消倒计时
            cancelCountDown();

            action = Constant.ACTION_AD;

            next();
        });
    }

    private void next() {
        //创建意图
        Intent intent = new Intent(getHostActivity(), MainActivity.class);
        IntentUtil.cloneIntent(getIntent(), intent);

        if (data != null) {
            //添加广告
            intent.putExtra(Constant.AD, data);
        }

        if (action != null) {
            //要跳转到广告界面
            //先启动主界面的
            //好处是
            //用户在广告界面
            //返回正好看到的主界面
            //这样才符合应用逻辑
            intent.setAction(action);
        }

        startActivity(intent);

        //关闭当前界面
        finish();
    }

    private void show() {
        File targetFile = FileUtil.adFile(getHostActivity(), data.getIcon());
        if (!targetFile.exists()) {
            //记录日志,因为正常来说,只要保存了,文件不能丢失
            next();
            return;
        }

        SuperViewUtil.show(binding.adControl);

        switch (data.getStyle()) {
            case Constant.VALUE0:
                showImageAd(targetFile);
                break;
            case Constant.VALUE10:
                showVideoAd(targetFile);
                break;
        }
    }

    /**
     * 显示视频广告
     *
     * @param data
     */
    private void showVideoAd(File data) {
        SuperViewUtil.show(binding.video);
        SuperViewUtil.show(binding.preload);

        //在要用到的时候在初始化,更节省资源,当然播放器控件也可以在这里动态创建
        //设置播放监听器

        //创建 player 对象
        player = new TXVodPlayer(getHostActivity());

        //关联 player 对象与界面 view
        player.setPlayerView(binding.video);

        //设置播放监听器
        player.setVodListener(this);

        //铺满
        binding.video.setRenderMode(TXLiveConstants.RENDER_MODE_FULL_FILL_SCREEN);

        //开启硬件加速
        player.enableHardwareDecode(true);

        player.startPlay(data.getAbsolutePath());
    }

    /**
     * 显示图片广告
     *
     * @param data
     */
    private void showImageAd(File data) {
        ImageUtil.showLocalImage(getHostActivity(), binding.image, data.getAbsolutePath());

        startCountDown(5000);
    }

    private void startCountDown(int data) {
        //创建倒计时
        adCountDownTimer = new CountDownTimer(data, 1000) {
            /**
             * 每次间隔调用
             *
             * @param millisUntilFinished
             */
            @Override
            public void onTick(long millisUntilFinished) {
                binding.skip.setText(getString(R.string.skip_ad_count, millisUntilFinished / 1000 + 1));
            }

            /**
             * 倒计时完成
             */
            @Override
            public void onFinish() {
                //执行下一步方法
                next();
            }
        };

        //启动定时器
        adCountDownTimer.start();
    }

    /**
     * 结束播放时记得销毁 view 控件,尤其是在下次 startPlay 之前,否则会产生大量的内存泄露以及闪屏问题。
     * <p>
     * 同时,在退出播放界面时,记得一定要调用渲染 View 的onDestroy()函数,否则可能会产生内存泄露和“Receiver not registered”报警。
     * https://cloud.tencent.com/document/product/881/20216
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        cancelCountDown();

        if (player != null) {
            player.stopPlay(true); // true 代表清除最后一帧画面
        }

        binding.video.onDestroy();
    }
    private void cancelCountDown() {
        if (adCountDownTimer != null) {
            adCountDownTimer.cancel();
            adCountDownTimer = null;
        }
    }

    //region 视频播放监听

    /**
     * 播放事件
     *
     * @param txVodPlayer
     * @param event
     * @param bundle
     */
    @Override
    public void onPlayEvent(TXVodPlayer txVodPlayer, int event, Bundle bundle) {
        if (TXLiveConstants.PLAY_EVT_PLAY_BEGIN == event) {
            //视频播放开始,如果有转菊花什么的这个时候该停了

        } else if (event == TXLiveConstants.PLAY_EVT_RCV_FIRST_I_FRAME) {
            // 视频I帧到达,开始播放
        } else if (TXLiveConstants.PLAY_EVT_PLAY_END == event) {
            //视频播放结束

            next();
        } else if (TXLiveConstants.PLAY_EVT_PLAY_PROGRESS == event) {
            if (adCountDownTimer != null) {
                return;
            }

            // 加载进度, 单位是毫秒
//                int duration_ms = bundle.getInt(TXLiveConstants.EVT_PLAYABLE_DURATION_MS);
//                mLoadBar.setProgress(duration_ms);

            // 播放进度, 单位是毫秒
            int progress = bundle.getInt(TXLiveConstants.EVT_PLAY_PROGRESS_MS);

            // 视频总长, 单位是毫秒
            int duration = bundle.getInt(TXLiveConstants.EVT_PLAY_DURATION_MS);

            startCountDown(duration);
        }
    }

    /**
     * 网络状态
     *
     * @param txVodPlayer
     * @param bundle
     */
    @Override
    public void onNetStatus(TXVodPlayer txVodPlayer, Bundle bundle) {

    }
    //endregion

    @Override
    public void onPause() {
        super.onPause();
        pausePlay();
    }

    @Override
    public void onResume() {
        super.onResume();
        startPlay();
    }

    private void startPlay() {
        if (player != null) {
            player.resume();
        }
    }

    private void pausePlay() {
        if (player != null) {
            player.pause();
        }
    }

    @Override
    protected String pageId() {
        return "Ad";
    }

}

activity_ad.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".component.ad.activity.AdActivity">

<!--    图片广告-->
    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"/>

<!--    视频播放器-->
<!--    VideoView默认没法设置视频填充整个空间,所以不用他-->
    <com.tencent.rtmp.ui.TXCloudVideoView
        android:id="@+id/video"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />

<!--    广告控制层-->
    <RelativeLayout
        android:id="@+id/ad_control"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/preload"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/d10"
            android:layout_marginTop="@dimen/d50"
            android:layout_marginBottom="@dimen/d50"
            android:background="@drawable/shape_button_transparent_radius_small"
            android:gravity="center"
            android:padding="@dimen/d5"
            android:text="@string/wifi_preload"
            android:textColor="?attr/colorLightWhite"
            android:textSize="@dimen/s12"
            android:visibility="gone"/>

<!--        跳过广告按钮-->
        <TextView
            android:id="@+id/skip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_marginTop="@dimen/d50"
            android:layout_marginRight="@dimen/d20"
            android:layout_marginBottom="@dimen/d50"
            android:background="@drawable/shape_button_transparent_radius_small"
            android:gravity="center"
            android:padding="@dimen/d10"
            android:textColor="?attr/colorLightWhite"
            android:textSize="@dimen/s14"
            app:cornerRadius="@dimen/d30"
            tools:text="@string/skip_ad_count"/>

        <com.facebook.shimmer.ShimmerFrameLayout
            android:id="@+id/shimmer"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:layout_marginHorizontal="50dp"
            android:layout_marginTop="@dimen/padding_large"
            android:layout_marginBottom="@dimen/d50">
            <!--打开广告按钮-->
            <TextView
                android:id="@+id/primary"
                android:layout_width="match_parent"
                android:layout_height="@dimen/d60"
                android:background="@drawable/shape_button_transparent_radius_large"
                android:gravity="center"
                android:text="@string/ad_click_tip"
                android:textColor="?attr/colorLightWhite"
                android:textSize="@dimen/text_large"
                app:cornerRadius="@dimen/d30" />
        </com.facebook.shimmer.ShimmerFrameLayout>

    </RelativeLayout>

</RelativeLayout>

Ad.java,其中Common是自定义基础类的模型

package com.ixuea.courses.mymusic.component.ad.model;

import android.os.Parcel;

import com.ixuea.courses.mymusic.model.Common;

/**
 * 广告模型
 */
public class Ad extends Common {
    /**
     * 标题
     */
    private String title;

    /**
     * 图片
     */
    private String icon;

    /**
     * 点击广告后跳转的地址
     */
    private String uri;

    /**
     * 类型,0:图片;10:视频;20:应用
     */
    private byte style;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getIcon() {
        return icon;
    }

    public void setIcon(String icon) {
        this.icon = icon;
    }

    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }

    public byte getStyle() {
        return style;
    }

    public void setStyle(byte style) {
        this.style = style;
    }


    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        super.writeToParcel(dest, flags);
        dest.writeString(this.title);
        dest.writeString(this.icon);
        dest.writeString(this.uri);
        dest.writeByte(this.style);
    }

    public Ad() {
    }

    protected Ad(Parcel in) {
        super(in);
        this.title = in.readString();
        this.icon = in.readString();
        this.uri = in.readString();
        this.style = in.readByte();
    }

    public static final Creator<Ad> CREATOR = new Creator<Ad>() {
        @Override
        public Ad createFromParcel(Parcel source) {
            return new Ad(source);
        }

        @Override
        public Ad[] newArray(int size) {
            return new Ad[size];
        }
    };
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值