Android 7.0でChromeがWebViewを提供するようになった仕組み


[Android 7.0からはChromeがWebViewを提供する](http://juggly.cn/archives/195375.html) みたいのはあちこちでバズってる割に、中身の仕組みをちゃんと書いた記事は殆ど見ません。
個人的にはとても残念に思います。

ということで、中を見てみました。


## まずは、Lollipopまでのおさらい

WebViewのAPIを提供するのが、WebViewFactoryProviderで、
`WebViewFactory::getProvider` のなかで

* ネイティブ側の準備：libwebviewchromium.soの探索とロード
	* パッケージ名はgetWebViewPackageName()で取得
* Java API側の準備：WebviewProviderの生成と初期化
	* WebViewChromiumFactoryProviderが内部でWebViewDelegateを実装する

みたいな流れの処理があります。

ソースレベルで見たければこのへん。

* http://tools.oesf.biz/android-5.1.1_r9.0/xref/frameworks/base/core/java/android/webkit/WebView.java
* http://tools.oesf.biz/android-5.1.1_r9.0/xref/frameworks/base/core/java/android/webkit/WebViewFactory.java
* http://tools.oesf.biz/android-5.1.1_r9.0/xref/frameworks/webview/chromium/java/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
* http://tools.oesf.biz/android-5.1.1_r9.0/xref/frameworks/webview/chromium/java/com/android/webview/chromium/WebViewDelegateFactory.java



ポイントとしては、端末ベンダーが `R.string.config_webViewPackageName` をオーバーライドして、
デフォルトは `com.android.webview` を見にいくのを、ねじ曲げることができるようになっています。

```java:frameworks/base/core/java/android/webkit/WebViewFactory.java
public static String getWebViewPackageName() {
    return AppGlobals.getInitialApplication().getString(
            com.android.internal.R.string.config_webViewPackageName);
}
```

## Marshmallowでどうなった？

AOSPからChromium WebViewのソースが削除されただけで、WebViewのロード自体は同じような仕組みで動いています。

この時期くらいからGoogleがChromeでWebView機能提供することを考え始めたのか、
ネイティブのパスがLollipopでは `libwebviewchromium.so` ってハードコーディングされてたのが、Marshmallowでは パッケージの meta(`com.android.webview.WebViewLibrary`)を見るように変わってたりはします。

　　

## で、Nougatでどう変わった？

結論から言うと、WebViewロードの仕組みは殆ど変わってません。設定の仕組みが追加されただけでした。


NougatではWebViewを提供するパッケージを開発者向けオプションで設定することができます。
ということは、システム設定に持ってるんですね。そこから辿ってみます。

```java:frameworks/base/core/java/android/provider/Settings.java
public static final String ACTION_WEBVIEW_SETTINGS = "android.settings.WEBVIEW_SETTINGS";
```

なんと、Settings.javaに設定名ではなくて、インテントのアクションの定義が・・・。

設定画面の実装はどこにあるか辿ってみると、

http://tools.oesf.biz/android-7.0.0_r1.0/xref/packages/apps/Settings/src/com/android/settings/WebViewImplementation.java

ここです。

```java
    mWebViewUpdateService  =
            IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate"));
    try {
        WebViewProviderInfo[] providers = mWebViewUpdateService.getValidWebViewPackages();
        if (providers == null) {
            Log.e(TAG, "No WebView providers available");
            finish();
            return;
        }
   
        String currentValue = mWebViewUpdateService.getCurrentWebViewPackageName();
        if (currentValue == null) {
            currentValue = "";
        }
```

WebViewの機能を提供するパッケージを探索
　↓
ダイアログ表示
　↓
`Settings.Global.WEBVIEW_PROVIDER` の設定値を更新
　↓
`WebViewFactory.onWebViewProviderChanged()`


という流れです。
NougatではWebViewを提供するパッケージを設定できる仕組みが加わっただけで、WebViewの初期化部分とかって、Lollipopのときと大きくは変わってないんですね。

もしかすると、Marshmallowでも最近のChromeをプリインストールして、'R.string.config_webViewPackageName' を `com.google.android.chrome` にして端末を焼けば、NougatみたいにChromeがWebViewを提供するようにできたりするんじゃないかなー、と・・・。(未検証ですｗ)

ソースレベルで見たければこのへん。

* http://tools.oesf.biz/android-7.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
* http://tools.oesf.biz/android-7.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/webkit/SystemImpl.java

　



ちなみに開発者向けオプションで変えない場合はどう動作してるの？というと、
http://tools.oesf.biz/android-7.0.0_r1.0/xref/frameworks/base/core/res/res/xml/config_webview_packages.xml
このXMLがWebViewUpdateService起動時に読まれて、適切なWebViewがロードされるようになってます。
このXMLは端末ベンダーがオーバーライド可能なので、NexusではAndroid WebViewではなくChromeが指定されてるという[噂](http://opengapps.org/blog/post/2016/10/06/nougat-webview/)があります。

　

## まとめ

NougatではWebViewのProviderを設定できる実装が入っただけで、
WebView実装を含んだAPKを選択的にロードする処理はLollipopからほとんど変わっていませんでした。

　
完全に余談ですが、
[らくらくホン](https://www.nttdocomo.co.jp/product/easy_phone/f06f/index.html) みたいに、今まではOSレベルで魔改造が必要だったWebViewも、今後はAPK載せてconfig変えるだけで動くようになるのはいい仕組みですね。万が一バグっててもGoogle Playアップデートもできるし。