Android iOS Unity JS API Media API 常见问题

开屏广告

开屏包含 开屏广告 与 开屏V+广告,下面分别介绍两种形态的接入文档。

开屏广告

开屏广告简介

开屏广告以App启动作为曝光时机,提供5s的可感知广告展示。用户可以点击广告跳转到目标页面;或者点击右上角的“跳过”按钮,跳转到app内容首页。

权限等级:开放

适用场景:开屏广告会在您的应用开启时加载,拥有固定展示时间(一般为5秒),展示完毕后自动关闭并进入您的应用主界面。

分类:开屏广告分为全屏和半屏广告,开屏全屏广告有更沉浸的广告播放体验,变现效果更佳:

全屏 半屏

主要 API

加了删除线的方法即将废弃。

SplashAD

方法名 方法介绍
SplashAD(Activity activity, String posId, SplashADListener adListener) 构造方法,广告相关状态会通过 adListener 通知开发者。参数说明:activity(展示广告的 activity)、posId(广告位 ID)、adListener(广告状态监听器)。
SplashAD(Activity activity, String posId, SplashADListener adListener, int fetchDelay) 构造方法,这个方法可以通过传入 fetchDelay 参数,设置开屏广告从请求到展示所花的最大时长(并不是指广告曝光时长),取值范围为[1500, 5000]ms。如果需要使用默认值,可以调用上一个构造方法,或者给 fetchDelay 设为0。
SplashAD(Activity activity, View skipContainer, String posId, SplashADListener adListener, int fetchDelay) 构造方法,这个方法可以通过传入传入 skipContainer 参数,支持开发者自定义的跳过按钮。SDK 要求skipContainer一定在传入后要处于 VISIBLE 状态(一定要在构造方法之前将 skipContainer 处于 VISIBLE 状态,不要在onADPresent等广告回调中再修改为可见),且其宽高都不得小于3x3dp。如果需要使用 SDK 默认的跳过按钮,可以选择上面两个构造方法。
SplashAD(Activity activity, String appId, String posId,SplashADListener adListener) 构造方法,广告相关状态会通过 adListener 通知开发者。参数说明:activity(展示广告的 activity)、appId(媒体 ID)、posId(广告位 ID)、adListener(广告状态监听器)。
SplashAD(Activity activity, String appId, String posId,SplashADListener adListener, int fetchDelay) 构造方法,这个方法可以通过传入 fetchDelay 参数,设置开屏广告从请求到展示所花的最大时长(并不是指广告曝光时长),取值范围为[1500, 5000]ms。如果需要使用默认值,可以调用上一个构造方法,或者给 fetchDelay 设为0。
SplashAD(Activity activity, View skipContainer, String appId, String posId,SplashADListener adListener, int fetchDelay) 构造方法,这个方法可以通过传入传入 skipContainer 参数,支持开发者自定义的跳过按钮。SDK 要求skipContainer一定在传入后要处于 VISIBLE 状态,且其宽高都不得小于3x3dp。如果需要使用 SDK 默认的跳过按钮,可以选择上面两个构造方法。
preLoad() 预加载广告(4.330.1200版本后支持预加载效果广告,需要申请才会有效,之前版本只支持预加载合约广告),调用后开屏广告会尝试进行广告数据和图片/视频素材的预加载(若视频广告的视频素材未能提前预加载,在得到播放机会时将退化为图片广告进行播放),预加载的广告在得到展示机会时可以跳过广告素材下载的步骤而直接展示,该接口有频次控制,默认最小间隔2小时及以上调用才会生效,建议一天执行一次。
fetchAndShowIn(ViewGroup container) 拉取并展示广告,广告内容被填充到 container 参数指向的 ViewGroup,该 ViewGroup 原有的内容会被清空。
fetchAdOnly() 拉取广告,配合showAd使用,实现和fetchAndShowIn相同的功能。
showAd(ViewGroup container) 展示广告,配合fetchAdOnly使用,实现和fetchAndShowIn相同的功能。
fetchFullScreenAndShowIn(ViewGroup container) 拉取全屏广告并展示,广告内容被填充到 container 参数指向的 ViewGroup,该 ViewGroup 原有的内容会被清空。container 要铺满屏幕宽高,否则无法正常展示。
fetchFullScreenAdOnly() 拉取全屏广告,配合showFullScreenAd使用,实现和fetchFullScreenAndShowIn相同的功能。
showFullScreenAd(ViewGroup container) 配合fetchFullScreenAdOnly使用,实现和fetchFullScreenAndShowIn相同的功能。
setDeveloperLogo(int logoRes) 设置开发者 logo,仅在全屏广告时有效,参数要求是 Resource ID。
setDeveloperLogo(byte[] logoData) 设置开发者 logo,仅在全屏广告时有效,参数要求是能转为 Bitmap 的字节数组。
getECPMLevel() 获取本条广告实时的eCMP价格层级标签,每个层级标签对应线下预定的eCPM价格范围,成功返回一个包含数字的字符串,比如“1”,无权限调用该接口或后台异常会返回空字符串
getECPM() 获取本条广告实时的eCPM价格,单位是分;权限提示:调用此接口前,需要向优量汇运营接口人申请实时竞价权限,当无权限调用该接口时,SDK会返回错误码-1

