Androidアプリ開発

AdMob無効なトラフィック対策

この記事は約24分で読めます。
記事内に広告が含まれています。
スポンサーリンク

こんにちは、まっさん(@Tera_Msaki)です。

この記事はAndroidスマホ用のアプリ開発の中で、
今後の開発で再使用性が高いと思われるコーディングをまとめたものです。
Javaでの開発経験、XML構文規則、Androidのアプリ開発経験がある方を対象としています。
Androidのアプリ開発でお役にたててれば、嬉しいです。
(これからAndroidのアプリ開発やJavaでの開発を始めたい方への案内は、記事の最後で紹介します)

◎テーマ
無効なトラフィック対策として、アプリ上に配置したAdMob広告を連続クリックできないようにしたい

◎ポイント
無効なトラフィック問題によるAdMob広告配信の制限というリスクを緩和するためには、アプリ側でAdMob広告を連続クリックできないよう、アプリ側で対策する必要があります。

◎対応前

AdMob広告を表示中は連続してクリックが可能(連続クリックは無効なトラフィックとして扱われる)

◎対応後

AdMob広告をクリックした場合、一定時間AdMob広告を表示しない

◎レイアウトXML(activity_main.xml)

	:

    <com.google.android.gms.ads.AdView
        android:id="@+id/adView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="invisible"
        ads:adSize="BANNER"
        ads:adUnitId="@string/adUnitId"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

	:

広告ユニットID(adUnitId)は、stings.xmlにて定義。

◎Java 制御部分のコーディング(MainActivity.java)

	:

