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)
|
||
}
|
||
}
|