SplashADListener

方法名 方法介绍
onNoAD(AdError error) 广告加载失败,error 对象包含了错误码和错误信息,错误码的详细内容可以参考文档第5章
onADDismissed() 广告关闭时调用,可能是用户关闭或者展示时间到。此时一般需要跳过开屏的 Activity,进入应用内容页面
onADPresent() 广告成功展示时调用,成功展示不等于有效展示(比如广告容器高度不够)
onADClicked() 广告被点击时调用,不代表满足计费条件(如点击时网络异常)
onADExposure() 广告曝光时调用
onADTick(long millisUntilFinished) 倒计时回调,返回广告还将被展示的剩余时间,单位是 ms
onADLoaded(long expireTimestamp) 广告加载成功的回调,在fetchAdOnly的情况下,表示广告拉取成功可以显示了。广告需要在SystemClock.elapsedRealtime <expireTimestamp前展示,否则在showAd时会返回广告超时错误。

接入注意事项


接入代码示例

(详细内容请参考压缩包中的代码示例,示例代码已过期,请参考压缩包的代码)
public class SplashActivity extends Activity implements SplashADListener {

  private SplashAD splashAD;
  private ViewGroup container;
  private ImageView splashHolder;
  private static final String SKIP_TEXT = "点击跳过 %d";

  public boolean canJump = false;
  private boolean needStartDemoList = true;