public class MainActivity extends AppCompatActivity {
    private static final boolean        DEBUG = false;
    private static final String         TAG = MainActivity.class.getSimpleName();
    private Context                     context;
    private Bundle                      bundle;
    private CustomDialogParcel[]        customDialogParcels;
    private PropertiesHandler           propertiesHandler;
    private ArrayAdapter<String>        arrayAdapter;
    private List<String>                keyValues;
    private AdView                      adView;
    private long                        WAIT;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main);
        // Google Mobile Ads SDK を初期化
        MobileAds.initialize(getApplicationContext(), initializationStatus -> {
            if (DEBUG) Log.d(TAG,"MobileAds:onInitialComplete");
        });
        //テンポラリファイル作成
        PropertiesHandler temporaryHandler = new PropertiesHandler(context);
        if (!temporaryHandler.isPropertiesFile(String.format("%s.temporary",context.getString(R.string.app_name)))) {
            temporaryHandler.setProperty(String.format("%s.temporary",context.getString(R.string.app_name)),"AD_CLICKED_TIME", "0");
        }
	:

    }
    @Override
    protected void onResume() {
        super.onResume();
        if (DEBUG) Log.d(TAG, "onResume");
        context = getApplicationContext();
	:

        if (adView != null) {
            adView.resume();
        } else {
            adView = findViewById(R.id.adView);
        }
        AdMob adMob = new AdMob(context, adView);
	:
        //一時ファイル
        PropertiesHandler temporaryHandler = new PropertiesHandler(context);
        String temporary = String.format("%s.temporary",context.getString(R.string.app_name));
        long AD_CLICKED_TIME = Long.parseLong(temporaryHandler.getProperty(temporary, "AD_CLICKED_TIME", String.valueOf(System.currentTimeMillis())));
	:

        //広告
        adView.setVisibility(System.currentTimeMillis() - AD_CLICKED_TIME > WAIT ? View.VISIBLE : View.INVISIBLE);
	:

AdMob広告の設定や制御は、AdMobクラスとして実装しています。
アクティビティでは、AdMobクラスのインスタンス化とAdMob広告の表示を制御しています。
AdMob広告のクリックで、クリック時刻を一時ファイルに出力しています。
一時ファイルとしている理由は、アプリ終了時も値を永続化させるためです。
AdMob広告は、setVisibilityメソッドで前回クリックから待ち時間(WAIT)を経過している場合のみ表示しています。
待ち時間(WAIT)は、お好み(数分が望ましい)で定数(ミリ秒)で定義してください。

◎Java AdMobクラスのコーディング(AdMob.java)

	:

public class AdMob {
    private static final boolean        DEBUG = false;
    private static final String         TAG = AdMob.class.getSimpleName();

    //コンストラクタ
    public AdMob(Context context, AdView adView) {
        AdRequest adRequest = new AdRequest.Builder().build();
        adView.loadAd(adRequest);
        adView.setAdListener(new AdListener() {
            @Override
            public void onAdImpression() {
                if (DEBUG) Log.d(TAG, "onAdImpression");
                super.onAdImpression();
            }
            @Override
            public void onAdClicked() {
                String temporary = String.format("%s.temporary",context.getString(R.string.app_name));
                PropertiesHandler temporaryHandler = new PropertiesHandler(context);
                temporaryHandler.setProperty(temporary, "AD_CLICKED_TIME", String.valueOf(System.currentTimeMillis()));
                super.onAdClicked();
            }
            @Override
            public void onAdLoaded() {
                if (DEBUG) Log.d(TAG, "Code to be executed when an ad finishes loading.");
            }
            @Override
            public void onAdOpened() {
                if (DEBUG) Log.d(TAG, "Code to be executed when an ad opens an overlay that covers the screen.");
            }
            @Override
            public void onAdClosed() {
                if (DEBUG) Log.d(TAG, "Code to be executed when when the user is about to return to the app after tapping on an ad.");
            }
            @Override
            public void onAdFailedToLoad(@NonNull LoadAdError error) {
                // Gets the domain from which the error came.
                String errorDomain = error.getDomain();
                // Gets the error code. See
                // https://developers.google.com/android/reference/com/google/android/gms/ads/AdRequest#constant-summary
                // for a list of possible codes.
                int errorCode = error.getCode();
                // Gets an error message.
                // For example "Account not approved yet". See
                // https://support.google.com/admob/answer/9905175 for explanations of
                // common errors.
                String errorMessage = error.getMessage();
                // Gets additional response information about the request. See
                // https://developers.google.com/admob/android/response-info for more
                // information.
                ResponseInfo responseInfo = error.getResponseInfo();
                // Gets the cause of the error, if available.
                AdError cause = error.getCause();
                // All of this information is available via the error's toString() method.
                if (DEBUG) Log.d(TAG, error.toString());
            }
        });
    }
    //ネットワーク接続確認
    public boolean netWorkCheck(Context context){
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkCapabilities networkCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.getActiveNetwork());
        return networkCapabilities != null;
    }
}

クリックした時間は一時ファイルとして、内部ストレージに格納(PropertiesHandlerクラスを使用)
AdMob広告をクリックして、リスナーがフックする前に広告を表示するため、super.onAdClicked() の呼び出しを制御する方法はとれませんでした。

◎Java PropertiesHandlerクラスのコーディング(PropertiesHandler.java)

			:
public class PropertiesHandler {
    private static final boolean    DEBUG = false;
    private static final String     TAG = PropertiesHandler.class.getSimpleName();
    private final File              path;
    private String[]                keyValues;
    private List<String>            keyValueList;

    //コンストラクタ
    public PropertiesHandler(Context context) {
        path = context.getFilesDir();
    }

