785 lines
27 KiB
Swift
785 lines
27 KiB
Swift
|
|
|
|||
|
|
//
|
|||
|
|
// MabInterstitialEngine.swift
|
|||
|
|
// Pods
|
|||
|
|
//
|
|||
|
|
// Created by 250102 on 2025/5/10.
|
|||
|
|
//
|
|||
|
|
|
|||
|
|
import Foundation
|
|||
|
|
|
|||
|
|
// 类型别名定义
|
|||
|
|
typealias InterstitialAdObtainer = (_ adPlatform: AdPlatform, _ adConfig: AdConfig) -> GuruInterstitialAd?
|
|||
|
|
|
|||
|
|
// InterstitialBidder 类实现
|
|||
|
|
private class InterstitialBidder: CustomStringConvertible, MabBidder, FusionAdListener {
|
|||
|
|
|
|||
|
|
// 静态属性和常量
|
|||
|
|
private static let idPool = AtomicInteger(0)
|
|||
|
|
static let TAG = "InterstitialBidder"
|
|||
|
|
|
|||
|
|
// 实例属性
|
|||
|
|
private let engine: InterstitialAdEngine
|
|||
|
|
var config: AggregatorConfig
|
|||
|
|
private let ad: GuruInterstitialAd
|
|||
|
|
private let adUnitSpec: AdUnitSpec
|
|||
|
|
private let faUnitId: String
|
|||
|
|
|
|||
|
|
var id: Int
|
|||
|
|
var revenue: Double = 0.0
|
|||
|
|
|
|||
|
|
private var fusionAd: FusionAd?
|
|||
|
|
private var loadedTimeInMillis: Int64?
|
|||
|
|
|
|||
|
|
private var loaded: Bool {
|
|||
|
|
return loadedTimeInMillis != nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var isExpired: Bool? {
|
|||
|
|
get {
|
|||
|
|
// 若配置为0 则永不过期
|
|||
|
|
if config.cacheExpireInSecond == 0.0 {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
guard let loadedTime = loadedTimeInMillis else {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
return (SystemClock.elapsedRealtime() - loadedTime) > Int64(config.cacheExpireInSecond * 1000)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var isFilled: Bool {
|
|||
|
|
return loaded && !(isExpired ?? true)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func getFusionAd() -> FusionAd? {
|
|||
|
|
return fusionAd
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 私有属性
|
|||
|
|
private let loadTimeOutToken = UUID()
|
|||
|
|
private let cacheExpireToken = UUID()
|
|||
|
|
private var loadStartTime: Int64?
|
|||
|
|
|
|||
|
|
// 初始化方法
|
|||
|
|
init(
|
|||
|
|
engine: InterstitialAdEngine,
|
|||
|
|
config: AggregatorConfig,
|
|||
|
|
ad: GuruInterstitialAd,
|
|||
|
|
adUnitSpec: AdUnitSpec,
|
|||
|
|
faUnitId: String
|
|||
|
|
) {
|
|||
|
|
self.engine = engine
|
|||
|
|
self.config = config
|
|||
|
|
self.ad = ad
|
|||
|
|
self.adUnitSpec = adUnitSpec
|
|||
|
|
self.faUnitId = faUnitId
|
|||
|
|
self.id = InterstitialBidder.idPool.incrementAndGet()
|
|||
|
|
ad.listener = self
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func consumeLoadDuration() -> Int64? {
|
|||
|
|
guard let start = loadStartTime else {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
loadStartTime = nil
|
|||
|
|
return SystemClock.elapsedRealtime() - start
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private var handler: StateMachine {
|
|||
|
|
return engine
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func load(reason: RequestReason?) -> Bool {
|
|||
|
|
MabAnalyticEvents.faLoad(faUnitId: faUnitId, adType: engine.adType, aggregatorId: config.aggregatorId, adUnitId: adUnitSpec.adUnitId, requestReason: reason)
|
|||
|
|
let result = ad.load()
|
|||
|
|
if result {
|
|||
|
|
handler.removeCallbacksAndMessages(cacheExpireToken)
|
|||
|
|
loadedTimeInMillis = nil
|
|||
|
|
loadStartTime = SystemClock.elapsedRealtime()
|
|||
|
|
|
|||
|
|
let timeout: Int64
|
|||
|
|
if let configTimeout = config.loadTimeoutInSecond {
|
|||
|
|
timeout = Int64(configTimeout * 1000)
|
|||
|
|
} else {
|
|||
|
|
timeout = MabInterstitialEngine.BIDDING_LOAD_TIMEOUT_MILLIS
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
handler.postDelayed(runnable: { [weak self] in self?.performLoadTimeout() }, token: loadTimeOutToken, delayed: .milliseconds(Int(timeout)))
|
|||
|
|
}
|
|||
|
|
return result
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func show(request: InterstitialShowRequest?) -> Bool {
|
|||
|
|
let result = ad.show(request)
|
|||
|
|
if result {
|
|||
|
|
handler.removeCallbacksAndMessages(cacheExpireToken)
|
|||
|
|
loadedTimeInMillis = nil
|
|||
|
|
}
|
|||
|
|
return result
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func destroy() {
|
|||
|
|
handler.removeCallbacksAndMessages(loadTimeOutToken)
|
|||
|
|
handler.removeCallbacksAndMessages(cacheExpireToken)
|
|||
|
|
revenue = 0.0
|
|||
|
|
ad.destroy()
|
|||
|
|
fusionAd = nil
|
|||
|
|
loadedTimeInMillis = nil
|
|||
|
|
loadStartTime = nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MARK: - FusionAdListener 方法
|
|||
|
|
|
|||
|
|
func onAdLoaded(ad: FusionAd) {
|
|||
|
|
logLoaded(ad)
|
|||
|
|
handler.removeCallbacksAndMessages(loadTimeOutToken)
|
|||
|
|
fusionAd = ad
|
|||
|
|
loadedTimeInMillis = SystemClock.elapsedRealtime()
|
|||
|
|
revenue = MabBidderUtils.adjustRevenue(ad: ad, floor: adUnitSpec.floor)
|
|||
|
|
engine.sendMessage(what: MabInterstitialEngine.EVENT_ON_LOADED, obj: EventParams(ad: ad, id: id))
|
|||
|
|
|
|||
|
|
handler.removeCallbacksAndMessages(cacheExpireToken)
|
|||
|
|
let cacheTimeInMillis = Int64(config.cacheExpireInSecond * 1000)
|
|||
|
|
if cacheTimeInMillis > 0 {
|
|||
|
|
handler.postDelayed(runnable: { [weak self] in self?.performCacheExpired() }, token: cacheExpireToken, delayed: .milliseconds(Int(cacheTimeInMillis + 50)))
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func onAdDisplayed(ad: FusionAd) {
|
|||
|
|
engine.sendMessage(what: MabInterstitialEngine.EVENT_ON_DISPLAYED, obj: EventParams(ad: ad, id: id))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func onAdHidden(ad: FusionAd) {
|
|||
|
|
engine.sendMessage(what: MabInterstitialEngine.EVENT_ON_HIDDEN, obj: EventParams(ad: ad, id: id))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func onAdClicked(ad: FusionAd) {
|
|||
|
|
engine.sendMessage(what: MabInterstitialEngine.EVENT_ON_CLICK, obj: EventParams(ad: ad, id: id))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func onAdLoadFailed(loadFailedInfo: LoadFailedInfo) {
|
|||
|
|
let errorCode = loadFailedInfo.error?.errorCode ?? FusionErrorCodes.UNKNOWN.code
|
|||
|
|
logLoadFailed(errorCode)
|
|||
|
|
loadedTimeInMillis = nil
|
|||
|
|
handler.removeCallbacksAndMessages(loadTimeOutToken)
|
|||
|
|
engine.sendMessage(what:
|
|||
|
|
MabInterstitialEngine.EVENT_ON_LOAD_FAILED,
|
|||
|
|
obj: EventParams(loadFailedInfo: loadFailedInfo, id: id)
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func onAdDisplayFailed(ad: FusionAd, error: FusionError?) {
|
|||
|
|
engine.sendMessage(
|
|||
|
|
what: MabInterstitialEngine.EVENT_ON_DISPLAY_FAILED,
|
|||
|
|
obj: EventParams(ad: ad, displayFailedInfo: error, id: id)
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func onAdRevenuePaid(ad: FusionAd) {
|
|||
|
|
engine.sendMessage(what: MabInterstitialEngine.EVENT_REVENUE_PAID, obj: EventParams(ad: ad, id: id))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MARK: - 私有方法
|
|||
|
|
|
|||
|
|
private func performLoadTimeout() {
|
|||
|
|
ad.destroy()
|
|||
|
|
onAdLoadFailed(
|
|||
|
|
loadFailedInfo: LoadFailedInfo(
|
|||
|
|
engineId: ad.engineId,
|
|||
|
|
adPlatform: ad.adPlatform,
|
|||
|
|
adType: engine.adType,
|
|||
|
|
adUnitId: ad.adUnitId,
|
|||
|
|
error: LoadAdTimeoutError()
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func performCacheExpired() {
|
|||
|
|
engine.sendMessage(what: MabInterstitialEngine.EVENT_CACHE_EXPIRED, obj: EventParams(id: id))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func logLoaded(_ fusionAd: FusionAd) {
|
|||
|
|
guard let duration = consumeLoadDuration() else {
|
|||
|
|
Logger.w(tag: InterstitialBidder.TAG, message: "logLoaded but duration is nil, ignored")
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
MabAnalyticEvents.faLoaded(
|
|||
|
|
faUnitId: faUnitId,
|
|||
|
|
adType: engine.adType,
|
|||
|
|
adSource: fusionAd.networkName,
|
|||
|
|
aggregatorId: config.aggregatorId,
|
|||
|
|
adUnitId: ad.adUnitId,
|
|||
|
|
durationInMillis: duration
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func logLoadFailed(_ errorCode: Int) {
|
|||
|
|
guard let duration = consumeLoadDuration() else {
|
|||
|
|
Logger.w(tag: InterstitialBidder.TAG, message: "logLoadFailed but duration is nil, ignored")
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
MabAnalyticEvents.faFailed(
|
|||
|
|
faUnitId: faUnitId,
|
|||
|
|
adType: engine.adType,
|
|||
|
|
aggregatorId: config.aggregatorId,
|
|||
|
|
adUnitId: ad.adUnitId,
|
|||
|
|
durationInMillis: duration,
|
|||
|
|
errorCode: errorCode
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var description: String {
|
|||
|
|
return "InterstitialBidder[\(aggregatorId)(\(adUnitSpec.adUnitId))]"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MabInterstitialEngine 类实现
|
|||
|
|
internal class MabInterstitialEngine: InterstitialAdEngine {
|
|||
|
|
|
|||
|
|
// MARK: - 常量定义
|
|||
|
|
|
|||
|
|
static let ACTION_LOAD = 1
|
|||
|
|
static let ACTION_SHOW = 2
|
|||
|
|
static let ACTION_DESTROY = 4
|
|||
|
|
static let ACTION_RETRY = 5
|
|||
|
|
|
|||
|
|
static let EVENT_ON_LOADED = 100
|
|||
|
|
static let EVENT_ON_LOAD_FAILED = 101
|
|||
|
|
static let EVENT_ON_DISPLAYED = 102
|
|||
|
|
static let EVENT_ON_DISPLAY_FAILED = 103
|
|||
|
|
static let EVENT_ON_CLICK = 104
|
|||
|
|
static let EVENT_ON_HIDDEN = 105
|
|||
|
|
static let EVENT_REVENUE_PAID = 107
|
|||
|
|
static let EVENT_SHOW_TIMEOUT = 109
|
|||
|
|
static let EVENT_CACHE_EXPIRED = 110
|
|||
|
|
|
|||
|
|
static let BIDDING_LOAD_TIMEOUT_MILLIS: Int64 = 60_000
|
|||
|
|
static let SHOW_TIMEOUT_MILLIS: Int64 = 120_000
|
|||
|
|
|
|||
|
|
// MARK: - 属性
|
|||
|
|
|
|||
|
|
private let faUnitId: String
|
|||
|
|
private let mabConfig: MabInterstitialConfig
|
|||
|
|
private let adObtainer: InterstitialAdObtainer
|
|||
|
|
|
|||
|
|
private let auctioneer: MabAuctioneer<InterstitialBidder>
|
|||
|
|
|
|||
|
|
// MARK: - 初始化方法
|
|||
|
|
|
|||
|
|
init(
|
|||
|
|
viewController: UIViewController,
|
|||
|
|
id: Int,
|
|||
|
|
faUnitId: String,
|
|||
|
|
mabConfig: MabInterstitialConfig,
|
|||
|
|
adObtainer: @escaping InterstitialAdObtainer
|
|||
|
|
) {
|
|||
|
|
self.mabConfig = mabConfig
|
|||
|
|
self.adObtainer = adObtainer
|
|||
|
|
self.faUnitId = faUnitId
|
|||
|
|
|
|||
|
|
// 创建拍卖器
|
|||
|
|
weak var futureSelf: MabInterstitialEngine? = nil
|
|||
|
|
auctioneer = MabAuctioneer<InterstitialBidder>(
|
|||
|
|
engineId: id,
|
|||
|
|
faUnitId: faUnitId,
|
|||
|
|
aggregatorConfigs: mabConfig.aggregatorConfigs,
|
|||
|
|
bidderFactory: { config, adUnitSpec in
|
|||
|
|
guard let self = futureSelf else { return nil }
|
|||
|
|
return self.createBidder(config: config, adUnitSpec: adUnitSpec)
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
super.init(viewController: viewController, id: id, adType: AdType.Interstitial, strategyName: "Mab")
|
|||
|
|
futureSelf = self
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public override func createIdleState() -> Idle {
|
|||
|
|
return IdleImpl(self)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public override func createLoadingState() -> Loading {
|
|||
|
|
return LoadingImpl(self)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public override func createLoadedState() -> Loaded {
|
|||
|
|
return LoadedImpl(self)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public override func createShowingState() -> Showing {
|
|||
|
|
return ShowingImpl(self)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MARK: - 私有方法
|
|||
|
|
|
|||
|
|
private func createBidder(
|
|||
|
|
config: AggregatorConfig,
|
|||
|
|
adUnitSpec: AdUnitSpec
|
|||
|
|
) -> InterstitialBidder? {
|
|||
|
|
let adAmzId = adUnitSpec.adAmazonSlotId
|
|||
|
|
let adPlatform = AdPlatform.fromName(config.adPlatform)
|
|||
|
|
|
|||
|
|
guard let ad = adObtainer(
|
|||
|
|
adPlatform,
|
|||
|
|
AdConfig(engineId: id, adUnitId: adUnitSpec.adUnitId, adAmazonSlotId: adAmzId, requireDisableAutoRetries: true)
|
|||
|
|
) else {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return InterstitialBidder(
|
|||
|
|
engine: self,
|
|||
|
|
config: config,
|
|||
|
|
ad: ad,
|
|||
|
|
adUnitSpec: adUnitSpec,
|
|||
|
|
faUnitId: faUnitId
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func handleUnhandledMessage(_ msg: Message?) -> Bool {
|
|||
|
|
guard let what = msg?.what else { return false }
|
|||
|
|
|
|||
|
|
switch what {
|
|||
|
|
case MabInterstitialEngine.ACTION_LOAD:
|
|||
|
|
logWarn("ACTION_LOAD on \(String(describing: currentState?.name))")
|
|||
|
|
adLoadFailed(
|
|||
|
|
loadFailedInfo: LoadFailedInfo(
|
|||
|
|
engineId: id,
|
|||
|
|
adPlatform: AdPlatform.fusion,
|
|||
|
|
adType: adType,
|
|||
|
|
adUnitId: "mab_interstitial",
|
|||
|
|
error: LoadAdRequestError(message: "unhandled ACTION_LOAD, current state is \(String(describing: currentState?.name))")
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.ACTION_SHOW:
|
|||
|
|
adDisplayFailed(ad: InvalidFusionAd.interstitial(engineId: id), error: ShowAdRequestError())
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.ACTION_DESTROY:
|
|||
|
|
logWarn("Destroy may not supported in a Mab Engine, may lead memory leakages if you are trying to destroy one.")
|
|||
|
|
transitionTo(idleState)
|
|||
|
|
// destroy all ads
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_REVENUE_PAID:
|
|||
|
|
logWarn("revenue paid event unhandled!")
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_ON_LOADED:
|
|||
|
|
logWarn("event EVENT_ON_LOADED unhandled!")
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_ON_LOAD_FAILED:
|
|||
|
|
logWarn("event EVENT_ON_LOAD_FAILED unhandled!")
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func reset() {
|
|||
|
|
auctioneer.reset()
|
|||
|
|
removeMessages(what: MabInterstitialEngine.EVENT_SHOW_TIMEOUT)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MARK: - InterstitialAdEngine 方法实现
|
|||
|
|
|
|||
|
|
override func requestLoad() {
|
|||
|
|
sendMessage(what: MabInterstitialEngine.ACTION_LOAD)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override func requestShow(request: InterstitialShowRequest?) {
|
|||
|
|
sendMessage(what: MabInterstitialEngine.ACTION_SHOW, obj: request)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override func requestDestroy() {
|
|||
|
|
sendMessage(what: MabInterstitialEngine.ACTION_DESTROY)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override var supportedAdPlatforms: Set<AdPlatform> {
|
|||
|
|
return [AdPlatform.fusion]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MARK: - 内部状态类
|
|||
|
|
|
|||
|
|
class IdleImpl: Idle {
|
|||
|
|
|
|||
|
|
public let engine: MabInterstitialEngine
|
|||
|
|
|
|||
|
|
init(_ engine: MabInterstitialEngine) {
|
|||
|
|
self.engine = engine
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override func enter(from: IState?, params: Any?) {
|
|||
|
|
super.enter(from: from, params: params)
|
|||
|
|
self.engine.reset()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override func exit(to: IState?) {
|
|||
|
|
super.exit(to: to)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override func processMessage(_ msg: Message) -> Bool {
|
|||
|
|
let what = msg.what
|
|||
|
|
if what == MabInterstitialEngine.ACTION_LOAD {
|
|||
|
|
let result = engine.auctioneer.requestNext()
|
|||
|
|
if result != MabAuctioneer.RequestResult.noAvailableSource {
|
|||
|
|
engine.transitionTo(engine.loadingState)
|
|||
|
|
return true
|
|||
|
|
} else {
|
|||
|
|
engine.logError("No available source")
|
|||
|
|
engine.adLoadFailed(
|
|||
|
|
loadFailedInfo: LoadFailedInfo(
|
|||
|
|
engineId: engine.id,
|
|||
|
|
adPlatform: AdPlatform.fusion,
|
|||
|
|
adType: engine.adType,
|
|||
|
|
adUnitId: "mab_interstitial",
|
|||
|
|
error: LoadAdNotAvailableError()
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return engine.handleUnhandledMessage(msg) || super.processMessage(msg)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override var name: String {
|
|||
|
|
return super.name
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class LoadingImpl: Loading {
|
|||
|
|
public let engine: MabInterstitialEngine
|
|||
|
|
|
|||
|
|
init(_ engine: MabInterstitialEngine) {
|
|||
|
|
self.engine = engine
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private var retryCount = 0
|
|||
|
|
|
|||
|
|
override func enter(from: IState?, params: Any?) {
|
|||
|
|
super.enter(from: from, params: params)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override func exit(to: IState?) {
|
|||
|
|
super.exit(to: to)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override func processMessage(_ msg: Message) -> Bool {
|
|||
|
|
let what = msg.what
|
|||
|
|
|
|||
|
|
switch what {
|
|||
|
|
case MabInterstitialEngine.ACTION_RETRY:
|
|||
|
|
retryCount += 1
|
|||
|
|
engine.transitionTo(engine.idleState)
|
|||
|
|
engine.requestLoad()
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.ACTION_LOAD:
|
|||
|
|
engine.logWarn("Already loading! Ignore load request")
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_ON_LOADED:
|
|||
|
|
if let eventParams = msg.obj as? EventParams {
|
|||
|
|
if eventParams.ad != nil {
|
|||
|
|
engine.auctioneer.onLoaded(bidderId: eventParams.id)
|
|||
|
|
engine.auctioneer.requestNext()
|
|||
|
|
if engine.auctioneer.hasWinner {
|
|||
|
|
engine.transitionTo(engine.loadedState, params: eventParams)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_ON_LOAD_FAILED:
|
|||
|
|
guard let eventParams = msg.obj as? EventParams else {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
engine.auctioneer.onLoadFailed(bidderId: eventParams.id)
|
|||
|
|
let result = engine.auctioneer.requestNext()
|
|||
|
|
|
|||
|
|
if result == MabAuctioneer.RequestResult.noAvailableSource {
|
|||
|
|
let failedInfo = eventParams.loadFailedInfo ?? LoadFailedInfo(
|
|||
|
|
engineId: engine.id,
|
|||
|
|
adPlatform: AdPlatform.fusion,
|
|||
|
|
adType: engine.adType,
|
|||
|
|
adUnitId: "mab_interstitial",
|
|||
|
|
error: LoadAdNotAvailableError()
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
engine.adLoadFailed(loadFailedInfo: failedInfo)
|
|||
|
|
|
|||
|
|
let delay = min(max(pow(2.0, Double(retryCount)) as Double, 10), 300)
|
|||
|
|
engine.sendMessageDelayed(what: MabInterstitialEngine.ACTION_RETRY, delayMillis: Int(delay * 1000))
|
|||
|
|
}
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return engine.handleUnhandledMessage(msg) || super.processMessage(msg)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class LoadedImpl: Loaded {
|
|||
|
|
public let engine: MabInterstitialEngine
|
|||
|
|
|
|||
|
|
init(_ engine: MabInterstitialEngine) {
|
|||
|
|
self.engine = engine
|
|||
|
|
}
|
|||
|
|
override func enter(from: IState?, params: Any?) {
|
|||
|
|
if let params = params as? EventParams, let ad = params.ad {
|
|||
|
|
self.engine.adLoaded(ad: ad)
|
|||
|
|
} else {
|
|||
|
|
self.engine.logWarn("Loaded state enter with invalid params")
|
|||
|
|
self.engine.transitionTo(self.engine.idleState)
|
|||
|
|
}
|
|||
|
|
super.enter(from: from, params: params)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override func exit(to: IState?) {
|
|||
|
|
super.exit(to: to)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override func processMessage(_ msg: Message) -> Bool {
|
|||
|
|
|
|||
|
|
let what = msg.what
|
|||
|
|
|
|||
|
|
switch what {
|
|||
|
|
case MabInterstitialEngine.ACTION_SHOW:
|
|||
|
|
let showRequest = msg.obj as? InterstitialShowRequest
|
|||
|
|
var showResult: Bool? = nil
|
|||
|
|
var finalWinner: InterstitialBidder? = nil
|
|||
|
|
|
|||
|
|
while true {
|
|||
|
|
guard let winner = engine.auctioneer.pollMaxRevenue() else {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if !winner.isFilled {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
showResult = winner.show(request: showRequest)
|
|||
|
|
|
|||
|
|
if showResult != true {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
finalWinner = winner
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if let winner = finalWinner {
|
|||
|
|
engine.auctioneer.onShown(winner: winner)
|
|||
|
|
engine.transitionTo(engine.showingState)
|
|||
|
|
} else {
|
|||
|
|
let errorMessage = showResult == nil
|
|||
|
|
? "requestShow while NO winner in candidates"
|
|||
|
|
: "Show Ad returns false"
|
|||
|
|
|
|||
|
|
engine.adDisplayFailed(
|
|||
|
|
ad: InvalidFusionAd.interstitial(engineId: engine.id),
|
|||
|
|
error: ShowAdRequestError(message: errorMessage)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
let result = engine.auctioneer.requestNext()
|
|||
|
|
if result != MabAuctioneer.RequestResult.noAvailableSource {
|
|||
|
|
engine.transitionTo(engine.loadingState)
|
|||
|
|
} else {
|
|||
|
|
engine.transitionTo(engine.idleState)
|
|||
|
|
engine.requestLoad()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_ON_LOADED:
|
|||
|
|
if let eventParams = msg.obj as? EventParams, eventParams.ad != nil {
|
|||
|
|
engine.auctioneer.onLoaded(bidderId: eventParams.id)
|
|||
|
|
engine.auctioneer.requestNext()
|
|||
|
|
}
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_ON_LOAD_FAILED:
|
|||
|
|
processLoadFailed(msg)
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_CACHE_EXPIRED:
|
|||
|
|
engine.auctioneer.evictCache()
|
|||
|
|
if !engine.auctioneer.hasWinner {
|
|||
|
|
engine.transitionTo(engine.idleState)
|
|||
|
|
engine.requestLoad()
|
|||
|
|
}
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return engine.handleUnhandledMessage(msg) || super.processMessage(msg)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func processLoadFailed(_ msg: Message) {
|
|||
|
|
guard let eventParams = msg.obj as? EventParams else {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
engine.auctioneer.onLoadFailed(bidderId: eventParams.id)
|
|||
|
|
engine.auctioneer.requestNext()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class ShowingImpl: Showing {
|
|||
|
|
public let engine: MabInterstitialEngine
|
|||
|
|
|
|||
|
|
init(_ engine: MabInterstitialEngine) {
|
|||
|
|
self.engine = engine
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override func enter(from: IState?, params: Any?) {
|
|||
|
|
|
|||
|
|
let timeout: Int64
|
|||
|
|
if let configTimeout = engine.mabConfig.showTimeoutInSecond {
|
|||
|
|
timeout = Int64(configTimeout * 1000)
|
|||
|
|
} else {
|
|||
|
|
timeout = MabInterstitialEngine.SHOW_TIMEOUT_MILLIS
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
engine.sendMessageDelayed(what: MabInterstitialEngine.EVENT_SHOW_TIMEOUT, delayed: .milliseconds(Int(timeout)))
|
|||
|
|
super.enter(from: from, params: params)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override func exit(to: IState?) {
|
|||
|
|
self.engine.removeMessages(what: MabInterstitialEngine.EVENT_SHOW_TIMEOUT)
|
|||
|
|
super.exit(to: to)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override func processMessage(_ msg: Message) -> Bool {
|
|||
|
|
let what = msg.what
|
|||
|
|
|
|||
|
|
switch what {
|
|||
|
|
case MabInterstitialEngine.EVENT_ON_DISPLAYED:
|
|||
|
|
if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad {
|
|||
|
|
engine.adDisplayed(ad: ad)
|
|||
|
|
} else {
|
|||
|
|
engine.logWarn("Showing state enter with invalid params")
|
|||
|
|
}
|
|||
|
|
engine.removeMessages(what: MabInterstitialEngine.EVENT_SHOW_TIMEOUT)
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_ON_CLICK:
|
|||
|
|
if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad {
|
|||
|
|
engine.adClicked(ad: ad)
|
|||
|
|
} else {
|
|||
|
|
engine.logWarn("Showing state enter with invalid params")
|
|||
|
|
}
|
|||
|
|
engine.removeMessages(what: MabInterstitialEngine.EVENT_SHOW_TIMEOUT)
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_ON_HIDDEN:
|
|||
|
|
if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad {
|
|||
|
|
engine.adHidden(ad: ad)
|
|||
|
|
} else {
|
|||
|
|
engine.logWarn("Showing state enter with invalid params")
|
|||
|
|
}
|
|||
|
|
processRequestAfterConsumed()
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_ON_DISPLAY_FAILED:
|
|||
|
|
if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad {
|
|||
|
|
engine.adDisplayFailed(ad: ad, error: eventParams.displayFailedInfo)
|
|||
|
|
} else {
|
|||
|
|
engine.logWarn("Showing state enter with invalid params")
|
|||
|
|
}
|
|||
|
|
processRequestAfterConsumed()
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_SHOW_TIMEOUT:
|
|||
|
|
engine.adDisplayFailed(ad: InvalidFusionAd.interstitial(engineId: engine.id), error: ShowAdTimeoutError())
|
|||
|
|
processRequestAfterConsumed()
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_REVENUE_PAID:
|
|||
|
|
if let eventParams = msg.obj as? EventParams, let ad = eventParams.ad {
|
|||
|
|
engine.adRevenuePaid(ad: ad)
|
|||
|
|
} else {
|
|||
|
|
engine.logWarn("revenue paid event with invalid params")
|
|||
|
|
}
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_ON_LOADED:
|
|||
|
|
if let eventParams = msg.obj as? EventParams {
|
|||
|
|
engine.auctioneer.onLoaded(bidderId: eventParams.id)
|
|||
|
|
}
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
case MabInterstitialEngine.EVENT_ON_LOAD_FAILED:
|
|||
|
|
if let eventParams = msg.obj as? EventParams {
|
|||
|
|
engine.auctioneer.onLoadFailed(bidderId: eventParams.id)
|
|||
|
|
}
|
|||
|
|
return true
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return engine.handleUnhandledMessage(msg) || super.processMessage(msg)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 消耗后重新填充请求队列
|
|||
|
|
* 正常情况下执行完毕后,队列头部为自抬价聚合请求,后续为按priority降序排列的聚合
|
|||
|
|
* 并开始一轮requestNext
|
|||
|
|
*/
|
|||
|
|
private func processRequestAfterConsumed() {
|
|||
|
|
|
|||
|
|
engine.auctioneer.refillCandidatesQueue()
|
|||
|
|
|
|||
|
|
let result = engine.auctioneer.requestNext()
|
|||
|
|
if let winner = engine.auctioneer.peekMaxRevenue() {
|
|||
|
|
engine.transitionTo(
|
|||
|
|
engine.loadedState,
|
|||
|
|
params: EventParams(ad: winner.getFusionAd())
|
|||
|
|
)
|
|||
|
|
} else if result != MabAuctioneer.RequestResult.noAvailableSource {
|
|||
|
|
engine.transitionTo(engine.loadingState)
|
|||
|
|
} else {
|
|||
|
|
engine.transitionTo(engine.idleState)
|
|||
|
|
engine.requestLoad()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private class AtomicInteger {
|
|||
|
|
private var value: Int
|
|||
|
|
|
|||
|
|
init(_ initialValue: Int) {
|
|||
|
|
self.value = initialValue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func get() -> Int {
|
|||
|
|
return value
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func incrementAndGet() -> Int {
|
|||
|
|
value += 1
|
|||
|
|
return value
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private class SystemClock {
|
|||
|
|
static func elapsedRealtime() -> Int64 {
|
|||
|
|
return Int64(ProcessInfo.processInfo.systemUptime * 1000)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static func elapsedRealtimeNanos() -> Int64 {
|
|||
|
|
return Int64(ProcessInfo.processInfo.systemUptime * 1_000_000_000)
|
|||
|
|
}
|
|||
|
|
}
|