こんにちは、まっさん(@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 のアプリ開発ができるようになるには、かなりの時間がかかります。オンラインスクールでの習得を、強くおススメします。
未経験者からシステムエンジニアを目指すのに最適かと、
まずは無料相談から♪
未経験者からプログラマーを目指すのに最適かと、
まずは無料カウンセリングから♪
カリキュラムとサポートがしっかりしています、
お得なキャンペーンとかいろいろやっています♪
ゲーム系に強いスクール、UnityやUnrealEngineを習得するのに最適かと、
まずは無料オンライン相談から♪
参考になったら、💛をポッチとしてね♪
コメント欄