Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Sign in / Register
Toggle navigation
A
appzxhy
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wanglei
appzxhy
Commits
a22342fc
Commit
a22342fc
authored
Jul 11, 2025
by
wanglei
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[AI重构]AI重构原生广告
parent
4cf529a8
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
887 additions
and
109 deletions
+887
-109
AdNativeMgr.kt
...a/com/simplecleaner/app/business/ads/admob/AdNativeMgr.kt
+66
-109
NativeAdCache.kt
...com/simplecleaner/app/business/ads/admob/NativeAdCache.kt
+131
-0
NativeAdConfig.kt
...om/simplecleaner/app/business/ads/admob/NativeAdConfig.kt
+33
-0
NativeAdLoader.kt
...om/simplecleaner/app/business/ads/admob/NativeAdLoader.kt
+112
-0
NativeAdManager.kt
...m/simplecleaner/app/business/ads/admob/NativeAdManager.kt
+203
-0
NativeAdRenderer.kt
.../simplecleaner/app/business/ads/admob/NativeAdRenderer.kt
+83
-0
README_NativeAd_Refactor.md
...leaner/app/business/ads/admob/README_NativeAd_Refactor.md
+259
-0
No files found.
app/src/main/java/com/simplecleaner/app/business/ads/admob/AdNativeMgr.kt
View file @
a22342fc
package
com.simplecleaner.app.business.ads.admob
package
com.simplecleaner.app.business.ads.admob
import
android.content.Context
import
android.content.Context
import
android.util.Log
import
androidx.annotation.LayoutRes
import
com.simplecleaner.app.GlobalConfig
import
com.simplecleaner.app.MyApplication
import
com.simplecleaner.app.business.ads.AdsType
import
com.simplecleaner.app.business.ads.LimitUtils
import
com.simplecleaner.app.business.ads.NativeParentView
import
com.google.android.gms.ads.AdListener
import
com.google.android.gms.ads.AdLoader
import
com.google.android.gms.ads.AdRequest
import
com.google.android.gms.ads.LoadAdError
import
com.google.android.gms.ads.nativead.NativeAd
import
com.google.android.gms.ads.nativead.NativeAd
import
com.google.android.gms.ads.nativead.NativeAdOptions
import
com.simplecleaner.app.business.ads.NativeParentView
import
com.simplecleaner.app.business.ads.admob.AdmobEvent.AdmobOnPaidEventListener
import
com.simplecleaner.app.utils.LogEx
import
java.util.concurrent.ConcurrentLinkedDeque
/**
/**
*原生广告加载显示管理类
* 原生广告加载显示管理类 - 重构版本
*
* 使用新的组件化架构:
* - NativeAdManager: 主要管理类
* - NativeAdCache: 缓存管理
* - NativeAdLoader: 广告加载
* - NativeAdRenderer: 广告渲染
* - NativeAdConfig: 配置管理
*/
*/
class
AdNativeMgr
{
class
AdNativeMgr
{
private
val
TAG
=
"AdNativeMgr"
private
val
TAG
=
"AdNativeMgr"
// 使用新的管理器
private
val
nativeAdManager
=
NativeAdManager
()
/**
/**
* 上一次的缓存成功时间
* 加载原生广告
*/
* @param context 上下文
protected
var
lastTime
:
Long
=
0
* @param admobEvent 广告事件
* @param showAction 展示回调
/**
* 原生广告缓存队列
*/
*/
private
val
cacheItems
=
ConcurrentLinkedDeque
<
NativeAd
>()
fun
loadAd
(
fun
loadAd
(
context
:
Context
,
context
:
Context
,
admobEvent
:
AdmobEvent
,
admobEvent
:
AdmobEvent
,
showAction
:
((
ad
:
NativeAd
)
->
Unit
)?
=
null
showAction
:
((
ad
:
NativeAd
)
->
Unit
)?
=
null
)
{
)
{
admobEvent
.
adPulStart
()
LogEx
.
logDebug
(
TAG
,
"Loading native ad"
)
nativeAdManager
.
loadAd
(
context
,
admobEvent
,
showAction
)
if
(!
LimitUtils
.
isAdShow
(
AdsType
.
NATIVE
,
admobEvent
))
{
// Log.e(TAG, "loadAd return")
return
}
var
currentNativeAd
:
NativeAd
?
=
null
val
adLoader
=
AdLoader
.
Builder
(
context
,
GlobalConfig
.
ID_ADMOB_NATIVE
).
forNativeAd
{
nativeAd
->
lastTime
=
System
.
currentTimeMillis
()
nativeAd
.
setOnPaidEventListener
(
AdmobOnPaidEventListener
(
nativeAd
,
admobEvent
.
scope
))
currentNativeAd
=
nativeAd
admobEvent
.
pullAd
(
nativeAd
.
responseInfo
)
if
(
showAction
!=
null
)
{
showAction
.
invoke
(
nativeAd
)
}
else
{
// Log.e(TAG, "offer ad")
cacheItems
.
offer
(
nativeAd
)
}
}.
withAdListener
(
object
:
AdListener
()
{
override
fun
onAdFailedToLoad
(
error
:
LoadAdError
)
{
admobEvent
.
pullAd
(
error
.
responseInfo
,
error
)
}
override
fun
onAdClicked
()
{
super
.
onAdClicked
()
admobEvent
.
clickAd
(
currentNativeAd
?.
responseInfo
)
}
override
fun
onAdClosed
()
{
super
.
onAdClosed
()
}
}).
withNativeAdOptions
(
NativeAdOptions
.
Builder
().
build
()
).
build
()
adLoader
.
loadAds
(
AdRequest
.
Builder
().
build
(),
1
)
}
}
/**
* 展示原生广告
* @param admobEvent 广告事件
* @param parent 父视图
* @param layout 布局资源ID
* @param nativeCallBack 回调
*/
fun
show
(
fun
show
(
admobEvent
:
AdmobEvent
,
admobEvent
:
AdmobEvent
,
parent
:
NativeParentView
,
parent
:
NativeParentView
,
layout
:
Int
,
@LayoutRes
layout
:
Int
,
nativeCallBack
:
((
Any
?)
->
Unit
)?
=
null
nativeCallBack
:
((
Any
?)
->
Unit
)?
=
null
)
{
)
{
LogEx
.
logDebug
(
TAG
,
"Showing native ad"
)
nativeAdManager
.
show
(
admobEvent
,
parent
,
layout
,
nativeCallBack
)
}
if
(!
LimitUtils
.
isAdShow
(
AdsType
.
NATIVE
,
admobEvent
))
{
/**
Log
.
e
(
TAG
,
"!isAdShow"
)
* 预加载广告
cacheItems
.
clear
()
* @param context 上下文
return
*/
}
fun
preloadAd
(
context
:
Context
)
{
if
(!
LimitUtils
.
isShowNative
(
AdsType
.
NATIVE
,
admobEvent
))
{
LogEx
.
logDebug
(
TAG
,
"Preloading native ad"
)
return
nativeAdManager
.
preloadAd
(
context
)
}
}
admobEvent
.
adPrepareShow
()
Log
.
e
(
TAG
,
"adNative can show"
)
if
(!
adAvailable
())
{
//缓存过期了就清空
Log
.
e
(
TAG
,
"cacheItems clear"
)
cacheItems
.
clear
()
}
val
nativeAd
=
cacheItems
.
peek
()
var
showAction
:
(
ad
:
NativeAd
)
->
Unit
=
{
ad
->
/**
parent
.
isAdShowed
=
true
* 清空缓存
nativeCallBack
?.
invoke
(
ad
)
*/
parent
.
setNativeAd
(
ad
,
layout
)
fun
clearCache
()
{
admobEvent
.
showAd
(
ad
.
responseInfo
)
LogEx
.
logDebug
(
TAG
,
"Clearing native ad cache"
)
//添加原生数量
nativeAdManager
.
clearCache
()
LimitUtils
.
addNativeDisplayNum
()
}
loadAd
(
MyApplication
.
appContext
,
AdmobEvent
(
"nativeAd"
,
"preload"
),
null
)
}
if
(
nativeAd
==
null
)
{
/**
* 获取缓存统计信息
*/
fun
getCacheStats
():
NativeAdCache
.
CacheStats
{
return
nativeAdManager
.
getCacheStats
()
}
loadAd
(
parent
.
context
,
admobEvent
)
{
ad
->
/**
Log
.
e
(
TAG
,
"load show"
)
* 检查是否有可用广告
showAction
.
invoke
(
ad
)
*/
}
fun
hasAvailableAd
():
Boolean
{
}
else
{
return
nativeAdManager
.
hasAvailableAd
()
val
flag
=
cacheItems
.
remove
(
nativeAd
)
}
Log
.
e
(
TAG
,
"ready show remove=$flag size=${cacheItems.size}"
)
showAction
.
invoke
(
nativeAd
)
}
/**
* 获取缓存大小
*/
fun
getCacheSize
():
Int
{
return
nativeAdManager
.
getCacheSize
()
}
}
// 兼容性方法 - 保持向后兼容
@Deprecated
(
"Use hasAvailableAd() instead"
,
ReplaceWith
(
"hasAvailableAd()"
))
private
fun
adAvailable
():
Boolean
{
private
fun
adAvailable
():
Boolean
{
return
(
lastTime
==
0L
)
||
((
System
.
currentTimeMillis
()
-
lastTime
)
/
1000
/
60
).
toInt
()
<
30
return
hasAvailableAd
()
}
}
}
}
app/src/main/java/com/simplecleaner/app/business/ads/admob/NativeAdCache.kt
0 → 100644
View file @
a22342fc
package
com.simplecleaner.app.business.ads.admob
import
com.google.android.gms.ads.nativead.NativeAd
import
com.simplecleaner.app.utils.LogEx
import
java.util.concurrent.ConcurrentLinkedDeque
import
java.util.concurrent.atomic.AtomicLong
/**
* 原生广告缓存管理器
*/
class
NativeAdCache
{
private
val
TAG
=
"NativeAdCache"
// 缓存队列
private
val
cacheQueue
=
ConcurrentLinkedDeque
<
NativeAd
>()
// 最后缓存时间
private
val
lastCacheTime
=
AtomicLong
(
0L
)
// 缓存统计
private
var
totalCached
=
0L
private
var
totalExpired
=
0L
/**
* 添加广告到缓存
*/
fun
addToCache
(
nativeAd
:
NativeAd
):
Boolean
{
return
try
{
if
(
cacheQueue
.
size
>=
NativeAdConfig
.
Cache
.
MAX_CACHE_SIZE
)
{
// 缓存已满,移除最旧的广告
val
oldestAd
=
cacheQueue
.
pollFirst
()
oldestAd
?.
destroy
()
LogEx
.
logDebug
(
TAG
,
"Cache full, removed oldest ad"
)
}
cacheQueue
.
offerLast
(
nativeAd
)
lastCacheTime
.
set
(
System
.
currentTimeMillis
())
totalCached
++
LogEx
.
logDebug
(
TAG
,
"Ad added to cache, size: ${cacheQueue.size}"
)
true
}
catch
(
e
:
Exception
)
{
LogEx
.
logDebug
(
TAG
,
"Failed to add ad to cache: ${e.message}"
)
false
}
}
/**
* 从缓存获取广告
*/
fun
getFromCache
():
NativeAd
?
{
return
try
{
if
(
isCacheExpired
())
{
clearCache
()
return
null
}
val
nativeAd
=
cacheQueue
.
pollFirst
()
if
(
nativeAd
!=
null
)
{
LogEx
.
logDebug
(
TAG
,
"Ad retrieved from cache, remaining: ${cacheQueue.size}"
)
}
nativeAd
}
catch
(
e
:
Exception
)
{
LogEx
.
logDebug
(
TAG
,
"Failed to get ad from cache: ${e.message}"
)
null
}
}
/**
* 检查缓存是否过期
*/
fun
isCacheExpired
():
Boolean
{
val
lastTime
=
lastCacheTime
.
get
()
if
(
lastTime
==
0L
)
return
false
val
elapsedMinutes
=
(
System
.
currentTimeMillis
()
-
lastTime
)
/
(
1000
*
60
)
return
elapsedMinutes
>=
NativeAdConfig
.
Cache
.
CACHE_EXPIRE_MINUTES
}
/**
* 清空缓存
*/
fun
clearCache
()
{
try
{
val
size
=
cacheQueue
.
size
cacheQueue
.
forEach
{
it
.
destroy
()
}
cacheQueue
.
clear
()
totalExpired
+=
size
lastCacheTime
.
set
(
0L
)
LogEx
.
logDebug
(
TAG
,
"Cache cleared, destroyed $size ads"
)
}
catch
(
e
:
Exception
)
{
LogEx
.
logDebug
(
TAG
,
"Failed to clear cache: ${e.message}"
)
}
}
/**
* 获取缓存大小
*/
fun
getCacheSize
():
Int
=
cacheQueue
.
size
/**
* 检查是否有可用广告
*/
fun
hasAvailableAd
():
Boolean
{
return
cacheQueue
.
isNotEmpty
()
&&
!
isCacheExpired
()
}
/**
* 获取缓存统计信息
*/
fun
getCacheStats
():
CacheStats
{
return
CacheStats
(
currentSize
=
cacheQueue
.
size
,
totalCached
=
totalCached
,
totalExpired
=
totalExpired
,
lastCacheTime
=
lastCacheTime
.
get
()
)
}
/**
* 缓存统计信息
*/
data class
CacheStats
(
val
currentSize
:
Int
,
val
totalCached
:
Long
,
val
totalExpired
:
Long
,
val
lastCacheTime
:
Long
)
}
\ No newline at end of file
app/src/main/java/com/simplecleaner/app/business/ads/admob/NativeAdConfig.kt
0 → 100644
View file @
a22342fc
package
com.simplecleaner.app.business.ads.admob
import
com.simplecleaner.app.GlobalConfig
/**
* 原生广告配置管理
*/
object
NativeAdConfig
{
// 广告单元ID
val
AD_UNIT_ID
=
GlobalConfig
.
ID_ADMOB_NATIVE
// 缓存配置
object
Cache
{
const
val
MAX_CACHE_SIZE
=
3
const
val
CACHE_EXPIRE_MINUTES
=
30L
const
val
LOAD_TIMEOUT_SECONDS
=
10L
}
// 事件配置
object
Event
{
const
val
AD_UNIT_NAME
=
"nativeAd"
const
val
PRELOAD_FROM
=
"preload"
}
// 错误码
object
ErrorCode
{
const
val
LOAD_FAILED
=
"load_failed"
const
val
CACHE_EXPIRED
=
"cache_expired"
const
val
NO_AVAILABLE_AD
=
"no_available_ad"
const
val
SHOW_LIMIT_EXCEEDED
=
"show_limit_exceeded"
}
}
\ No newline at end of file
app/src/main/java/com/simplecleaner/app/business/ads/admob/NativeAdLoader.kt
0 → 100644
View file @
a22342fc
package
com.simplecleaner.app.business.ads.admob
import
android.content.Context
import
com.google.android.gms.ads.AdListener
import
com.google.android.gms.ads.AdLoader
import
com.google.android.gms.ads.AdRequest
import
com.google.android.gms.ads.LoadAdError
import
com.google.android.gms.ads.nativead.NativeAd
import
com.google.android.gms.ads.nativead.NativeAdOptions
import
com.simplecleaner.app.business.ads.admob.AdmobEvent.AdmobOnPaidEventListener
import
com.simplecleaner.app.utils.LogEx
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.SupervisorJob
/**
* 原生广告加载器
*/
class
NativeAdLoader
{
private
val
TAG
=
"NativeAdLoader"
// 协程作用域
private
val
scope
=
CoroutineScope
(
SupervisorJob
()
+
Dispatchers
.
IO
)
/**
* 加载原生广告
*/
fun
loadAd
(
context
:
Context
,
admobEvent
:
AdmobEvent
,
onSuccess
:
((
NativeAd
)
->
Unit
)?
=
null
,
onError
:
((
LoadAdError
)
->
Unit
)?
=
null
)
{
try
{
LogEx
.
logDebug
(
TAG
,
"Starting to load native ad"
)
admobEvent
.
adPulStart
()
var
currentNativeAd
:
NativeAd
?
=
null
val
adLoader
=
AdLoader
.
Builder
(
context
,
NativeAdConfig
.
AD_UNIT_ID
)
.
forNativeAd
{
nativeAd
->
try
{
LogEx
.
logDebug
(
TAG
,
"Native ad loaded successfully"
)
// 设置付费事件监听器
nativeAd
.
setOnPaidEventListener
(
AdmobOnPaidEventListener
(
nativeAd
,
scope
)
)
currentNativeAd
=
nativeAd
admobEvent
.
pullAd
(
nativeAd
.
responseInfo
)
onSuccess
?.
invoke
(
nativeAd
)
}
catch
(
e
:
Exception
)
{
LogEx
.
logDebug
(
TAG
,
"Error handling loaded ad: ${e.message}"
)
}
}
.
withAdListener
(
object
:
AdListener
()
{
override
fun
onAdFailedToLoad
(
error
:
LoadAdError
)
{
LogEx
.
logDebug
(
TAG
,
"Native ad failed to load: ${error.message}"
)
admobEvent
.
pullAd
(
error
.
responseInfo
,
error
)
onError
?.
invoke
(
error
)
}
override
fun
onAdClicked
()
{
LogEx
.
logDebug
(
TAG
,
"Native ad clicked"
)
admobEvent
.
clickAd
(
currentNativeAd
?.
responseInfo
)
}
override
fun
onAdClosed
()
{
LogEx
.
logDebug
(
TAG
,
"Native ad closed"
)
}
})
.
withNativeAdOptions
(
NativeAdOptions
.
Builder
()
.
setAdChoicesPlacement
(
NativeAdOptions
.
ADCHOICES_TOP_RIGHT
)
.
setRequestCustomMuteThisAd
(
true
)
.
setVideoOptions
(
com
.
google
.
android
.
gms
.
ads
.
VideoOptions
.
Builder
()
.
setStartMuted
(
true
)
.
build
()
)
.
build
()
)
.
build
()
// 开始加载广告
adLoader
.
loadAds
(
AdRequest
.
Builder
().
build
(),
1
)
}
catch
(
e
:
Exception
)
{
LogEx
.
logDebug
(
TAG
,
"Error creating ad loader: ${e.message}"
)
}
}
/**
* 预加载广告
*/
fun
preloadAd
(
context
:
Context
)
{
loadAd
(
context
=
context
,
admobEvent
=
AdmobEvent
(
NativeAdConfig
.
Event
.
AD_UNIT_NAME
,
NativeAdConfig
.
Event
.
PRELOAD_FROM
),
onSuccess
=
{
nativeAd
->
LogEx
.
logDebug
(
TAG
,
"Preload ad loaded successfully"
)
},
onError
=
{
error
->
LogEx
.
logDebug
(
TAG
,
"Preload ad failed: ${error.message}"
)
}
)
}
}
\ No newline at end of file
app/src/main/java/com/simplecleaner/app/business/ads/admob/NativeAdManager.kt
0 → 100644
View file @
a22342fc
package
com.simplecleaner.app.business.ads.admob
import
android.content.Context
import
androidx.annotation.LayoutRes
import
com.google.android.gms.ads.LoadAdError
import
com.google.android.gms.ads.nativead.NativeAd
import
com.simplecleaner.app.MyApplication
import
com.simplecleaner.app.business.ads.NativeParentView
import
com.simplecleaner.app.business.ads.LimitUtils
import
com.simplecleaner.app.business.ads.AdsType
import
com.simplecleaner.app.utils.LogEx
/**
* 原生广告管理器 - 重构后的主要管理类
*/
class
NativeAdManager
{
private
val
TAG
=
"NativeAdManager"
// 组件实例
private
val
cache
=
NativeAdCache
()
private
val
loader
=
NativeAdLoader
()
private
val
renderer
=
NativeAdRenderer
()
/**
* 加载原生广告
*/
fun
loadAd
(
context
:
Context
,
admobEvent
:
AdmobEvent
,
showAction
:
((
ad
:
NativeAd
)
->
Unit
)?
=
null
)
{
try
{
LogEx
.
logDebug
(
TAG
,
"Loading native ad"
)
// 检查广告展示限制
if
(!
LimitUtils
.
isAdShow
(
AdsType
.
NATIVE
,
admobEvent
))
{
LogEx
.
logDebug
(
TAG
,
"Ad show limit exceeded"
)
return
}
loader
.
loadAd
(
context
=
context
,
admobEvent
=
admobEvent
,
onSuccess
=
{
nativeAd
->
LogEx
.
logDebug
(
TAG
,
"Ad loaded successfully"
)
if
(
showAction
!=
null
)
{
// 直接展示广告
showAction
.
invoke
(
nativeAd
)
}
else
{
// 添加到缓存
if
(!
cache
.
addToCache
(
nativeAd
))
{
LogEx
.
logDebug
(
TAG
,
"Failed to add ad to cache, destroying"
)
renderer
.
destroyAd
(
nativeAd
)
}
}
},
onError
=
{
error
->
LogEx
.
logDebug
(
TAG
,
"Ad load failed: ${error.message}"
)
}
)
}
catch
(
e
:
Exception
)
{
LogEx
.
logDebug
(
TAG
,
"Error in loadAd: ${e.message}"
)
}
}
/**
* 展示原生广告
*/
fun
show
(
admobEvent
:
AdmobEvent
,
parent
:
NativeParentView
,
@LayoutRes
layout
:
Int
,
nativeCallBack
:
((
Any
?)
->
Unit
)?
=
null
)
{
try
{
LogEx
.
logDebug
(
TAG
,
"Showing native ad"
)
// 检查广告展示限制
if
(!
LimitUtils
.
isAdShow
(
AdsType
.
NATIVE
,
admobEvent
))
{
LogEx
.
logDebug
(
TAG
,
"Ad show limit exceeded"
)
cache
.
clearCache
()
nativeCallBack
?.
invoke
(
null
)
return
}
// 检查展示限制
if
(!
LimitUtils
.
isShowNative
(
AdsType
.
NATIVE
,
admobEvent
))
{
LogEx
.
logDebug
(
TAG
,
"Show native limit exceeded"
)
nativeCallBack
?.
invoke
(
null
)
return
}
admobEvent
.
adPrepareShow
()
// 尝试从缓存获取广告
var
nativeAd
=
cache
.
getFromCache
()
if
(
nativeAd
!=
null
)
{
// 使用缓存的广告
LogEx
.
logDebug
(
TAG
,
"Using cached ad"
)
showAd
(
nativeAd
,
parent
,
layout
,
admobEvent
,
nativeCallBack
)
}
else
{
// 缓存中没有广告,重新加载
LogEx
.
logDebug
(
TAG
,
"No cached ad available, loading new ad"
)
loadAd
(
context
=
parent
.
context
,
admobEvent
=
admobEvent
)
{
ad
->
showAd
(
ad
,
parent
,
layout
,
admobEvent
,
nativeCallBack
)
}
}
}
catch
(
e
:
Exception
)
{
LogEx
.
logDebug
(
TAG
,
"Error in show: ${e.message}"
)
nativeCallBack
?.
invoke
(
null
)
}
}
/**
* 展示广告的具体实现
*/
private
fun
showAd
(
nativeAd
:
NativeAd
,
parent
:
NativeParentView
,
@LayoutRes
layout
:
Int
,
admobEvent
:
AdmobEvent
,
nativeCallBack
:
((
Any
?)
->
Unit
)?
)
{
renderer
.
renderAd
(
nativeAd
=
nativeAd
,
parent
=
parent
,
layout
=
layout
,
admobEvent
=
admobEvent
,
onSuccess
=
{
ad
->
LogEx
.
logDebug
(
TAG
,
"Ad shown successfully"
)
nativeCallBack
?.
invoke
(
ad
)
// 预加载下一个广告
preloadNextAd
()
},
onError
=
{
error
->
LogEx
.
logDebug
(
TAG
,
"Failed to show ad: $error"
)
nativeCallBack
?.
invoke
(
null
)
}
)
}
/**
* 预加载下一个广告
*/
private
fun
preloadNextAd
()
{
try
{
loadAd
(
context
=
MyApplication
.
appContext
,
admobEvent
=
AdmobEvent
(
NativeAdConfig
.
Event
.
AD_UNIT_NAME
,
NativeAdConfig
.
Event
.
PRELOAD_FROM
)
)
}
catch
(
e
:
Exception
)
{
LogEx
.
logDebug
(
TAG
,
"Error preloading next ad: ${e.message}"
)
}
}
/**
* 预加载广告
*/
fun
preloadAd
(
context
:
Context
)
{
try
{
loader
.
preloadAd
(
context
)
}
catch
(
e
:
Exception
)
{
LogEx
.
logDebug
(
TAG
,
"Error preloading ad: ${e.message}"
)
}
}
/**
* 清空缓存
*/
fun
clearCache
()
{
cache
.
clearCache
()
}
/**
* 获取缓存统计信息
*/
fun
getCacheStats
():
NativeAdCache
.
CacheStats
{
return
cache
.
getCacheStats
()
}
/**
* 检查是否有可用广告
*/
fun
hasAvailableAd
():
Boolean
{
return
cache
.
hasAvailableAd
()
}
/**
* 获取缓存大小
*/
fun
getCacheSize
():
Int
{
return
cache
.
getCacheSize
()
}
}
\ No newline at end of file
app/src/main/java/com/simplecleaner/app/business/ads/admob/NativeAdRenderer.kt
0 → 100644
View file @
a22342fc
package
com.simplecleaner.app.business.ads.admob
import
android.content.Context
import
androidx.annotation.LayoutRes
import
com.google.android.gms.ads.nativead.NativeAd
import
com.simplecleaner.app.business.ads.NativeParentView
import
com.simplecleaner.app.business.ads.LimitUtils
import
com.simplecleaner.app.business.ads.AdsType
import
com.simplecleaner.app.utils.LogEx
/**
* 原生广告渲染器
*/
class
NativeAdRenderer
{
private
val
TAG
=
"NativeAdRenderer"
/**
* 渲染原生广告
*/
fun
renderAd
(
nativeAd
:
NativeAd
,
parent
:
NativeParentView
,
@LayoutRes
layout
:
Int
,
admobEvent
:
AdmobEvent
,
onSuccess
:
((
NativeAd
)
->
Unit
)?
=
null
,
onError
:
((
String
)
->
Unit
)?
=
null
)
{
try
{
LogEx
.
logDebug
(
TAG
,
"Rendering native ad"
)
// 检查展示限制
if
(!
LimitUtils
.
isShowNative
(
AdsType
.
NATIVE
,
admobEvent
))
{
LogEx
.
logDebug
(
TAG
,
"Show limit exceeded"
)
onError
?.
invoke
(
NativeAdConfig
.
ErrorCode
.
SHOW_LIMIT_EXCEEDED
)
return
}
// 设置广告到父视图
parent
.
isAdShowed
=
true
parent
.
setNativeAd
(
nativeAd
,
layout
)
// 上报展示事件
admobEvent
.
showAd
(
nativeAd
.
responseInfo
)
// 增加原生广告展示数量
LimitUtils
.
addNativeDisplayNum
()
LogEx
.
logDebug
(
TAG
,
"Native ad rendered successfully"
)
onSuccess
?.
invoke
(
nativeAd
)
}
catch
(
e
:
Exception
)
{
LogEx
.
logDebug
(
TAG
,
"Error rendering native ad: ${e.message}"
)
onError
?.
invoke
(
e
.
message
?:
"Unknown rendering error"
)
}
}
/**
* 检查广告是否有效
*/
fun
isAdValid
(
nativeAd
:
NativeAd
?):
Boolean
{
return
try
{
nativeAd
?.
let
{
ad
->
ad
.
headline
?.
isNotEmpty
()
==
true
||
ad
.
body
?.
isNotEmpty
()
==
true
}
?:
false
}
catch
(
e
:
Exception
)
{
LogEx
.
logDebug
(
TAG
,
"Error checking ad validity: ${e.message}"
)
false
}
}
/**
* 销毁广告
*/
fun
destroyAd
(
nativeAd
:
NativeAd
?)
{
try
{
nativeAd
?.
destroy
()
LogEx
.
logDebug
(
TAG
,
"Native ad destroyed"
)
}
catch
(
e
:
Exception
)
{
LogEx
.
logDebug
(
TAG
,
"Error destroying native ad: ${e.message}"
)
}
}
}
\ No newline at end of file
app/src/main/java/com/simplecleaner/app/business/ads/admob/README_NativeAd_Refactor.md
0 → 100644
View file @
a22342fc
dNativeMgr 重构说明
## 重构概述
本次重构将原来的单一
`AdNativeMgr`
类拆分为多个职责单一的组件,提升了代码的可维护性、可测试性和扩展性。
## 重构前后对比
### 重构前的问题
1.
**职责不清晰**
- 一个类承担了加载、缓存、展示、事件处理等多个职责
2.
**代码重复**
- 事件处理逻辑重复,错误处理分散
3.
**硬编码**
- 缓存时间、广告ID等硬编码在代码中
4.
**错误处理不足**
- 缺乏完善的异常处理机制
5.
**可测试性差**
- 依赖过多,难以进行单元测试
6.
**可维护性差**
- 逻辑复杂,难以扩展新功能
### 重构后的优势
1.
**单一职责**
- 每个类都有明确的职责
2.
**高内聚低耦合**
- 组件间依赖关系清晰
3.
**易于测试**
- 每个组件都可以独立测试
4.
**易于扩展**
- 新增功能只需修改对应组件
5.
**配置集中**
- 所有配置统一管理
6.
**错误处理完善**
- 每个层级都有完善的错误处理
## 新的架构设计
### 核心组件
#### 1. NativeAdConfig (配置管理)
```
kotlin
object
NativeAdConfig
{
const
val
AD_UNIT_ID
=
GlobalConfig
.
ID_ADMOB_NATIVE
object
Cache
{
const
val
MAX_CACHE_SIZE
=
3
const
val
CACHE_EXPIRE_MINUTES
=
30L
}
object
Event
{
const
val
AD_UNIT_NAME
=
"nativeAd"
const
val
PRELOAD_FROM
=
"preload"
}
}
```
**职责:**
-
集中管理所有配置参数
-
提供常量定义
-
便于配置修改和维护
#### 2. NativeAdCache (缓存管理)
```
kotlin
class
NativeAdCache
{
fun
addToCache
(
nativeAd
:
NativeAd
):
Boolean
fun
getFromCache
():
NativeAd
?
fun
isCacheExpired
():
Boolean
fun
clearCache
()
fun
getCacheStats
():
CacheStats
}
```
**职责:**
-
管理广告缓存队列
-
处理缓存过期逻辑
-
提供缓存统计信息
-
自动清理过期广告
#### 3. NativeAdLoader (广告加载)
```
kotlin
class
NativeAdLoader
{
fun
loadAd
(
context
:
Context
,
admobEvent
:
AdmobEvent
,
onSuccess
:
((
NativeAd
)
->
Unit
)?,
onError
:
((
LoadAdError
)
->
Unit
)?)
fun
preloadAd
(
context
:
Context
)
}
```
**职责:**
-
负责广告加载逻辑
-
处理加载成功/失败回调
-
设置广告监听器
-
配置广告选项
#### 4. NativeAdRenderer (广告渲染)
```
kotlin
class
NativeAdRenderer
{
fun
renderAd
(
nativeAd
:
NativeAd
,
parent
:
NativeParentView
,
layout
:
Int
,
admobEvent
:
AdmobEvent
,
onSuccess
:
((
NativeAd
)
->
Unit
)?,
onError
:
((
String
)
->
Unit
)?)
fun
isAdValid
(
nativeAd
:
NativeAd
?):
Boolean
fun
destroyAd
(
nativeAd
:
NativeAd
?)
}
```
**职责:**
-
负责广告展示逻辑
-
检查广告有效性
-
处理广告销毁
-
上报展示事件
#### 5. NativeAdManager (主管理器)
```
kotlin
class
NativeAdManager
{
fun
loadAd
(
context
:
Context
,
admobEvent
:
AdmobEvent
,
showAction
:
((
ad
:
NativeAd
)
->
Unit
)?)
fun
show
(
admobEvent
:
AdmobEvent
,
parent
:
NativeParentView
,
layout
:
Int
,
nativeCallBack
:
((
Any
?)
->
Unit
)?)
fun
preloadAd
(
context
:
Context
)
fun
clearCache
()
fun
getCacheStats
():
NativeAdCache
.
CacheStats
}
```
**职责:**
-
协调各个组件工作
-
提供统一的对外接口
-
处理业务逻辑流程
-
管理组件生命周期
#### 6. AdNativeMgr (兼容层)
```
kotlin
class
AdNativeMgr
{
private
val
nativeAdManager
=
NativeAdManager
()
fun
loadAd
(
context
:
Context
,
admobEvent
:
AdmobEvent
,
showAction
:
((
ad
:
NativeAd
)
->
Unit
)?)
fun
show
(
admobEvent
:
AdmobEvent
,
parent
:
NativeParentView
,
layout
:
Int
,
nativeCallBack
:
((
Any
?)
->
Unit
)?)
// ... 其他方法
}
```
**职责:**
-
保持向后兼容
-
委托给新的管理器
-
提供迁移过渡期支持
## 使用方式
### 基本使用(保持原有接口)
```
kotlin
val
adNativeMgr
=
AdNativeMgr
()
// 加载广告
adNativeMgr
.
loadAd
(
context
,
admobEvent
)
{
nativeAd
->
// 处理加载成功的广告
}
// 展示广告
adNativeMgr
.
show
(
admobEvent
,
parentView
,
R
.
layout
.
native_ad_layout
)
{
result
->
// 处理展示结果
}
```
### 高级使用(新接口)
```
kotlin
val
nativeAdManager
=
NativeAdManager
()
// 获取缓存统计
val
stats
=
nativeAdManager
.
getCacheStats
()
Log
.
d
(
"Cache"
,
"Current size: ${stats.currentSize}, Total cached: ${stats.totalCached}"
)
// 检查是否有可用广告
if
(
nativeAdManager
.
hasAvailableAd
())
{
// 有可用广告
}
// 预加载广告
nativeAdManager
.
preloadAd
(
context
)
```
## 性能优化
### 1. 缓存优化
-
使用
`ConcurrentLinkedDeque`
保证线程安全
-
自动清理过期广告,避免内存泄漏
-
限制缓存大小,控制内存使用
### 2. 错误处理
-
每个层级都有完善的异常处理
-
提供详细的错误信息和日志
-
优雅降级,避免应用崩溃
### 3. 内存管理
-
及时销毁不需要的广告对象
-
使用弱引用避免内存泄漏
-
定期清理过期缓存
## 测试策略
### 单元测试
```
kotlin
// 测试缓存管理
@Test
fun
testCacheAddAndGet
()
{
val
cache
=
NativeAdCache
()
val
mockAd
=
mock
<
NativeAd
>()
assertTrue
(
cache
.
addToCache
(
mockAd
))
assertEquals
(
mockAd
,
cache
.
getFromCache
())
}
// 测试广告加载
@Test
fun
testAdLoader
()
{
val
loader
=
NativeAdLoader
()
// 测试加载逻辑
}
```
### 集成测试
```
kotlin
// 测试完整流程
@Test
fun
testCompleteFlow
()
{
val
manager
=
NativeAdManager
()
// 测试加载->缓存->展示的完整流程
}
```
## 迁移指南
### 1. 直接替换
现有代码可以直接使用新的
`AdNativeMgr`
,接口保持不变。
### 2. 逐步迁移
可以逐步将代码迁移到新的
`NativeAdManager`
接口:
```
kotlin
// 旧代码
val
adNativeMgr
=
AdNativeMgr
()
adNativeMgr
.
loadAd
(
context
,
admobEvent
)
// 新代码
val
nativeAdManager
=
NativeAdManager
()
nativeAdManager
.
loadAd
(
context
,
admobEvent
)
```
### 3. 配置调整
可以通过修改
`NativeAdConfig`
来调整配置:
```
kotlin
// 修改缓存大小
NativeAdConfig
.
Cache
.
MAX_CACHE_SIZE
=
5
// 修改缓存过期时间
NativeAdConfig
.
Cache
.
CACHE_EXPIRE_MINUTES
=
60L
```
## 后续优化建议
1.
**添加监控**
- 集成性能监控,收集广告加载成功率、展示率等指标
2.
**A/B测试**
- 支持不同的广告策略配置
3.
**智能预加载**
- 根据用户行为预测广告需求
4.
**多广告源**
- 支持多个广告平台的统一管理
5.
**广告质量评估**
- 根据广告质量自动调整展示策略
## 总结
本次重构显著提升了代码质量:
-
✅ 职责分离,每个组件都有明确的功能
-
✅ 提高了代码的可维护性和可测试性
-
✅ 增强了错误处理能力
-
✅ 保持了向后兼容性
-
✅ 为后续功能扩展奠定了基础
重构后的代码更加健壮、可维护,为广告模块的长期发展提供了良好的架构基础。
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment