Posted at

[Android]WebViewにAdblockを実装する


WebViewの広告を消したい

javascriptをオフにすれば大抵の広告は消えるけど、javascriptが使えないと何かと不便なので、adblockを実装したときのメモです。英語ではそれなりに資料が合ったのですが、日本語のページが見つからなかったので。


参考

このページを追えば実装できます

https://www.hidroh.com/2016/05/19/hacking-up-ad-blocker-android/

広告のホスト一覧

https://sites.google.com/site/hosts2ch/


だいたいの流れ

WebViewClient.shouldInterceptRequest()をオーバーライドして、ページ内からのリクエストURLが広告配信ホストかどうかを判定する。

もし広告の場合は空のレスポンスを返す。

以上。


実装


広告ホスト一覧を扱いやすいように成形する

https://sites.google.com/site/hosts2ch/ で、広告を配信しているホストの一覧がダウンロードできます。(めっちゃ便利..!!)

ダウンロードするとこんな感じになってると思うので、扱いやすいように成形します。


ja.txt

# hosts2ch - Japanese mobile ad servers hosts file.

# This file is public domain, of course.
#
# file size: 34 kB
# entries: 1279
#
# ---------- last updated: 2019-01-16 ----------

127.0.0.1 100234.advision-adnw.jp
127.0.0.1 100291.adnico.jp
127.0.0.1 100602.advision-adnw.jp
127.0.0.1 100651.advision-adnw.jp
127.0.0.1 100800.advision-adnw.jp
127.0.0.1 101623.sprout-ad.com
127.0.0.1 107008.speead.jp
127.0.0.1 150601.fc2rs.com
127.0.0.1 1tmg.info
127.0.0.1 2.chmato.me




これのコメント行とIPアドレスの列を削除してホストだけにし、ファイル名をhosts.txtにします。

こんな感じ↓


hosts.txt

100234.advision-adnw.jp

100291.adnico.jp
100602.advision-adnw.jp
100651.advision-adnw.jp
100800.advision-adnw.jp
101623.sprout-ad.com
107008.speead.jp
150601.fc2rs.com
1tmg.info
2.chmato.me





hosts.txtをプロジェクトファイルに追加する

assetsフォルダに追加します。もしassetsフォルダがない場合は作ってください。

スクリーンショット 2019-01-22 15.57.10.png


AdBlockを実装する


AdBlocker.java

public class AdBlocker {

private static final String AD_HOSTS_FILE = "hosts.txt";
private static final Set<String> AD_HOSTS = new HashSet<>();

public static void init(final Context context) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
loadFromAssets(context);
} catch (IOException e) {
// noop
}
return null;
}
}.execute();
}

@WorkerThread
private static void loadFromAssets(Context context) throws IOException {
InputStream stream = context.getAssets().open(AD_HOSTS_FILE);
BufferedSource buffer = Okio.buffer(Okio.source(stream));
String line;
while ((line = buffer.readUtf8Line()) != null) {
AD_HOSTS.add(line);
}
buffer.close();
stream.close();
}

public static boolean isAd(String url) {
HttpUrl httpUrl = HttpUrl.parse(url);
return isAdHost(httpUrl != null ? httpUrl.host() : "");
}

private static boolean isAdHost(String host) {
if (TextUtils.isEmpty(host)) {
return false;
}
int index = host.indexOf(".");
return index >= 0 && (AD_HOSTS.contains(host) ||
index + 1 < host.length() && isAdHost(host.substring(index + 1)));
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static WebResourceResponse createEmptyResource() {
return new WebResourceResponse("text/plain", "utf-8", new ByteArrayInputStream("".getBytes()));
}
}



WebViewClient.shouldInterceptRequest()を実装する

WebViewClientのshouldInterceptRequest()メソッドをオーバーライドして実装します。

webviewで読み込んだソースからのリクエストは全て一度shouldInterceptRequest()を通る(という認識なのですが合ってるのでしょうか)


MyWebViewClient.java

private Map<String, Boolean> loadedUrls = new HashMap<>();

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@override99
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
boolean ad;
if (!loadedUrls.containsKey(url)) {
ad = AdBlocker.isAd(url);
loadedUrls.put(url, ad);
} else {
ad = mLoadedUrls.get(url);
}
return ad ? AdBlocker.createEmptyResource() :
super.shouldInterceptRequest(view, url);
}



Applicationを実装する

Applicationを継承したMyApplicationの中でAdBlockerを初期化します。


MyApplication.java

public class MyApplication extends Application {

@Override
public void onCreate() {
super.onCreate();
AdBlocker.init(this);
}
}

AndroidManifestにも忘れず追加しましょう。


AndroidManifest.xml

<application

android:name="MyApplication"
....
....
/>


以上

https://www.hidroh.com/2016/05/19/hacking-up-ad-blocker-android/ ほとんどこのページにある通りなので、詳細はこちらを参考にされるといいと思います。