开发首屏广告(Android)简述
source link: http://www.androidchina.net/4079.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
作为一个成熟的应用, 必须要有广告. 那么, 如何优雅地开发广告呢? 需要注意一些细节.
本文提供一个简单的示例, 代码仅供参考.
需求:
具体来说, 就是
1. 显示本地存储广告图片, 点击图片, 跳转广告链接, 并提供微信分享功能.
2. 异步下载广告信息, 提高启动速度; 异步下载并保存广告和分享图片, 提高加载速度.
开发过程中, 使用了一些小技巧, 我会详细讲解注意的要点, 包括:
(1) 使用RxAndroid库, 在新线程上做异步下载广告信息.
(2) 使用Picasso库, 异步下载图片(Bitmap)并存储至本地.
(3) 使用原生Handler类, 实现计时器功能, 按秒跳转数字.
(4) 使用WebView视图, 加载广告链接, 并提供分享功能.
1. 下载广告
在欢迎页面中, 启动一个异步线程, 加载广告信息, 提高启动速度, 防止网速过慢导致切换卡顿.
// 异步广告信息
private
void
AsyncCheckInfo() {
// 异步线程处理监听, 在新线程上监听, 发送到主线程
Observable<String> observable = Observable.create(
new
Observable.OnSubscribe<String>() {
@Override
public
void
call(Subscriber<?
super
String> subscriber) {
subscriber.onNext(checkInfo());
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread());
// 成功回调
observable.subscribe(
new
Subscriber<String>() {
@Override
public
void
onCompleted() {
Log.i(TAG,
"onCompleted"
);
}
@Override
public
void
onError(Throwable e) {
}
@Override
public
void
onNext(String s) {
Log.i(TAG,
"onNext"
);
}
});
}
在新线程(newThread)中加载, 完成后发送到主线程(mainThread). 参考.
判断网络, 在有网的时候, 加载广告信息; 在无网的时候, 直接略过.
// 加载广告信息
public
String checkInfo() {
if
(NetUtils.isNetworkConnected(ChunyuApp.getAppContext())) {
UpdateUtils.checkDailyInfo(WelcomeActivity.
this
, mDailyRequestCallback);
return
"Begin to load info."
;
}
else
{
return
"Stop to load info"
;
}
}
在UpdateUtils.checkDailyInfo
中, 解析广告请求的返回值. 如果包含广告信息, 则存储在首选项(SharedPreference)中, 下次启动广告直接读取; 如果不包含广告信息, 则设置无数据标记, 在使用时判定无广告.
最后调用回调接口mDailyRequestCallback
继续处理.
ArrayList<Advert> adverts = version.advert;
if
(adverts.size() >
0
) {
for
(
int
i =
0
; i < adverts.size(); ++i) {
Advert advert = adverts.get(i);
if
(advert.Number ==
1
) {
// Number等于0是广告
PedometerAdManager.getInstance().init(advert);
}
}
}
else
{
Log.e(TAG,
"广告是空"
);
SharedPreferences sp =
PreferenceManager.getDefaultSharedPreferences(ChunyuApp.getAppContext());
sp.edit().putBoolean(WelcomeActivity.FIRST_AD_IS_HAVE_PREFS,
false
).apply();
}
2. 存储图片
已经存储广告信息之后, 即可获得图片下载链接, 为了提高显示速度, 下载图片存储在本地. 因为下载属于网络请求, 需要异步处理, 本文使用Picasso库, 没有发明轮子.
// 日常信息回调
private
final
UpdateUtils.DailyRequestCallback mDailyRequestCallback
=
new
UpdateUtils.DailyRequestCallback() {
@Override
public
void
operationExecutedSuccess() {
if
(mAdManager.getImageUrl() !=
null
&& !mAdManager.getImageUrl().isEmpty())
Picasso.with(WelcomeActivity.
this
).
load(mAdManager.getImageUrl()).into(mAdImageTarget);
if
(mAdManager.getShareIcon() !=
null
&& !mAdManager.getShareIcon().isEmpty())
Picasso.with(WelcomeActivity.
this
).
load(mAdManager.getShareIcon()).into(mAdShareImageTarget);
}
@Override
public
void
operationExecutedFailed() {
Log.e(TAG,
"operationExecutedFailed"
);
}
};
// 广告图片
private
Target mAdImageTarget =
new
Target() {
@Override
public
void
onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
String path = FileUtility.savePic(bitmap);
mPrefs.edit().putString(FIRST_AD_PATH_PREFS, path).apply();
}
@Override
public
void
onBitmapFailed(Drawable errorDrawable) {
}
@Override
public
void
onPrepareLoad(Drawable placeHolderDrawable) {
}
};
// 分享Icon
private
Target mAdShareImageTarget =
new
Target() {
@Override
public
void
onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
String path = FileUtility.savePic(bitmap);
mPrefs.edit().putString(FIRST_AD_SHARE_IMAGE_URL_PREFS, path).apply();
}
@Override
public
void
onBitmapFailed(Drawable errorDrawable) {
}
@Override
public
void
onPrepareLoad(Drawable placeHolderDrawable) {
}
};
在页面暂停时, 移除Picasso的请求线程.
@Override
protected
void
onPause() {
MobclickAgent.onPause(
this
);
handler.removeCallbacks(runnable);
// 停止
Picasso.with(
this
).cancelRequest(mAdImageTarget);
// 停止
Picasso.with(
this
).cancelRequest(mAdShareImageTarget);
// 停止
super
.onPause();
}
注意: 在Picasso中,
Target
是和ImageView控件
弱绑定, 在销毁ImageView时, 会随之销毁. 如果未提供ImageView控件
, 需要手动销毁请求, 如在onPause
中取消. 否则会出现下载异常. 参考.
3. 显示广告
首先Logo页显示LOGO_TIME
秒, 再判断显示引导(首次启动)
或显示广告
.
显示广告是使用存储在首选项(SharedPreference)中的数据, 图片使用本地资源解析, 提高显示速度.
// 显示启动信息
private
void
showLaunchInfo() {
// 显示一段时间的主屏Logo
new
Handler().postDelayed(
this
::showAdInfo, LOGO_TIME);
}
// 显示广告信息
private
void
showAdInfo() {
// 判断是否有广告
if
(mPrefs.getBoolean(FIRST_AD_IS_HAVE_PREFS,
false
)) {
Log.e(TAG,
"包含广告"
);
String path = mPrefs.getString(FIRST_AD_PATH_PREFS,
""
);
if
(!path.isEmpty()) {
int
time = mPrefs.getInt(FIRST_AD_TIME_PREFS,
0
);
Bitmap bitmap = BitmapFactory.decodeFile(path);
Log.e(TAG,
"time: "
+ time);
showAdImage(bitmap, time);
if
(!NetUtils.isNetworkConnected(ChunyuApp.getAppContext())) {
mIvWebImage.setClickable(
false
);
}
}
else
{
gotoOtherActivity();
}
}
else
{
gotoOtherActivity();
}
}
显示的广告使用
上次网络请求的存储数据
, 也可能是本次网络请求的
, 主要取决于在LOGO_TIME
时间中, 是否下载完成启动信息, 并存储至本地.
4. 广告计时器
在广告图片显示时, 提供倒计时器
, 按秒跳时, 提供跳过按钮
直接跳过广告.
// 显示广告
private
void
showAdImage(Bitmap bitmap,
int
time) {
mIvWebImage.setVisibility(View.VISIBLE);
mTvSkip.setVisibility(View.VISIBLE);
mTvSkip.setOnClickListener(v -> gotoOtherActivity());
mIvBackground.setVisibility(View.INVISIBLE);
mIvFirstLogo.setVisibility(View.INVISIBLE);
mIvWebImage.setImageBitmap(bitmap);
mAdTime = time +
2
;
handler.post(runnable);
// 设置读秒
}
// 设置读秒器
private
int
s =
0
;
// 时间Delay
private
final
Handler handler =
new
Handler();
private
final
Runnable runnable =
new
Runnable() {
@Override
public
void
run() {
// handler自带方法实现定时器
try
{
handler.postDelayed(
this
,
1000
);
if
(s <
1
) {
s++;
return
;
}
if
(s <= (mAdTime -
1
)) {
mTvSkip.setText(String.valueOf(
"跳过\n"
+ Integer.toString((mAdTime -
1
) - (s++)) +
"秒"
));
}
// 计时器为0时, 开始跳转
if
(s == mAdTime) {
gotoOtherActivity();
}
}
catch
(Exception e) {
e.printStackTrace();
}
}
};
广告时间额外显示两秒, 提供页面跳转间隔, 前一秒后一秒, 保证广告时间充足.
在广告页跳转
或页面结束
时, 删除计时回调.
// 跳转到现实广告的视图
public
void
gotoShowAdView(View view) {
NV.o(
this
, AdvertisementActivity.
class
);
handler.removeCallbacks(runnable);
finish();
}
@Override
protected
void
onPause() {
MobclickAgent.onPause(
this
);
handler.removeCallbacks(runnable);
// 停止
Picasso.with(
this
).cancelRequest(mAdImageTarget);
// 停止
Picasso.with(
this
).cancelRequest(mAdShareImageTarget);
// 停止
super
.onPause();
}
本文使用
handler
类, 循环调用计时, 必须在离开页面时, 清除runnable
回调. 否则会遗忘线程泄露内存.
5. 链接页面
点击广告图片, 会跳转至广告链接, 根据参数设置全屏或者提供分享功能, 把链接分享至微信. 微信分享需要标题, 内容, 图标(Icon), 其中图片是从服务器下载后预存在本地.
/**
* 广告Activity
* <p>
* Created by wangchenlong on 15/12/2.
*/
public
class
AdvertisementActivity
extends
PActivity {
@SuppressWarnings
(
"unused"
)
private
static
final
String TAG =
"DEBUG-WCL: "
+ AdvertisementActivity.
class
.getSimpleName();
@Bind
(R.id.advertise_pwv_container) PedoWebView mPwvContainer;
@Bind
(R.id.advertise_ll_back_home) LinearLayout mLlBackHome;
@Bind
(R.id.advertise_ll_send_session) LinearLayout mLlSendSession;
@Bind
(R.id.advertise_ll_send_timeline) LinearLayout mLlSendTimeline;
@Bind
(R.id.advertise_ll_action_bar) LinearLayout mLlActionBar;
private
SharedPreferences mPrefs;
private
int
mFlag;
// 判断分享地点
private
static
final
int
WECHAT_SESSION =
0
;
// 微信对话
private
static
final
int
WECHAT_TIMELINE =
1
;
// 朋友圈
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_advertisement);
ButterKnife.bind(
this
);
mPrefs = PreferenceManager.getDefaultSharedPreferences(ChunyuApp.getAppContext());
ActionBar actionBar = getSupportActionBar();
if
(actionBar !=
null
)
actionBar.setDisplayHomeAsUpEnabled(
true
);
// 是否全屏
if
(mPrefs.getBoolean(WelcomeActivity.FIRST_AD_IS_FULL_PREFS,
false
)) {
mLlActionBar.setVisibility(View.GONE);
}
else
{
mLlBackHome.setOnClickListener(v -> {
NV.o(
this
, PedometerActivity.
class
);
finish();
});
// 是否分享
if
(mPrefs.getBoolean(WelcomeActivity.FIRST_AD_IS_SHARE_PREFS,
false
)) {
mLlSendSession.setOnClickListener(v -> {
mFlag = WECHAT_SESSION;
shareWechat();
});
mLlSendTimeline.setOnClickListener(v -> {
mFlag = WECHAT_TIMELINE;
shareWechat();
});
}
else
{
mLlSendSession.setVisibility(View.GONE);
mLlSendTimeline.setVisibility(View.GONE);
}
}
mPwvContainer.loadUrl(mPrefs.getString(WelcomeActivity.FIRST_AD_URL_PREFS,
""
));
}
// 分享到微信
public
void
shareWechat() {
IWXAPI wxapi =
WXAPIFactory.createWXAPI(ChunyuApp.getAppContext(), SNSConst.WX_APP_ID_ONLINE,
true
);
wxapi.registerApp(SNSConst.WX_APP_ID_ONLINE);
WXWebpageObject webpage =
new
WXWebpageObject();
webpage.webpageUrl = mPrefs.getString(WelcomeActivity.FIRST_AD_URL_PREFS,
""
);
WXMediaMessage msg =
new
WXMediaMessage(webpage);
msg.title = mPrefs.getString(WelcomeActivity.FIRST_AD_SHARE_TITLE_PREFS,
""
);
msg.description = mPrefs.getString(WelcomeActivity.FIRST_AD_SHARE_CONTENT_PREFS,
""
);
String path = mPrefs.getString(WelcomeActivity.FIRST_AD_SHARE_IMAGE_URL_PREFS,
""
);
if
(!path.isEmpty()) {
Bitmap bitmap = BitmapFactory.decodeFile(path);
if
(bitmap !=
null
) {
msg.setThumbImage(bitmap);
}
else
{
msg.setThumbImage(BitmapFactory.decodeResource(getResources(), R.drawable.icon));
}
SendMessageToWX.Req req =
new
SendMessageToWX.Req();
req.transaction = String.valueOf(System.currentTimeMillis());
req.message = msg;
req.scene = ((mFlag ==
0
) ?
SendMessageToWX.Req.WXSceneSession : SendMessageToWX.Req.WXSceneTimeline);
wxapi.sendReq(req);
}
}
@Override
public
void
onBackPressed() {
if
(mPwvContainer.canGoBack()) {
mPwvContainer.goBack();
}
else
{
NV.o(
this
, PedometerActivity.
class
);
finish();
}
}
}
调用后退按钮(onBackPressed)
: 在网页跳转多页时, 返回上一页; 在首页时, 退出广告页面, 跳转主页. 微信分享的图标(Icon), 最好使用方形全图, 否则透明部分会被黑色替代, 服务器提供图片时需要注意.
OK, 广告页面开发完成了, 可以开心的赚钱了! Enjoy It.
转载请注明:Android开发中文站 » 开发首屏广告(Android)简述
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK