FBAdManager.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  1. // fb文档 https://developers.facebook.com/docs/games/instant-games/sdk/fbinstant6.3
  2. // 使用的话,直接看广告管理器 FBAdManager
  3. /*
  4. 使用步骤
  5. * 1. addXXXXAd() 添加相应的广告,以及预加载的数量(默认为3)
  6. * 1.1. 插屏 addInterstitial
  7. * 1.2. 激励视频 addRewardedVideo
  8. * 1.3. banner addBanner
  9. * 2. loadAll() 预加载所有广告实例
  10. * 3. isXXXReady() 检查是否可以播放
  11. * 3.1. 插屏 isInterstitialAdReady
  12. * 3.2. 激励视频 isRewardedVideoReady
  13. * 3.3. banner isBannerReady
  14. * 4. showXXXAsync() 播放广告,并检查播放状态
  15. * 4.1. 插屏 showInterstitialAd
  16. * 4.2. 激励视频 showRewardedVideo
  17. * 4.3. banner showBannerAsync
  18. * 5. hideXXXAsync() 隐藏广告(banner专属)
  19. * 5.1. 插屏 不需要
  20. * 5.2. 激励视频 不需要
  21. * 5.3. banner hideBannerAsync
  22. * 其他:
  23. * 6. 判断是否支持特定api
  24. * 6.1 判断是否支持banner广告
  25. //
  26. */
  27. import { utils } from "../Utils";
  28. const FB_MAX_AD_INSTANCE = 3; // FB允许的最多广告实例数量
  29. const FB_INIT_AD_COUNT = 3; // 预加载的广告实例数量
  30. const FB_BANNER_REFRESH_INTERVAL = 30+10; // FB: Banner广告有播放间隔限制 30 seconds (由于网络原因,需要多加一点时间)
  31. const FB_INTERSTITIAL_REFRESH_INTERVAL = 30+10; // FB: 插屏广告有播放间隔限制
  32. const FB_REWARDED_VIDEO_REFRESH_INTERVAL = 0; // FB: 激励视频没有播放间隔限制
  33. const FB_MAX_BANNER_ERROR = 1; // banner加载连续出现N次错误后,终止加载
  34. const FB_MAX_INTERSTITIAL_ERROR = 3; // 插屏加载连续出现N次错误后,终止加载
  35. const FB_MAX_REWARDED_VIDEO_ERROR = 3; // 激励视频加载连续出现N次错误后,终止加载
  36. const FB_AUTO_LOAD_ON_PLAY = true; // 插屏、激励视频是否在播放完毕后自动加载
  37. const FB_AUTO_RELOAD_DELAY = 1; // 自动重新加载时,延迟加载等待的时间
  38. const FB_AD_DELAY_FOR_FIRST_BANNER = 0; // 首个banner广告延迟N秒显示
  39. const FB_AD_DELAY_FOR_FIRST_INTERSTITIAL = 30; // 首个插屏广告需要延迟30秒播放(避免游戏前30秒就播放广告)
  40. const FB_AD_DELAY_FOR_FIRST_REWARDED_VIDEO = 0; // 首个激励视频广告延迟N秒显示
  41. enum FB_AD_TYPE{
  42. INTERSTITIAL = 0,
  43. REWARDED_VIDEO = 1,
  44. BANNER = 2
  45. }
  46. enum FB_AD_STATE{
  47. NONE,
  48. NEW,
  49. LOADING,
  50. LOADED,
  51. PLAYING
  52. }
  53. function getStateName(state:FB_AD_STATE){
  54. let str = "NONE";
  55. switch(state){
  56. case FB_AD_STATE.NEW:
  57. str = "NEW";
  58. break;
  59. case FB_AD_STATE.LOADING:
  60. str = "LOADING";
  61. break;
  62. case FB_AD_STATE.LOADED:
  63. str = "LOADED";
  64. break;
  65. case FB_AD_STATE.PLAYING:
  66. str = "PLAYING";
  67. break;
  68. }
  69. return str;
  70. }
  71. async function waitTimeSecond(timeoutSecond:number, callback?) {
  72. return new Promise<void>((resolve, reject)=>{
  73. setTimeout(()=>{
  74. if(callback){
  75. callback();
  76. }
  77. resolve();
  78. }, timeoutSecond * 1000);
  79. });
  80. }
  81. interface FB_ERROR{
  82. code: string;
  83. message: string;
  84. }
  85. const ErrorTooManyAdInstance:FB_ERROR = {
  86. code: "EXCEED_MAX_AD_INSTANCE",
  87. message: "广告对象不允许超过 " + FB_MAX_AD_INSTANCE
  88. }
  89. const ErrorNoReadyAdInstance:FB_ERROR = {
  90. code: "NO_READY_AD_INSTANCE",
  91. message: "没有加载完毕的广告,或者广告播放太频繁"
  92. }
  93. const ErrorNotReadyForLoad:FB_ERROR = {
  94. code: "NOT_READY_FOR_LOAD",
  95. message: "当前状态不允许再次加载"
  96. }
  97. const ErrorAdIsLoading:FB_ERROR = {
  98. code: "AD_IS_LOADING",
  99. message: "广告正在加载"
  100. }
  101. const ErrorNotReadyForPlay:FB_ERROR = {
  102. code: "NOT_READY_FOR_PLAYING",
  103. message: "没有可以播放的广告"
  104. }
  105. const ErrorAdIsPlaying:FB_ERROR = {
  106. code: "AD_IS_PLAYING",
  107. message: "广告正在播放"
  108. }
  109. const ErrorNoBannerAdInstance:FB_ERROR = {
  110. code: "NO_BANNER_AD",
  111. message: "没有添加Banner广告"
  112. }
  113. const ErrorApiNotSupport:FB_ERROR = {
  114. code: "API_NOT_SUPPORT",
  115. message: "广告接口不支持"
  116. }
  117. const ErrorTooFastShow:FB_ERROR = {
  118. code: "TOO_FAST_SHOW",
  119. message: "广告播放太频繁"
  120. }
  121. const ErrorNotPlaying:FB_ERROR = {
  122. code: "NOT_PLAYING",
  123. message: "广告没有播放"
  124. }
  125. const ErrorTooManyErrors:FB_ERROR = {
  126. code: "TOO_MANY_ERRORS",
  127. message: "太多错误, 停止操作"
  128. }
  129. const FB_API_BANNER = "loadBannerAdAsync";
  130. const FB_ERROR_CODE_RATE_LIMITED = "RATE_LIMITED";
  131. const FB_ERROR_CLIENT_UNSUPPORTED_OPERATION = "CLIENT_UNSUPPORTED_OPERATION";
  132. const FB_ERROR_ADS_NO_FILL = "ADS_NO_FILL";
  133. // state : NONE -> NEW -> LOADING -> LOADED -> SHOWING -> (SHOWED) NONE
  134. interface FBAdOption{
  135. autoLoadOnPlay: boolean,
  136. maxLoadError: number, // 最多失误多少次后不再加载
  137. }
  138. interface AdTimerOption{
  139. refreshInterval: number, // 播放间隔
  140. delayForFirstAd: number, // 第一个广告延迟N秒播放(避免游戏前30秒就播放广告)
  141. }
  142. function getOption(opt:FBAdOption, key:string, defaultValue:any){
  143. if(opt && typeof(opt[key])!= "undefined") {
  144. return opt[key];
  145. }
  146. return defaultValue;
  147. }
  148. // 广告计时器
  149. class AdTimer{
  150. protected _lastShowTime:number = 0; // 上次显示时间
  151. protected _refreshInterval:number = 0; // 刷新间隔, <=0 表示无限制
  152. constructor(interval:number, delay:number){
  153. this._refreshInterval = interval>0?interval:0;
  154. this._lastShowTime = 0;
  155. if(delay>0){
  156. this._lastShowTime = Date.now() + delay * 1000 - this._refreshInterval * 1000;
  157. }
  158. }
  159. public isReadyToRefresh(){
  160. return this.getNextRefreshInterval() <= 0;
  161. }
  162. public getNextRefreshInterval(){
  163. let refreshInterval = 0;
  164. if(this._refreshInterval>0 && this._lastShowTime > 0){
  165. let currentTime = Date.now();
  166. refreshInterval = this._refreshInterval - (currentTime - this._lastShowTime)/1000;
  167. }
  168. return refreshInterval;
  169. }
  170. public updateLastShowTime(){
  171. this._lastShowTime = Date.now();
  172. }
  173. }
  174. class FBAdUnitBase{
  175. protected _state:FB_AD_STATE;
  176. protected _adId:string;
  177. protected _type:FB_AD_TYPE;
  178. // protected _lastShowTime:number = 0; // 上次显示时间
  179. // protected _refreshInterval:number = 0; // 刷新间隔, <=0 表示无限制
  180. protected _maxLoadError:number = 0;
  181. protected _errorCounter:number = 0;
  182. protected _fatalError:boolean = false;
  183. protected _sharedTimer:AdTimer = null;
  184. constructor(id:string, type:FB_AD_TYPE, sharedTimer:AdTimer, opt?:FBAdOption){
  185. this._adId = id;
  186. this._state = FB_AD_STATE.NONE;
  187. this._type = type;
  188. this._sharedTimer = sharedTimer;
  189. this._fatalError = false;
  190. utils.showLog(!!sharedTimer, "sharedTimer is invalid", sharedTimer);
  191. // this._refreshInterval = getOption(opt, "refreshInterval", 0);
  192. this._maxLoadError = getOption(opt, "maxLoadError", 0);
  193. // const delayForFirstAd = getOption(opt, "delayForFirstAd", 0);
  194. // if(delayForFirstAd > 0) {
  195. // this._lastShowTime = Date.now() + delayForFirstAd * 1000 - this._refreshInterval * 1000;
  196. // }else{
  197. // this._lastShowTime = 0;
  198. // }
  199. }
  200. public getStateName(){
  201. return getStateName(this._state);
  202. }
  203. public getAdTypeName(){
  204. if(this._type == FB_AD_TYPE.INTERSTITIAL){
  205. return "插屏广告";
  206. }
  207. if(this._type == FB_AD_TYPE.REWARDED_VIDEO){
  208. return "激励视频广告";
  209. }
  210. if(this._type == FB_AD_TYPE.BANNER){
  211. return "Banner";
  212. }
  213. return "UNKNOWN";
  214. }
  215. public getInfo(){
  216. return `[${this.getAdTypeName()}:${this._adId}:${this.getStateName()}]`;
  217. }
  218. public isReadyToRefresh(){
  219. // return this.getNextRefreshInterval() <= 0;
  220. return this._sharedTimer.isReadyToRefresh();
  221. }
  222. public getNextRefreshInterval(){
  223. return this._sharedTimer.getNextRefreshInterval();
  224. }
  225. protected updateLastShowTime(){
  226. this._sharedTimer.updateLastShowTime();
  227. }
  228. protected increaseErrorCounter(){
  229. this._errorCounter++;
  230. }
  231. protected resetErrorCounter(){
  232. this._errorCounter = 0;
  233. }
  234. protected setFatalError(){
  235. this._fatalError = true;
  236. }
  237. public isErrorTooMany(){
  238. return this._fatalError || (this._maxLoadError>0 && this._errorCounter >= this._maxLoadError);
  239. }
  240. }
  241. // 有状态的广告对象
  242. abstract class FBStatefulAdUnit extends FBAdUnitBase{
  243. private _adInstance:FBInstant.AdInstance;
  244. private _autoLoadOnPlay:boolean; // 播放完毕后是否立即自动加载
  245. constructor(id:string, type:number, sharedTimer:AdTimer, opt?:FBAdOption){
  246. super(id, type, sharedTimer, opt);
  247. this._adInstance = null;
  248. this._autoLoadOnPlay = getOption(opt, "autoLoadOnPlay", false);
  249. }
  250. protected abstract createAdInstanceAsync(adId:string):Promise<FBInstant.AdInstance>;
  251. // 预加载广告
  252. public async loadAsync(){
  253. // [1] 获取 AdInstance
  254. if(this._adInstance == null){
  255. if(this._state == FB_AD_STATE.NONE){
  256. // 只能创建一次
  257. this._state = FB_AD_STATE.NEW;
  258. utils.showLog("获取广告对象: " + this.getInfo());
  259. this._adInstance = await this.createAdInstanceAsync(this._adId);
  260. }else{
  261. // 已经在创建对象了 (new-ing)
  262. utils.showLog("当前状态未满足加载条件, 正在获取广告对象: " + this.getInfo());
  263. return;
  264. }
  265. }else{
  266. // 对象已经创建好
  267. // 可以进行预加载
  268. }
  269. // [2] 检查是否满足预加载条件
  270. if(this._state != FB_AD_STATE.NEW){
  271. // 只有 NEW 状态才能进行加载
  272. utils.showLog("当前状态未满足加载条件: " + this.getInfo());
  273. if(this._state == FB_AD_STATE.LOADING){
  274. utils.showLog("广告正在加载中,不要重复加载" + this.getInfo());
  275. throw ErrorAdIsLoading;
  276. }else{
  277. throw ErrorNotReadyForLoad;
  278. }
  279. }
  280. if(this.isErrorTooMany()){
  281. utils.showLog("太多错误,停止加载: " + this.getInfo());
  282. throw ErrorTooManyErrors;
  283. }
  284. try{
  285. // [3] 加载广告
  286. // 设置为加载中
  287. this._state = FB_AD_STATE.LOADING;
  288. utils.showLog("开始加载广告: " + this.getInfo());
  289. await this._adInstance.loadAsync();
  290. // [4] 成功加载
  291. this._state = FB_AD_STATE.LOADED;
  292. this.resetErrorCounter();
  293. utils.showLog("广告加载成功: " + this.getInfo());
  294. return true;
  295. }catch(e){
  296. // [5] 加载失败
  297. // 异常能正常进入promise的catch分支
  298. // 加载失败,不需要重置 adInstance
  299. // this._adInstance = null;
  300. // 状态回退到加载前
  301. utils.showLog("广告加载失败: " + this.getInfo(), e);
  302. if((e as FB_ERROR).code == FB_ERROR_ADS_NO_FILL){
  303. // 遇到 NOT FILL错误,就不能再继续加载了
  304. utils.showLog("广告无法填充,不再继续请求: " + this.getInfo());
  305. this.setFatalError();
  306. }else{
  307. this.increaseErrorCounter();
  308. this._state = FB_AD_STATE.NEW;
  309. // [6] 加载失败,自动重新加载
  310. // 适当延迟
  311. let delayTime = 10 * this._errorCounter + FB_AUTO_RELOAD_DELAY;
  312. utils.showLog("延迟" + delayTime + "秒后, 自动重新加载: " + this.getInfo());
  313. waitTimeSecond(delayTime, this.loadAsync.bind(this));
  314. }
  315. throw e;
  316. }
  317. }
  318. // 广告是否加载完毕
  319. public isReady(){
  320. return this._adInstance != null && this._state == FB_AD_STATE.LOADED;
  321. }
  322. // 播放广告
  323. public async showAsync(){
  324. // [1.1] 判断是否满足播放条件
  325. if(!this.isReady()){
  326. utils.showLog("当前状态未满足播放条件: " + this.getInfo());
  327. if(this._state == FB_AD_STATE.PLAYING){
  328. throw ErrorAdIsPlaying;
  329. }else{
  330. throw ErrorNotReadyForPlay;
  331. }
  332. }
  333. // [1.2] 是否满足播放间隔
  334. if(!this.isReadyToRefresh()){
  335. utils.showLog("播放太频繁,还需间隔" + this.getNextRefreshInterval() + " 秒: " + this.getInfo());
  336. throw ErrorTooFastShow;
  337. }
  338. try{
  339. // [2] 播放广告
  340. // 设置为播放中
  341. this._state = FB_AD_STATE.PLAYING;
  342. utils.showLog("开始播放广告: " + this.getInfo());
  343. await this._adInstance.showAsync();
  344. utils.showLog("播放广告完毕: " + this.getInfo());
  345. // [3] 播放完毕后重置广告对象
  346. this._adInstance = null;
  347. this._state = FB_AD_STATE.NONE;
  348. this.updateLastShowTime();
  349. // [4] 播完自动加载
  350. if(this._autoLoadOnPlay){
  351. // TODO: 应该适当延迟
  352. utils.showLog("延迟" + FB_AUTO_RELOAD_DELAY + "秒后, 自动重新加载: " + this.getInfo());
  353. waitTimeSecond(FB_AUTO_RELOAD_DELAY, this.loadAsync.bind(this));
  354. }
  355. return true;
  356. }catch(e){
  357. // [5] 播放完毕后重置广告对象
  358. utils.showLog("播放广告失败: " + this.getInfo(), e);
  359. if(e.code == FB_ERROR_CODE_RATE_LIMITED){
  360. // 播放太频繁,可忽略
  361. // 状态回退
  362. this._state = FB_AD_STATE.LOADED;
  363. }else{
  364. this._adInstance = null;
  365. this._state = FB_AD_STATE.NONE;
  366. // [6] 失败自动重新加载
  367. if(this._autoLoadOnPlay){
  368. utils.showLog("延迟" + FB_AUTO_RELOAD_DELAY + "秒后, 自动重新加载: " + this.getInfo());
  369. waitTimeSecond(FB_AUTO_RELOAD_DELAY, this.loadAsync.bind(this));
  370. }
  371. }
  372. throw e;
  373. }
  374. // return false;
  375. }
  376. }
  377. // 插屏广告
  378. class FBInterstitialUnit extends FBStatefulAdUnit{
  379. constructor(id:string, sharedTimer:AdTimer, opt?:FBAdOption){
  380. super(id, FB_AD_TYPE.INTERSTITIAL, sharedTimer, opt);
  381. }
  382. protected async createAdInstanceAsync(adId: string){
  383. return await FBInstant.getInterstitialAdAsync(this._adId);
  384. }
  385. }
  386. // 激励视频广告
  387. class FBRewardedVideoUnit extends FBStatefulAdUnit{
  388. constructor(id:string, sharedTimer:AdTimer, opt?:FBAdOption){
  389. super(id, FB_AD_TYPE.REWARDED_VIDEO, sharedTimer, opt);
  390. }
  391. protected async createAdInstanceAsync(adId: string){
  392. return await FBInstant.getRewardedVideoAsync(this._adId);
  393. }
  394. }
  395. // 横幅广告
  396. class FBBannerUnit extends FBAdUnitBase{
  397. constructor(id:string, sharedTimer:AdTimer,opt?:FBAdOption){
  398. super(id, FB_AD_TYPE.BANNER, sharedTimer, opt);
  399. }
  400. // 显示Banner广告, 注意可以调用多次
  401. public async showAsync(){
  402. if(!this.isReadyToRefresh()){
  403. utils.showLog("播放太频繁,还需间隔" + this.getNextRefreshInterval() + " 秒: " + this.getInfo());
  404. throw ErrorTooFastShow;
  405. }
  406. if(this.isErrorTooMany()){
  407. utils.showLog("太多错误,停止加载: " + this.getInfo());
  408. throw ErrorTooManyErrors;
  409. }
  410. try{
  411. this._state = FB_AD_STATE.PLAYING;
  412. utils.showLog("开始显示广告: " + this.getInfo());
  413. await FBInstant.loadBannerAdAsync(this._adId);
  414. utils.showLog("显示广告成功: " + this.getInfo());
  415. // 更新刷新时间
  416. this.updateLastShowTime();
  417. this.resetErrorCounter();
  418. }catch(e){
  419. utils.showLog("显示广告失败: " + this.getInfo(), e);
  420. if(e.code == FB_ERROR_CODE_RATE_LIMITED){
  421. // 播放太频繁,可忽略
  422. // 不用重置,保留
  423. }else if(e.code == FB_ERROR_ADS_NO_FILL){
  424. // 遇到 NOT FILL错误,就不能再继续加载了
  425. utils.showLog("广告无法填充,不再继续请求: " + this.getInfo());
  426. this.setFatalError();
  427. }else{
  428. this.increaseErrorCounter();
  429. }
  430. throw e;
  431. }
  432. }
  433. public async hideAsync(){
  434. if(this._state != FB_AD_STATE.PLAYING){
  435. utils.showLog("广告没有在播放中: " + this.getInfo());
  436. throw ErrorNotPlaying;
  437. }
  438. try{
  439. utils.showLog("隐藏广告: " + this.getInfo());
  440. // TODO: 重复隐藏广告不会报错
  441. await FBInstant.hideBannerAdAsync();
  442. this._state = FB_AD_STATE.NONE;
  443. }catch(e){
  444. utils.showLog("隐藏广告失败: " + this.getInfo(), e);
  445. // 隐藏失败不做任何操作
  446. // this._state = FB_AD_STATE.NONE;
  447. throw e;
  448. }
  449. }
  450. }
  451. export default class FBAdManager{
  452. public static getVersion(){
  453. return "1.0.2";
  454. }
  455. private static _interstitialAds:Array<FBStatefulAdUnit> = [];
  456. private static _rewardedVideos:Array<FBStatefulAdUnit> = [];
  457. private static _banners:Array<FBBannerUnit> = [];
  458. private static _interstitialTimer:AdTimer = null;
  459. private static _rewardedVideoTimer:AdTimer = null;
  460. private static _bannerTimer:AdTimer = null;
  461. private static _bannerSupport = undefined;
  462. // 插屏广告默认参数
  463. public static defaultInterstitialOption:FBAdOption = {
  464. autoLoadOnPlay: FB_AUTO_LOAD_ON_PLAY,
  465. maxLoadError: FB_MAX_INTERSTITIAL_ERROR,
  466. };
  467. // 激励视频默认参数
  468. public static defaultRewardedVideoOption:FBAdOption = {
  469. autoLoadOnPlay: FB_AUTO_LOAD_ON_PLAY,
  470. maxLoadError: FB_MAX_REWARDED_VIDEO_ERROR,
  471. };
  472. // banner默认参数
  473. public static defaultBannerOption:FBAdOption = {
  474. autoLoadOnPlay: FB_AUTO_LOAD_ON_PLAY, // banner不需要这个参数
  475. maxLoadError: FB_MAX_BANNER_ERROR,
  476. };
  477. // 插屏广告计时器默认参数
  478. public static defaultInterstitialTimerOption:AdTimerOption = {
  479. refreshInterval: FB_INTERSTITIAL_REFRESH_INTERVAL,
  480. delayForFirstAd: FB_AD_DELAY_FOR_FIRST_INTERSTITIAL
  481. };
  482. // 激励视频计时器默认参数
  483. public static defaultRewardedVideoTimerOption:AdTimerOption = {
  484. refreshInterval: FB_REWARDED_VIDEO_REFRESH_INTERVAL,
  485. delayForFirstAd: FB_AD_DELAY_FOR_FIRST_REWARDED_VIDEO
  486. };
  487. // banner计时器默认参数
  488. public static defaultBannerTimerOption:AdTimerOption = {
  489. refreshInterval: FB_BANNER_REFRESH_INTERVAL,
  490. delayForFirstAd: FB_AD_DELAY_FOR_FIRST_BANNER
  491. };
  492. // 1.1 添加插屏广告
  493. // 返回已经添加的插屏广告总数
  494. public static addInterstitial(id:string, count:number=FB_INIT_AD_COUNT){
  495. if(this._interstitialTimer == null){
  496. this._interstitialTimer = new AdTimer(this.defaultInterstitialTimerOption.refreshInterval, this.defaultInterstitialTimerOption.delayForFirstAd);
  497. }
  498. for(let i=0;i<count;i++){
  499. if(this._interstitialAds.length >= FB_MAX_AD_INSTANCE){
  500. utils.showLog("添加插屏广告失败, 超出限制: " + this._interstitialAds.length, id);
  501. throw ErrorTooManyAdInstance;
  502. }
  503. let adUnit = new FBInterstitialUnit(id, this._interstitialTimer, this.defaultInterstitialOption);
  504. this._interstitialAds.push(adUnit);
  505. utils.showLog("添加插屏广告: " + id, "count: " + this._interstitialAds.length);
  506. }
  507. return this._interstitialAds.length;
  508. }
  509. // 1.2. 添加激励视频广告
  510. // 返回已经添加的激励视频总数
  511. public static addRewardedVideo(id:string, count:number=FB_INIT_AD_COUNT){
  512. if(this._rewardedVideoTimer == null){
  513. this._rewardedVideoTimer = new AdTimer(this.defaultRewardedVideoTimerOption.refreshInterval, this.defaultRewardedVideoTimerOption.delayForFirstAd);
  514. }
  515. for(let i=0;i<count;i++){
  516. if(this._rewardedVideos.length >= FB_MAX_AD_INSTANCE){
  517. utils.showLog("添加激励视频广告失败, 超出限制: " + this._rewardedVideos.length, id);
  518. throw ErrorTooManyAdInstance;
  519. }
  520. let adUnit = new FBRewardedVideoUnit(id, this._rewardedVideoTimer, this.defaultRewardedVideoOption);
  521. this._rewardedVideos.push(adUnit);
  522. utils.showLog("添加激励视频广告: " + id, "count: " + this._rewardedVideos.length);
  523. }
  524. return this._rewardedVideos.length;
  525. }
  526. // 1.3. 添加Banner广告
  527. public static addBanner(id:string){
  528. if(this._bannerTimer == null){
  529. this._bannerTimer = new AdTimer(this.defaultBannerTimerOption.refreshInterval, this.defaultBannerTimerOption.delayForFirstAd);
  530. }
  531. let adUnit = new FBBannerUnit(id, this._bannerTimer, this.defaultBannerOption);
  532. this._banners.push(adUnit);
  533. utils.showLog("添加Banner广告: " + id, "count: " + this._banners.length);
  534. return adUnit;
  535. }
  536. // 2. 初始化和预加载
  537. // Deprecated 此方法用于保持兼容, 建议使用 loadAllAsync
  538. public static async loadAll(){
  539. utils.showLog("初始化广告队列");
  540. return await this.loadAllAsync();
  541. }
  542. // 异步顺序预加载所有广告
  543. public static async loadAllAsync(){
  544. utils.showLog("FBAdManager Version: " + this.getVersion());
  545. utils.showLog("初始化广告队列");
  546. // 两次加载间间隔N秒
  547. // 先加载激励视频
  548. for(let i=0;i<this._rewardedVideos.length;i++){
  549. const adUnit = this._rewardedVideos[i];
  550. if(i>0){
  551. await waitTimeSecond(0.1);
  552. }
  553. try{
  554. await adUnit.loadAsync();
  555. }catch(e){
  556. }
  557. }
  558. // 之后加载插屏
  559. for(let i=0;i<this._interstitialAds.length;i++){
  560. const adUnit = this._interstitialAds[i];
  561. if(i>0){
  562. await waitTimeSecond(0.1);
  563. }
  564. try{
  565. await adUnit.loadAsync();
  566. }catch(e){
  567. }
  568. }
  569. }
  570. private static _isAdReady(type: FB_AD_TYPE){
  571. let adUnits = (type == FB_AD_TYPE.INTERSTITIAL)?this._interstitialAds:this._rewardedVideos;
  572. let isReady = false;
  573. for(let i=0;i<adUnits.length;i++){
  574. const adUnit = adUnits[i];
  575. if(adUnit.isReady() && adUnit.isReadyToRefresh()){
  576. isReady = true;
  577. break;
  578. }
  579. }
  580. return isReady;
  581. }
  582. private static _showAsync(type: FB_AD_TYPE){
  583. let adUnits = (type == FB_AD_TYPE.INTERSTITIAL)?this._interstitialAds:this._rewardedVideos;
  584. let readyUnit:FBStatefulAdUnit = null;
  585. for(let i=0;i<adUnits.length;i++){
  586. const adUnit = adUnits[i];
  587. if(adUnit.isReady() && adUnit.isReadyToRefresh()){
  588. readyUnit = adUnit;
  589. break;
  590. }
  591. }
  592. if(readyUnit != null){
  593. return readyUnit.showAsync();
  594. }
  595. throw ErrorNoReadyAdInstance;
  596. }
  597. private static _getAdTimer(type: FB_AD_TYPE){
  598. if(type == FB_AD_TYPE.INTERSTITIAL){
  599. return this._interstitialTimer;
  600. }
  601. if(type == FB_AD_TYPE.REWARDED_VIDEO){
  602. return this._rewardedVideoTimer;
  603. }
  604. return this._bannerTimer;
  605. }
  606. // 3.1. 判断是否可以播放插屏广告
  607. public static isInterstitialAdReady(){
  608. return this._isAdReady(FB_AD_TYPE.INTERSTITIAL);
  609. }
  610. // 4.1. 播放插屏广告
  611. public static async showInterstitialAd(){
  612. return await this._showAsync(FB_AD_TYPE.INTERSTITIAL);
  613. }
  614. // 3.2. 判断是否可以播放激励视频广告
  615. public static isRewardedVideoReady(){
  616. return this._isAdReady(FB_AD_TYPE.REWARDED_VIDEO);
  617. }
  618. // 4.2. 播放激励视频广告
  619. public static async showRewardedVideo(){
  620. return await this._showAsync(FB_AD_TYPE.REWARDED_VIDEO);
  621. }
  622. // 6. 检查是否支持对应API
  623. public static checkApiSupport(api:string){
  624. if(FBInstant.getSupportedAPIs().indexOf(api) >= 0){
  625. return true;
  626. }
  627. else{
  628. return false;
  629. }
  630. }
  631. // 6.1. 是否支持banner
  632. public static isBannerSupport(){
  633. if(typeof this._bannerSupport == "undefined"){
  634. this._bannerSupport = this.checkApiSupport(FB_API_BANNER);
  635. }
  636. return this._bannerSupport;
  637. }
  638. // 3.3. banner广告是否可以刷新或者重新加载
  639. public static isBannerReady(){
  640. if(this._banners.length <= 0){
  641. throw ErrorNoBannerAdInstance;
  642. }
  643. let adUnit = this._banners[0];
  644. return adUnit.isReadyToRefresh();
  645. }
  646. // 4.3. 播放默认banner广告
  647. public static async showBannerAsync(){
  648. if(!this.isBannerSupport()){
  649. throw ErrorApiNotSupport;
  650. }
  651. if(this._banners.length <= 0){
  652. throw ErrorNoBannerAdInstance;
  653. }
  654. let adUnit = this._banners[0];
  655. return await adUnit.showAsync();
  656. }
  657. // 5.3. 隐藏默认banner广告
  658. public static async hideBannerAsync(){
  659. if(!this.isBannerSupport()){
  660. throw ErrorApiNotSupport;
  661. }
  662. if(this._banners.length <= 0){
  663. throw ErrorNoBannerAdInstance;
  664. }
  665. let adUnit = this._banners[0];
  666. return await adUnit.hideAsync();
  667. }
  668. }