AndroidのWebViewでハマったポイントまとめ

# 当記事について

WebViewは厄介な存在だと思います。
無数にあるWebページがちゃんと表示されるかが怪しいと感じる今日この頃。
あとはWebViewに限った話ではないですが、OSバージョンや端末によって起きたり起きなかったりするバグ。
当記事では、WebViewの実装で個人的にハマったポイントを随時更新していきます。

# Android 6.0にて、動画コンテンツのあるURLを表示しようとするとクラッシュする

## 事象

Android 6.0にて、動画コンテンツのあるURLを表示しようとすると、以下のようにクラッシュします。

```
07-21 22:35:26.674 4770-4770/com.example.samplewebviewapp E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.samplewebviewapp, PID: 4770
    java.lang.NullPointerException: Attempt to invoke virtual method 'int android.graphics.Bitmap.getWidth()' on a null object reference
        at com.android.webview.chromium.WebViewContentsClientAdapter.getDefaultVideoPoster(WebViewContentsClientAdapter.java:1208)
        at org.chromium.android_webview.DefaultVideoPosterRequestHandler$1.run(DefaultVideoPosterRequestHandler.java:39)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:148)
        at android.app.ActivityThread.main(ActivityThread.java:5417)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
07-21 22:35:26.779 4770-4811/com.example.samplewebviewapp E/eglCodecCommon: glUtilsParamSize: unknow param 0x000085b5
````

## 原因

HTML内部に`video`タグがあり、その中に`poster`属性という属性が存在します。
どうやらユーザー環境で利用できる動画が無い場合に表示させる画像を指定するようです。
[\<video>－HTML5タグリファレンス](http://www.htmq.com/html5/video.shtml)

この属性が指定されていない場合、`WebChromeClient#getDefaultVideoPoster`メソッドが呼ばれるのですが、その戻り値が`null`であるために起きていました。

## 解決策

`AndroidManifest.xml`に`WRITE_EXTERNAL_STORAGE`を宣言する方法では解決できませんでした。
`WebChromeClient#getDefaultVideoPoster`メソッドをオーバーライドし、`getDefaultVideoPoster`メソッドが`null`を返すようであれば、代わりの画像を返すように修正することでクラッシュしなくなりました。
ちなみに、`BitmapFactory.decodeResource`メソッドの第2引数に指定するリソースIDですが、pngやjpegなどの画像ファイルを指定する必要があります。XMLで定義したリソースでは`null`となってしまうので注意。

```kotlin
webView.webChromeClient = object : WebChromeClient() {
    override fun getDefaultVideoPoster(): Bitmap? {
        return if (super.getDefaultVideoPoster() == null) {
            BitmapFactory.decodeResource(applicationContext.resources, android.R.drawable.ic_media_play)
        } else {
            super.getDefaultVideoPoster()
        }
    }
}
```

結果としては以下の通り表示されますが、指定したリソースが画面に表示されてしまい、ブラウザで本来表示されている内容とは異なってきてしまいます。
ですので、1×1ピクセルの黒画像を用意するなどして、目立たないように表示すると良いかもしれません。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93963/ee204de9-3873-71b2-a3ed-c833451cff4b.png)


## 参考URL

- [android-Bitmap.getWidth（）でのnullポインタによるWebViewのクラッシュ-スタックオーバーフロー](https://stackoverflow.com/questions/48290277/webview-crash-by-null-pointer-at-bitmap-getwidth)
- [AndroidでWebViewを使うときはManifestに「WRITE_EXTERNAL_STORAGE」 | GROUP DEV BLOG | TECHNO MOBILE](https://www.tcmobile.jp/dev_blog/programming/android%E3%81%A7webview%E4%BD%BF%E3%81%86%E3%81%A8%E3%81%8D%E3%81%AF%E3%80%8Cwrite_external_storage%E3%80%8D%E3%82%92%E8%A8%B1%E5%8F%AF%E3%81%97%E3%82%88%E3%81%86/)
- [Bitmap.getWidth() on a null object reference at webview.getDefaultVideoPoster_Gdeer 的专栏-CSDN博客_getdefaultvideoposter](https://blog.csdn.net/Gdeer/article/details/88817191)

# WebViewを長時間表示させるとメモリリークが発生する

## 事象

WebViewを長時間表示させるとメモリリークが発生します。
起動時間としては端末によりけりですが、だいたい8時間〜12時間弱で発生するようです。

## 原因

いまいちよくわかりませんが、WebViewのインスタンスを生成しただけでも発生するので、おそらくWebView側のインスタンス生成処理に問題があるようです。

## 解決策

今のところありません。。インスタンスを破棄しても発生するようです。。
試してはいませんが、代替可能なら[Chrome Custom Tabs](https://developer.chrome.com/multidevice/android/customtabs)を使用することで解決できるかもしれません。

## 参考URL

- [android-WebViewのメモリリーク-スタックオーバーフロー](https://stackoverflow.com/questions/3130654/memory-leak-in-webview)