Androidで動画広告を再生するための仕組みを最短で実装する方法

Androidエンジニアのnissiyです。 iQONではアプリの収益化のために、ページによってネイティブ広告や動画広告の掲載を行っています。

先日アプリのアップデートで動画広告に関してのアップデートを行いましたが、案件の都合で広告再生の仕組みを外部アドネットワークのSDKを使用せずに自作することにしました。 しかし、実装に多くの時間を割くことができなかったため、Androidの標準の仕組みをベースにポイントを押さえながら最短で実装を行いました。

実装した動画広告は以下のようなインタースティシャルの動画広告になります。

f:id:vasilyjp:20160819170306p:plain

アプリのOSバージョンと導入スピードからVideoViewを選択

現在Androidには動画や音楽を提供するための仕組みがいくつか存在します。 代表的なものだと、APIレベル1から存在する標準の仕組みであるMediaPlayer、2014年に登場したGoogle製の外部ライブラリであるExoPlayerがあります。

本当は新しい仕組みであるExoPlayerを使用して実装したかったのですが、ExoPlayerは内部でMediaCodecを使用しているのでAPIレベル16以上でないと動作しないため、ExoPlayerの導入を諦めました(iQONはminSdkVersionが15で、APIレベル15の端末のユーザーが多くいる)。

そこでMediaPlayerを利用することに決めましたが、導入スピードを考えてMediaPlayerとSurfaceViewを内包して作られているVideoViewを使用することにしました。 VideoViewはAPIレベル1から存在する標準の仕組みであり、レイアウトファイルに記述すれば最小で以下のコードだけで動画を再生することができます。

ネットワーク上の動画を再生する場合

VideoView videoView = (VideoView) findViewById(R.id.video_view);
videoView.setVideoURI(Uri.parse(<動画ファイルのURL>));
videoView.start();

ローカルの動画を再生する場合

VideoView videoView = (VideoView) findViewById(R.id.video_view);
videoView.setVideoPath(<動画ファイルのパス>);
videoView.start();

動画広告が再生されても、他のメディアプレイヤーの再生を止めないようにする

VideoViewをそのまま使用する場合、Google Play Musicなどの音楽プレイヤーを使用している状態で動画広告の再生をスタートしてしまうと音楽プレイヤーの再生が止まってしまいます。 動画がメインのアプリの場合は全く問題ありませんが、動画広告や無音動画しか再生されないアプリの場合にこの挙動は非常に不便です。

なぜこのようなことが起こるかというと、VideoView内で明示的に他のプレイヤーの音を止める処理が書かれているからです。

// VideoView.java
private void openVideo() {
    if (mUri == null || mSurfaceHolder == null) {
        // not ready for playback just yet, will try again later
        return;
    }
    // we shouldn't clear the target state, because somebody might have
    // called start() previously
    release(false);

    // この2行が他のプレイヤーの音を止める処理
    AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
    am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

    // 以下省略...

この2行を無効にするために今回はこちらのGistを参考にVideoViewをカスタマイズしました。 カスタマイズしたViewを使用することで、動画広告が再生されても他のプレイヤーの音が止まることは無くなりました。

VideoViewをラップしたCustomViewで広告画面を実装

透過したマスクをかけた上にインタースティシャルの動画広告を再生させるために画面全体をCustomViewで実装しました。 レイアウトは以下のような構成になります。

<jp.vasily.iqon.ui.InterstitialMovieLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/interstitial_movie_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#b2000000"
    android:clickable="true"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:visibility="gone">

    <jp.vasily.iqon.ui.CustomVideoView
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center" />

</jp.vasily.iqon.ui.InterstitialMovieLayout>

最初は広告画面全体を別Activityとして実装していましたが、VideoView内のSurfaceViewが厄介で広告の表示・非表示の制御が上手くいきませんでした。 そのためCustomViewで全体を覆うようにして、Visibilityの変更をトリガーに広告画面の表示・非表示と、動画の再生・停止を制御するようにしました。

最後に

動画広告を再生するための仕組みを紹介しましたが、広告は案件次第で使うAPIやLibraryに制限が出る場合があるため、今回のVideoViewを使った実装はあくまで一例に過ぎないことを覚えておいてください。 また、今回の内容は動画広告だけでなくライトな動画を再生するためにも有効な方法だと思いますので、別の用途でも参考にしていただければと思います。

VASILYではiQONを一緒に開発してくれるAndroidエンジニアを募集しています。興味がある方は以下のリンクをご覧ください。