  /**
    * 为防止无广告时造成视觉上类似于"闪退"的情况,设定无广告时页面跳转根据需要延迟一定时间,demo
    * 给出的延时逻辑是从拉取广告开始算开屏最少持续多久,仅供参考,开发者可自定义延时逻辑,如果开发者采用demo
    * 中给出的延时逻辑,也建议开发者考虑自定义minSplashTimeWhenNoAD的值(单位ms)
    **/
  private int minSplashTimeWhenNoAD = 2000;
  /**
    * 记录拉取广告的时间
    */
  private long fetchSplashADTime = 0;
  private Handler handler = new Handler(Looper.getMainLooper());

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_splash);
    container = (ViewGroup) this.findViewById(R.id.splash_container);
    splashHolder = (ImageView) findViewById(R.id.splash_holder);
    boolean needLogo = getIntent().getBooleanExtra("need_logo", true);
    needStartDemoList = getIntent().getBooleanExtra("need_start_demo_list", true);
    if (!needLogo) {
      findViewById(R.id.app_logo).setVisibility(View.GONE);
    }
    // 如果targetSDKVersion >= 23,就要申请好权限。如果您的App没有适配到Android6.0(即targetSDKVersion < 23),那么只需要在这里直接调用fetchSplashAD接口。
    if (Build.VERSION.SDK_INT >= 23) {
      checkAndRequestPermission();
    } else {
      // 如果是Android6.0以下的机器,默认在安装时获得了所有权限,可以直接调用SDK
      fetchSplashAD(this, container, getPosId(), this, 0);
    }
  }

  private String getPosId() {
    String posId = getIntent().getStringExtra("pos_id");
    return TextUtils.isEmpty(posId) ? Constants.SplashPosID : posId;
  }

  /**
    *
    * ----------非常重要----------
    *
    * Android6.0以上的权限适配简单示例:
    *
    * 如果targetSDKVersion >= 23,那么必须要申请到所需要的权限,再调用优量汇SDK,否则优量汇SDK不会工作。
    *
    * Demo代码里是一个基本的权限申请示例,请开发者根据自己的场景合理地编写这部分代码来实现权限申请。
    * 注意:下面的`checkSelfPermission`和`requestPermissions`方法都是在Android6.0的SDK中增加的API,如果您的App还没有适配到Android6.0以上,则不需要调用这些方法,直接调用优量汇SDK即可。
    */
  @TargetApi(Build.VERSION_CODES.M)
  private void checkAndRequestPermission() {
    List<String> lackedPermission = new ArrayList<String>();
    if (!(checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED)) {
      lackedPermission.add(Manifest.permission.READ_PHONE_STATE);
    }

    if (!(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) {
      lackedPermission.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
    }

    if (!(checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)) {
      lackedPermission.add(Manifest.permission.ACCESS_FINE_LOCATION);
    }

    // 权限都已经有了,那么直接调用SDK
    if (lackedPermission.size() == 0) {
      fetchSplashAD(this, container, getPosId(), this, 0);
    } else {
      // 请求所缺少的权限,在onRequestPermissionsResult中再看是否获得权限,如果获得权限就可以调用SDK,否则不要调用SDK。
      String[] requestPermissions = new String[lackedPermission.size()];
      lackedPermission.toArray(requestPermissions);
      requestPermissions(requestPermissions, 1024);
    }
  }

  private boolean hasAllPermissionsGranted(int[] grantResults) {
    for (int grantResult : grantResults) {
      if (grantResult == PackageManager.PERMISSION_DENIED) {
        return false;
      }
    }
    return true;
  }

  @Override
  public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == 1024 && hasAllPermissionsGranted(grantResults)) {
      fetchSplashAD(this, container, getPosId(), this, 0);
    } else {
      // 如果用户没有授权,那么应该说明意图,引导用户去设置里面授权。
      Toast.makeText(this, "应用缺少必要的权限!请点击\"权限\",打开所需要的权限。", Toast.LENGTH_LONG).show();
      Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
      intent.setData(Uri.parse("package:" + getPackageName()));
      startActivity(intent);
      finish();
    }
  }

  /**
    * 拉取开屏广告,开屏广告的构造方法有3种,详细说明请参考开发者文档。
    *
    * @param activity        展示广告的activity
    * @param adContainer     展示广告的大容器
    * @param posId           广告位ID
    * @param adListener      广告状态监听器
    * @param fetchDelay      拉取广告的超时时长:取值范围[1500, 5000],设为0表示使用优量汇SDK默认的超时时长。
    */
  private void fetchSplashAD(Activity activity, ViewGroup adContainer, String posId, SplashADListener adListener, int fetchDelay) {
    fetchSplashADTime = System.currentTimeMillis();
    // skipContainer 此时必须是 VISIBLE 状态,否则将不能正常曝光计费
    splashAD = new SplashAD(activity, posId, adListener, fetchDelay);
    splashAD.fetchAndShowIn(adContainer);
  }

  @Override
  public void onADPresent() {
    Log.i("AD_DEMO", "SplashADPresent");
  }

  @Override
  public void onADClicked() {
    Log.i("AD_DEMO", "SplashADClicked clickUrl: "
        + (splashAD.getExt() != null ? splashAD.getExt().get("clickUrl") : ""));
  }

  /**
    * 倒计时回调,返回广告还将被展示的剩余时间。
    * 通过这个接口,开发者可以自行决定是否显示倒计时提示,或者还剩几秒的时候显示倒计时
    *
    * @param millisUntilFinished 剩余毫秒数
    */
  @Override
  public void onADTick(long millisUntilFinished) {
    Log.i("AD_DEMO", "SplashADTick " + millisUntilFinished + "ms");
  }

  @Override
  public void onADExposure() {
    Log.i("AD_DEMO", "SplashADExposure");
  }

  @Override
  public void onADDismissed() {
    Log.i("AD_DEMO", "SplashADDismissed");
    next();
  }

  @Override
  public void onNoAD(AdError error) {
    Log.i(
        "AD_DEMO",
        String.format("LoadSplashADFail, eCode=%d, errorMsg=%s", error.getErrorCode(),
            error.getErrorMsg()));
    /**
      * 为防止无广告时造成视觉上类似于"闪退"的情况,设定无广告时页面跳转根据需要延迟一定时间,demo
      * 给出的延时逻辑是从拉取广告开始算开屏最少持续多久,仅供参考,开发者可自定义延时逻辑,如果开发者采用demo
      * 中给出的延时逻辑,也建议开发者考虑自定义minSplashTimeWhenNoAD的值
      **/
    long alreadyDelayMills = System.currentTimeMillis() - fetchSplashADTime;//从拉广告开始到onNoAD已经消耗了多少时间
    long shouldDelayMills = alreadyDelayMills > minSplashTimeWhenNoAD ? 0 : minSplashTimeWhenNoAD
        - alreadyDelayMills;//为防止加载广告失败后立刻跳离开屏可能造成的视觉上类似于"闪退"的情况,根据设置的minSplashTimeWhenNoAD
    // 计算出还需要延时多久
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        if (needStartDemoList) {
          SplashActivity.this.startActivity(new Intent(SplashActivity.this, DemoListActivity.class));
        }
        SplashActivity.this.finish();
      }
    }, shouldDelayMills);
  }

  /**
    * 设置一个变量来控制当前开屏页面是否可以跳转,当开屏广告为普链类广告时,点击会打开一个广告落地页,此时开发者还不能打开自己的App主页。当从广告落地页返回以后,
    * 才可以跳转到开发者自己的App主页;当开屏广告是App类广告时只会下载App。
    */
  private void next() {
    if (canJump) {
      if (needStartDemoList) {
        this.startActivity(new Intent(this, DemoListActivity.class));
      }
      this.finish();
    } else {
      canJump = true;
    }
  }

  @Override
  protected void onPause() {
    super.onPause();
    canJump = false;
  }

  @Override
  protected void onResume() {
    super.onResume();
    if (canJump) {
      next();
    }
    canJump = true;
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    handler.removeCallbacksAndMessages(null);
  }

  /** 开屏页一定要禁止用户对返回按钮的控制,否则将可能导致用户手动退出了App而广告无法正常曝光和计费 */
  @Override
  public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME) {
      return true;
    }
    return super.onKeyDown(keyCode, event);
  }

}