    //プロパティファイル存在確認
    public boolean isPropertiesFile(String propertiesFile) {
        File file = new File(path, propertiesFile);
        return file.exists();
    }
    //プロパティ読込
    public String getProperty(String propertiesFile,
                              String key,
                              String defaultValue) {
        Properties properties = new Properties();
        String value = null;
        try (FileInputStream fileInputStream = new FileInputStream(new File(path, propertiesFile).toString())) {
            properties.load(fileInputStream);
            value = properties.getProperty(key);
            if (value == null) {
                //プロパティがない場合は追加
                setProperty(propertiesFile, key ,defaultValue);
                value = defaultValue;
            }
            switch (isValueType(defaultValue)) {
                case InputType.TYPE_CLASS_TEXT:
                    break;
                case InputType.TYPE_CLASS_NUMBER:
                    value = isValueType(value) == InputType.TYPE_CLASS_NUMBER ? value: defaultValue;
                    break;
                case InputType.TYPE_NUMBER_FLAG_DECIMAL:
                    value = isValueType(value) == InputType.TYPE_NUMBER_FLAG_DECIMAL ? value: defaultValue;
                    break;
                default:
            }
        } catch (IOException e) {
            Log.d(TAG, Objects.requireNonNull(e.getMessage()));
            e.printStackTrace();
        }
        if (DEBUG) Log.d(TAG, String.format("%s:%s:%s", key, value, defaultValue));
        return value;
    }
    //プロパティ書込
    public void setProperty(String propertiesFile,
                            String key,
                            String newValue) {
        Properties properties = new Properties();
        getProperties(propertiesFile);
        if (keyValues != null) {
            for (int i = 0; i < keyValues.length; i++) {
                //行頭、行末の空白を取り除く
                keyValues[i] = keyValues[i].trim();
                if (keyValues[i] == null || keyValues[i].length() == 0) {
                    //空白
                } else if (keyValues[i].charAt(0) == '#') {
                    //コメント
                } else if (keyValues[i].charAt(0) == '['
                        && keyValues[i].charAt(keyValues[i].length() - 1) == ']') {
                    //セクション
                } else if (keyValues[i].length() >= 3
                        && keyValues[i].contains("=")
                        && keyValues[i].length() > keyValues[i].indexOf("=") + 1) {
                    //キー&バリュー
                    properties.setProperty(keyValues[i].substring(0, keyValues[i].indexOf("=")),
                            keyValues[i].substring(keyValues[i].indexOf("=") + 1));
                }
            }
            properties.remove(key);
        }
        properties.put(key, newValue);
        try (FileOutputStream fileOutputStream = new FileOutputStream(new File(path, propertiesFile).toString())) {
            properties.store(fileOutputStream, "");
        } catch (IOException e) {
            Log.d(TAG, Objects.requireNonNull(e.getMessage()));
            e.printStackTrace();
        }
    }
    //全プロパティ読込
    public void getProperties(String propertiesFile) {
        Properties properties = new Properties();
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             PrintStream printStream = new PrintStream(byteArrayOutputStream, true, StandardCharsets.UTF_8.name());
             FileInputStream fileInputStream = new FileInputStream(new File(path, propertiesFile).toString())) {
            properties.load(fileInputStream);
            properties.list(printStream);
            keyValues = byteArrayOutputStream.toString().split("\r\n|\r|\n");
            keyValueList = new ArrayList<>(Arrays.asList(keyValues));
            //Listヘッダを削除
            keyValueList.remove(0);
        } catch (Exception e) {
            Log.d(TAG, Objects.requireNonNull(e.getMessage()));
            e.printStackTrace();
        }
    }
    //keyValueList取得
    public List<String> getKeyValues() { return keyValueList; }
    //値判定
    public int isValueType(String value) {
        //defaultValueの型を判定
        try {
            float f = Float.parseFloat(value);
            if (value.contains("."))
                //小数点を含む場合はFloat
                return InputType.TYPE_NUMBER_FLAG_DECIMAL;
            else
                //少数点を含まない場合はInteger(Long)
                return InputType.TYPE_CLASS_NUMBER;
        } catch(Exception e) {
            //Floatに型変化できない場合はString
            return InputType.TYPE_CLASS_TEXT;
        }
    }
}

プロパティファイルを扱うクラス

これからAndroidのアプリ開発やJavaでの開発を始めたい方へ

アプリケーション開発経験がない方や、アプリケーション開発経験がある方でも、Java や C# などのオブジェクト指向言語が初めての方は、Android のアプリ開発ができるようになるには、かなりの時間がかかります。オンラインスクールでの習得を、強くおススメします。

未経験者からシステムエンジニアを目指すのに最適かと、
まずは無料相談から♪

未経験者からプログラマーを目指すのに最適かと、
まずは無料カウンセリングから♪

カリキュラムとサポートがしっかりしています、
お得なキャンペーンとかいろいろやっています♪