Skip to content

oppo 快应用广告多次回调问题

1.遇到的问题:

可能是因为在 oppo 快应用中,每个页面都是一个单独的 NativeObject 实例,当同时发起两个请求时,它们的回调函数可能会被存储在同一个 NativeObject 实例的同一个成员变量中,导致第二个请求的回调函数会覆盖第一个请求的回调函数,从而导致第二个请求的回调函数被触发时,也会触发第一个请求的回调函数,

有问题代码如下:

js
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();
  });
};

针对问题,想解决方案:

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

3.目前在用的

因为造成这个问题的根源是同时请求了两个广告,这个是 oppo 快应用存在的问题,但是需求是的确需要同时请求多个广告,所以就是前面广告执行完,销毁了广告的回调监听,然后再走下一个广告的拉取,伪代码如下:

js
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;
}

这个是目前能想到的最好的办法,在这个示例代码中,requestAds()函数会按照顺序依次请求三种类型的广告(横幅广告、插屏广告、激励视频广告),并将每个广告的实例添加到ads数组中。requestAd()函数返回一个 Promise 对象,使用await关键字可以等待其完成后再执行下一个循环。

但是因为我的需求是多个组件之间的交互,并没有实际去操作,不知道会遇到什么问题,所以还并没有是实际的实现,目前在用的方法就是定义一个变量,每次只让一个请求进来,其他的直接走 err (做是可以做,千万别让老板知道)

这个具体的多个页面实现还没空写,直接重构的话,害怕影响其他的机型,等下个产品再来吧

最近更新