开屏V+广告

开屏V+广告简介

开屏V+广告是一个5s-30s的视频广告,在5s开屏呈现的过程中,用户点击右上角的“进入首页”或5s曝光结束后,视频均将收缩到APP内右下角的小视窗继续播放。

权限等级:需要申请

适用场景:开屏V+广告会在您的应用开启时加载,广告曝光过程中用户点击右上角的“进入首页”或5s曝光结束后将收缩到APP首页右下角的小视窗继续播放。

分类:开屏V+广告分为半屏和全屏,其中半屏开屏V+广告支持开发者自定义设置开屏底部的界面,用以展示应用Logo等。具体种类可以在开发者平台进行选择:

半屏 全屏 悬浮状态 落地页
视频示例

主要 API

代码中*zoomOut*命名即为开屏V+相关逻辑。

SplashAD

开屏广告使用同样的方法,开屏V+新增的方法如下

方法名 方法介绍
zoomOutAnimationFinish() 开屏V+使用该接口(4.310.1180)版本之后,使用该接口,通知sdk,开屏V+的窗口已经动画结束,sdk会处理内部状态和显示关闭按钮,在显示开屏V+广告时必须回调该函数
Bitmap getZoomOutBitmap() 开屏V+使用该接口(4.310.1180)版本之后,执行动画时移动view前可以通过该接口获取当前视频的截图,放在被移除的地方,减少动画闪烁

