2010年11月9日火曜日

AdMobで広告が表示されない場合の対策

タイトル通りの記事です。Android版の「Army & Maiden」の開発で引っ掛かった部分です。

Androidアプリに広告を載せる方法としては、Google傘下のAdMobが選択肢になると思います。というわけで、こいつを昨晩から試していました。

しかし、このAdMobの広告が上手く表示されなかったのと、広告が配信されないことがあるのとで、きちんと上手く表示させるのに苦労しました。

軽量級のアプリだとほぼ成功するのですが、重量級のアプリだとけっこう失敗します。そこで、その対策と、広告取得失敗時の処理について書いておこうと思います。

最初、原因が分からずに、最小セットで現象を再現するのにえらく苦労しました。また、ネットに情報が少なく、非常に難渋しました。こんな馬鹿らしい実験で時間を使うのは、人類の時間の無駄使いなので解決方法をまとめておきます。

以下、表示例です。最初の画像が自社広告(適当な仮画像)。その後、正常に広告が取得できたら、AdMobの広告に切り換わります。





まず、AdMob自体の実装方法については、以下のURLの記事を参考にしてください。また、AdMob自体のドキュメントも参考になります。

□mucchinのAndroid戦記 - Androidアプリで広告収入を得れるAdMobへの登録方法
http://android.roof-balcony.com/admob/admob-regist/

□IT Pro - モバイル・アプリにAdMob広告を組み込む
http://itpro.nikkeibp.co.jp/article/COLUMN/20100601/348727/

□くにこら。 - Androidアプリに広告表示
http://blog.kunbe.net/?eid=1246989



以下、発生した現象です。

・小さなアプリを作って実装したら、広告はきちんと表示される。

・重量級のアプリに実装したら、広告がなぜか表示されない。

どうも、起動に時間がかかるアプリだと、広告が上手く取得できないみたいです。これは、単純な「時間」ではなく、メインスレッドに対しての「CPU空き時間」が関係しているようです。

メインスレッドが起動時に物凄い勢いで大量のファイルを読んだり、展開したりして、かなりの時間を消費するようなソフトでは、広告の取得が失敗します。

ピンポイントでの「ここ」というソース部分が原因ではなく、合計計算量が多いと発生するようでした。ソースが公開されていないので詳細は分かりません。



以下、解決方法です。

・「refreshInterval」の値を設定する。

基本的に、これだけで解決します。この値を設定した結果、ソフト起動時に、すぐに広告が表示されずに、時間差で広告が表示されるようになります。

値の単位は秒です。最初、小さな数字にしていたのですが、LogCatを見ていたら「13以上にしてね」と警告が出ていたので「13」にしました。

「Interval」なので、13秒に1回広告を読みに行くのかと思っていたのですが、一度読み込むとその13秒後に再度読みに行くことはありませんでした(もし今後読みに行くような現象が確認されたら、一度広告を読んだら、この値を書き換える処理を追加するつもりです)。

また、この「refreshInterval」の値を設定して、遅延で広告を表示させるようにすると、その間、広告表示領域が寂しいことになります。具体的に言うと、真っ黒のままになります。そこでこの領域に、自社広告を出すようにしました。

というわけで、この「広告取得エラー回避」と「自社広告」の部分のソースコードを以下に掲載します。



【app.xml】(アプリのレイアウトXMLファイル)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:myapp="http://schemas.android.com/apk/res/com.crocro.android.ArmyAndMaidenFree1"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/frm_lyt"
        android:layout_width="fill_parent"
        android:layout_height="50px">

        <com.admob.android.ads.AdView
            android:id="@+id/ad"
            android:layout_width="fill_parent"
            android:layout_height="50px"
            myapp:backgroundColor="#000000"
            myapp:primaryTextColor="#FFFFFF"
            myapp:secondaryTextColor="#CCCCCC"
            myapp:refreshInterval="13"
        />

        <ImageButton
            android:id="@+id/tmp_ad"
            android:src="@drawable/tmp_ad"
            android:background="@drawable/tmp_ad_bg"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
        />

    </FrameLayout>

    <com.crocro.wrp.dvc.and.AppView
        android:id="@+id/app_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
    />

</LinearLayout>



【tmp_ad_bg.xml】(自社広告の背景用、drawable用XMLファイル)

<?xml version="1.0" encoding="utf-8"?>
<bitmap
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/tmp_ad_bg_img"
    android:antialias="true"
    android:dither="false"
    android:filter="false"
    android:gravity="fill"
    android:tileMode="repeat"
/>



【tmp_ad.png】(drawable用画像ファイル)
【tmp_ad_bg_img.png】(drawable用画像ファイル)



【MyActivity.java:グローバル領域】

ImageButton gTmpAd = null;



【MyActivity.java:「onCreate」メソッド内の「setContentView」宣言の後】

// 仮広告ボタン
gTmpAd = (ImageButton)findViewById(R.id.tmp_ad);
gTmpAd.setOnClickListener(new OnClickListener() {
    @Override public void onClick(View v) {
        // URLを開く
        Intent intent = new Intent(
            Intent.ACTION_VIEW,
            Uri.parse(getResources().getString(R.string.tmp_ad_url))
        );
        startActivity(intent);
    }
});

// 広告デバッグ
AdManager.setTestDevices(new String[]{
    AdManager.TEST_EMULATOR, // Android emulator
});

// 広告表示
AdView adView = (AdView)findViewById(R.id.ad);
adView.setAdListener(new AdListener() {
    @Override public void onReceiveRefreshedAd(AdView arg0) {}
    @Override public void onReceiveAd(AdView arg0) {
        // 広告の切り替え
        FrameLayout fl = (FrameLayout)findViewById(R.id.frm_lyt);
        fl.removeView(gTmpAd);
        gTmpAd = null;
    }
    @Override public void onFailedToReceiveRefreshedAd(AdView arg0) {}
    @Override public void onFailedToReceiveAd(AdView arg0) {}
});
adView.requestFreshAd();



簡単な解説です。

「app.xml」で、広告領域を「FrameLayout」で囲んでいます。「FrameLayout」は、複数のビューをZ軸上に重ねて表示するレイアウトです。

「ImageButton」の方が後に宣言されていますので、起動時には、「com.admob.android.ads.AdView」を「ImageButton」が覆う形で初期化されます。

アプリ起動後に「MyActivity.java」で、この「ImageButton」のクリック時の動作(URLを開く)と、広告読み込み成功時の広告差し替え処理を実装しています。

広告を表示する「AdView」は、「setAdListener」メソッドで「AdListener」を登録すると、広告読み込み処理の結果を受け取れます。

そこで、「onReceiveAd」が呼ばれた際に「ImageButton」を削除するようにしています。「ImageButton」が削除されると、「FrameLayout」内でこの下にあった「com.admob.android.ads.AdView」が前面に出てくるようになります。

これで、起動時には自社広告。AdMobの広告取得が成功したら、AdMobの広告が表示されるようになります。

ちなみに「com.crocro.wrp.dvc.and.AppView」はアプリの本体です。これは「SurfaceView」を継承したものです。



というわけで、Android版の「Army & Maiden」も、リリースがだいぶ近づいてきました。

0 件のコメント:

コメントを投稿