Android中实现首页图片/视频广告
如何实现
在首页把广告下载到本地,然后启动界面显示,提前下载的目的是,显示本地更快。
参考文档:
我的工具类专栏
下载依赖
//给任意控件添加闪光效果
//这里主要用到广告界面,打开广告按钮
//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];
}
};
}