SplashADZoomOutListener

方法名 方法介绍
onZoomOut() 开屏V+广告开始进入小窗悬挂状态,收到该回调时可以进行动画缩小广告的播放界面,动画完成后要调用zoomOutAnimationFinish
onZoomOutPlayFinish() 开屏V+的视频播放结束
isSupportZoomOut() 在使用SplashADZoomOutListener接口时不支持开屏V+广告可以在这里返回false

开屏V+ demo辅助函数

demo的动画提供了开屏V+辅助函数供开发者参考,详细细节可以参见demo代码,悬浮窗的尺寸,应尽量使用推荐的尺寸,必须宽高比为 16:9

方法名 方法介绍
startZoomOut 开屏V+动画实现参考,内部有定义推荐的悬浮窗的尺寸,应尽量使用推荐的尺寸,否则可能导致显示异常
setSplashInfo() 开屏V+动画实现参考,开屏使用单独的activity时,使用该方法在不同activty间传递数据

接入注意事项


接入代码示例

(关键代码展示,详细请参考压缩包的代码)

public class SplashADActivity extends Activity implements View.OnClickListener,
    AdapterView.OnItemSelectedListener, SplashADZoomOutListener {
  private View splashView;
  private SplashAD splashAD;
  private ViewGroup zoomOutView;
  private boolean isZoomOuted;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_splash_ad);
    findViewById(R.id.splashShowInView).setOnClickListener(this);
  }

  private String getPosID() {
    String posId = ((EditText) findViewById(R.id.posId)).getText().toString();
    return TextUtils.isEmpty(posId) ? PositionId.SPLASH_POS_ID : posId;
  }

  private boolean needLogo() {
    return ((CheckBox) findViewById(R.id.checkBox)).isChecked();
  }

  private boolean customSkipBtn(){
    return ((CheckBox)findViewById(R.id.checkCustomSkp)).isChecked();
  }

  @Override
  public void onClick(View v) {
    cleanZoomOut();
    switch (v.getId()) {
      case R.id.splashShowInView:
        showSplashInThisActivity();
        break;
    }
  }

  private void showSplashInThisActivity() {
    Log.d(TAG, "showSplashInThisActivity");
    DemoUtil.hideSoftInput(this);
    if (splashView == null) {
      ViewGroup contentView = findViewById(android.R.id.content);
      LayoutInflater inflater = getLayoutInflater();
      splashView = inflater.inflate(R.layout.activity_splash, null);
      splashView.setClickable(true);//显示出来的时候阻止点击事件传递到后面的按钮
      contentView.addView(splashView);
      Log.d(TAG, "contentView child count:" + contentView.getChildCount());
    } else if (splashView.getVisibility() == View.VISIBLE) {
      Toast.makeText(SplashADActivity.this.getApplicationContext(), "有开屏正在展示,忽略本次拉取",
          Toast.LENGTH_SHORT).show();
      Log.d(TAG, "有开屏正在展示,忽略本次拉取");
      return;
    } else {
      splashView.setVisibility(View.VISIBLE);
    }
    if (!needLogo()) {
      splashView.findViewById(R.id.app_logo).setVisibility(View.GONE);
    } else {
      splashView.findViewById(R.id.app_logo).setVisibility(View.VISIBLE);
    }
    TextView skipView = null;
    if (customSkipBtn()) {
      skipView = splashView.findViewById(R.id.skip_view);
      skipView.setVisibility(View.VISIBLE);
    } else {
      splashView.findViewById(R.id.skip_view).setVisibility(View.GONE);
    }
    ViewGroup container = splashView.findViewById(R.id.splash_container);
    container.removeAllViews();
    //因为SplashAD是和广告位绑定的,在广告位变化时需要重新创建
    splashAD = new SplashAD(this, skipView, getPosID(), this, 0);
    Log.d(TAG, "fetchAndShowIn");
    isZoomOuted = false;
    splashAD.fetchAndShowIn(container);
  }

  private void cleanZoomOut() {
    if (zoomOutView != null) {
      ViewUtils.removeFromParent(zoomOutView);
      zoomOutView = null;
    }
  }

  @Override
  public void onADDismissed() {
    splashView.setVisibility(View.GONE);
    if (isZoomOuted && zoomOutView != null) {
      ViewUtils.removeFromParent(zoomOutView);
    }
    Log.d(TAG, "onADDismissed");
  }

  @Override
  public void onNoAD(AdError error) {
    splashView.setVisibility(View.GONE);
    String str = String.format("LoadSplashADFail, eCode=%d, errorMsg=%s", error.getErrorCode(),
        error.getErrorMsg());
    Log.i("AD_DEMO", str);
    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        Toast.makeText(SplashADActivity.this.getApplicationContext(), str, Toast.LENGTH_SHORT).show();
      }
    });
  }

  @Override
  public void onADPresent() {
    Log.d(TAG, "onADPresent");
  }

  @Override
  public void onADClicked() {
    Log.d(TAG, "onADClicked");
  }

  @Override
  public void onADTick(long millisUntilFinished) {
    Log.d(TAG, "onADTick");
    if (splashView != null) {
      TextView skipView = splashView.findViewById(R.id.skip_view);
      if (skipView != null && skipView.getVisibility() == View.VISIBLE)
        skipView.setText(String.format(SKIP_TEXT, Math.round(millisUntilFinished / 1000f)));
    }
  }

  @Override
  public void onADExposure() {
    Log.d(TAG, "onADExposure");
  }

  @Override
  public void onADLoaded(long expireTimestamp) {
    Log.d(TAG, "onADLoaded");
  }

  @Override
  public void onZoomOut() {
    Log.d(TAG, "onZoomOut");
    isZoomOuted = true;
    SplashZoomOutManager splashZoomOutManager = SplashZoomOutManager.getInstance();
    View splash = ((ViewGroup) splashView.findViewById(R.id.splash_container)).getChildAt(0);
    if (splash == null) {
      Log.e(TAG, "在开屏展示的过程中进行了新的拉取,导致广告View被清空了");
      return;
    }
    splash.setVisibility(View.VISIBLE);
    ViewGroup content = findViewById(android.R.id.content);
    zoomOutView = splashZoomOutManager.startZoomOut(splash, content, content,
        new SplashZoomOutManager.AnimationCallBack() {
      @Override
      public void animationStart(int animationTime) {
        Log.d(TAG, "animationStart:" + animationTime);
      }
      @Override
      public void animationEnd() {
        Log.d(TAG, "animationEnd");
        splashAD.zoomOutAnimationFinish();
      }
    });
    splashView.setVisibility(View.GONE);
  }

  @Override
  public void onZoomOutPlayFinish() {
    Log.d(TAG, "onPlayFinish");
  }

  @Override
  public boolean isSupportZoomOut() {
    return true;
  }
}

Copyright © 1998 - 2020 Tencent Inc. All Rights Reserved.
腾讯公司 版权所有 | 加入优量汇