oppo 快应用广告多次回调问题
# 1.遇到的问题:
可能是因为在 oppo 快应用中,每个页面都是一个单独的 NativeObject 实例,当同时发起两个请求时,它们的回调函数可能会被存储在同一个 NativeObject 实例的同一个成员变量中,导致第二个请求的回调函数会覆盖第一个请求的回调函数,从而导致第二个请求的回调函数被触发时,也会触发第一个请求的回调函数,
有问题代码如下:
const ad = require("@service.ad");
const getOppoNativeAd = (adUnitId) => {
return new Promise((resolve, reject) => {
const nativeAd = ad.createNativeAd({
adUnitId: adUnitId,
});
nativeAd.onLoad((e) => {
e.adList[0].nativeAd = nativeAd;
e.adList[0].isad = true;
e.adList[0].adUnitId = adUnitId;
// 上报曝光
nativeAd.reportAdShow({
adId: e.adList[0].adId,
});
nativeAd.offLoad();
nativeAd.offError();
resolve(e.adList[0]);
});
nativeAd.onError((err) => {
nativeAd.offLoad();
nativeAd.offError();
reject(err);
});
nativeAd.load();
});
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 针对问题,想解决方案:
# 1.(pass)
在多次执行 onLoad
回调时,如果要确定哪次回调属于当前请求,可以在每次请求时给 nativeAd
对象添加一个唯一标识,比如可以添加一个随机数或时间戳,然后在 onLoad
回调中判断这个标识是否与当前请求匹配
const ad = require("@service.ad")
const adRequests = {}
const getOppoNativeAd = (adUnitId) => {
const requestId = Date.now()
adRequests[requestId] = { adUnitId }
return new Promise((resolve, reject) => {
const nativeAd = ad.createNativeAd({
adUnitId: adUnitId
})
nativeAd.onLoad((e) => {
const request = adRequests[requestId]
if (request && request.adUnitId === adUnitId) {
delete adRequests[requestId]
e.adList[0].nativeAd = nativeAd
e.adList[0].isad = true
e.adList[0].adUnitId = adUnitId
nativeAd.offLoad()
nativeAd.offError()
resolve(e.adList[0])
}
})
nativeAd.onError((err) => {
const request = adRequests[requestId]
if (request && request.adUnitId === adUnitId) {
delete adRequests[requestId]
nativeAd.offLoad()
nativeAd.offError()
reject(err)
}
})
nativeAd.load()
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 2.(pass)
对每个请求都创建一个独立的 nativeAd
对象,以避免出现并发请求的问题。
同时,你可以在 nativeAd
对象上设置一个自定义属性,例如 requestId
,在回调函数中判断这个属性是否和当前请求的 ID 一致,如果不一致则不处理这个回调。可以将请求 ID 存储在请求的 Promise
对象中,每个请求的 ID 都应该是唯一的。
const ad = require('@service.ad')
let requestId = 0
/**
* 请求快应用广告
* @param {string} adUnitId 广告位 ID
* @return {Promise} Promise 对象,resolve 回调函数传递一个广告对象,reject 回调函数传递一个错误对象
*/
function requestNativeAd(adUnitId) {
requestId++
const nativeAd = ad.createNativeAd({ adUnitId })
return new Promise((resolve, reject) => {
const currentRequestId = requestId
nativeAd.onLoad((event) => {
if (currentRequestId !== requestId) {
return
}
const adObj = event.adList[0]
adObj.nativeAd = nativeAd
adObj.isad = true
adObj.adUnitId = adUnitId
resolve(adObj)
})
nativeAd.onError((event) => {
if (currentRequestId !== requestId) {
return
}
reject(event)
})
nativeAd.load()
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 3.目前在用的
因为造成这个问题的根源是同时请求了两个广告,这个是 oppo 快应用存在的问题,但是需求是的确需要同时请求多个广告,所以就是前面广告执行完,销毁了广告的回调监听,然后再走下一个广告的拉取,伪代码如下:
async function requestAds() {
const adTypes = [AdType.BANNER, AdType.INTERSTITIAL, AdType.REWARDED_VIDEO];
const ads = [];
for (let i = 0; i < adTypes.length; i++) {
const adType = adTypes[i];
const ad = await requestAd(adType);
ads.push(ad);
}
return ads;
}
2
3
4
5
6
7
8
9
10
11
12
这个是目前能想到的最好的办法,在这个示例代码中,requestAds()
函数会按照顺序依次请求三种类型的广告(横幅广告、插屏广告、激励视频广告),并将每个广告的实例添加到ads
数组中。requestAd()
函数返回一个 Promise 对象,使用await
关键字可以等待其完成后再执行下一个循环。
但是因为我的需求是多个组件之间的交互,并没有实际去操作,不知道会遇到什么问题,所以还并没有是实际的实现,目前在用的方法就是定义一个变量,每次只让一个请求进来,其他的直接走 err (做是可以做,千万别让老板知道)
这个具体的多个页面实现还没空写,直接重构的话,害怕影响其他的机型,等下个产品再来吧