4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

iRidgeAdvent Calendar 2020

Day 19

Androidプロジェクト内のWebviewをデバッグする

Last updated at Posted at 2020-12-19

はじめに

Webviewが表示されなくなったから見て欲しいと依頼されることがある。
Webviewだけで完結するものでよければいいのだが、ログインのtoken情報をアプリから受け取ったりすることが想定されるアプリでは、
ページを動かしただけでは、画面が表示されない場合がある。
どのようなやりとりをされているのかドキュメントが残っていればいいのだが、
昔の作ったもので、埋め込んだあと特に変更されていないものの場合、
ソースコードを頼りに、目星をつけるか、ネイティブアプリのエンジニアに聞く必要がある。テストコードがあればそこから目星をつけることも可能だが
また、tokenは、常に使えるものでもないので、定期的に更新するのも大変である。
なので、アプリのWebviewをそのままデバッグできるところまで、確認できたものを備忘録として残しておく。

本記事は、Projectも何もないまっさらな状態からサンプルアプリを作成して、デバッグするまでの流れで記述しています。
既存のWebViewのProjectがあり、どの辺の修正を加えればデバッグ可能か知りたい場合は、AndroidのWebviewのURLをLocal環境に設定するまで飛ばしてください。

Webviewアプリを作成する

Android Studio(筆者の環境では4.1.1)で、新規プロジェクトを作成する。
テンプレートを使っても良いとは思うが、本記事では、「基本アクティビティー(Basic Activity)」を使用する。

確認ようのアプリなので、プロジェクトの構成は以下のようにした。
言語 : Kotlin
最小 SDK : API 29: Android 10.0 (Q)

※ デバイスの追加については省略します。 Android 4.4(KitKat)以降が対象となります。

まずはじめに、インターネットに接続するための、パーミッションを追加する。

app/src/main/AndroidManifest.xml
      </application>
+     <uses-permission android:name="android.permission.INTERNET"/>
  </manifest>

画面にWebviewを設置する。
使用したテンプレートの基本アクティビティーはフラグメントに設定させているので、app/src/main/res/layout/fragment_second.xmlにWebviewを設置する。
※ MainAcrivityのみしかないテンプレートを選択したときは、MainAcrivityに記述することになるが、内容が異なるので注意が必要

余計なtextview_secondのコンポーネントを削除を行い、
app/src/main/res/layout/fragment_second.xmlパレットから「Widgets」-「Webview」をドラックアンドドロップで設置する
idをwebviewとする

スクリーンショット 2020-12-18 18.55.16.png

次にWebviewの設定内容をapp/src/main/java/com/example/webviewapp/SecondFragment.ktに記述していく
最初ということで、https://example.com/のサンプルページを表示させるようにする。

app/src/main/java/com/example/webviewapp/SecondFragment.kt
class SecondFragment : Fragment() {

    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        val view: View =  inflater.inflate(R.layout.fragment_second, container, false)

        //xmlのWebViewで指定したid
        val myWebView: WebView = view.findViewById(R.id.webview)
        myWebView.loadUrl("https://example.com/")

        return view
    }

Android Emulator上でアプリを実行する。

スクリーンショット 2020-12-19 1.14.53.png
上記のようにExample DomainのページをWebviewで表示できれば成功。

Webviewで表示させるコンテンツを作成する

コンテンツ提供のためのWebサーバを作成する。
簡易的なものをgolangで作成する。

main.go
package main

import (
	"fmt"
	"log"
	"net/http"
	"text/template"
)

type Page struct {
	Title string
}

func handler(w http.ResponseWriter, r *http.Request) {

	switch r.Method {
	case http.MethodPost:
		page := Page{"Webview Test"}
		tmpl, err := template.ParseFiles("layout.html")

		if err != nil {
			http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
			return
		}

		tmpl.Execute(w, page)
	}

}

func main() {
	var httpServer http.Server

	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static/"))))
	http.HandleFunc("/", handler)

	log.Println("start http listening :8080")
	httpServer.Addr = ":8080"
	log.Println(httpServer.ListenAndServe())
}
layout.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>{{.Title}}</title>
    <link rel="stylesheet" href="/static/index.css">
  </head>
  <body>
    <div id="wrapper" class="hiddden">
      <h1>ようこそ</h1>
      <h3 id="id">ID</h3>
      <h3 id="name">表示名</h3>
    </div>
    <script src="/static/main.js"></script>
  </body>
</html>

初期状態では、非表示なるようにcssを作成する。
他、細かいstyleの調整をする。

static/index.css
body {
    display: flex;
    min-height: 100vh;
    margin: 0;
}

#wrapper {
    margin: auto;
    text-align: center;
}

#wrapper.hidden {
    display: none;
}

AndroidからWebviewに、値を渡すための関数を用意する。

static/main.js
function setValue(id, name) {
    if (arguments.length !== 2) {
        throw Error('2つ引数を持つ関数です');
    }

    console.log(`id : ${id}`)
    console.log(`name : ${name}`)

    const idElement = document.getElementById('id');
    idElement.textContent = id;

    const nameElement = document.getElementById('name');
    nameElement.textContent = name;

    const wrapper = document.getElementById('wrapper');
    wrapper.classList.remove('hidden');
}
$ go run main.go

AndroidのWebviewのURLをLocal環境に設定する。

まず、Webviewの向き先をlocalサーバに変更する。

app/src/main/java/com/example/webviewapp/SecondFragment.kt
    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        val view: View =  inflater.inflate(R.layout.fragment_second, container, false)

        val myWebView: WebView = view.findViewById(R.id.webview)
        myWebView.settings.javaScriptEnabled = true

        WebView.setWebContentsDebuggingEnabled(true)

        val postData = ""
        myWebView.postUrl("http://10.0.2.2:8080/", postData.toByteArray());

        val id = "1234567890"
        val name = "test"

        myWebView.webViewClient = object : WebViewClient() {
            override fun onPageFinished(webView: WebView, url: String) {
                super.onPageFinished(webView, url)

                val script = "window.setValue('$id','$name')"
                println(script)

                webView.evaluateJavascript(script, null)
            }
        }

WebView.setWebContentsDebuggingEnabled(true)はChrome Developer Toolsを許可するオプション。
Webviewのインスタンスではなく、staticメソッドからの設定となるので注意が必要。
myWebView.settings.javaScriptEnabled = trueはデフォルトでは、JavaScriptが有効化されないので、有効化する。
最近では、SPAのWebviewも多くなってきていると思うので、つけることが多いのかもしれない
10.0.2.2はエミュレータから開発ホストへのループバックインターフェースへのエイリアスで、開発端末のlocalhostにあたる。
webView.evaluateJavascript(script, null)で特定のJavaScriptをネイティブアプリから実行させることができる。

スクリーンショット 2020-12-19 18.10.13.png

次に、ローカル実行しているサーバはHTTPSではないので、

/app/src/main/res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain incincludeSubdomainsludeSubdomains="false">10.0.2.2</domain>
    </domain-config>
</network-security-config>

設定ファイルを読み込ませようにAndroidManifest.xmlを修正する。

app/src/main/AndroidManifest.xml
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
-       android:theme="@style/Theme.WebviewApp">
+       android:theme="@style/Theme.WebviewApp"
+       android:networkSecurityConfig="@xml/network_security_config">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/Theme.WebviewApp.NoActionBar">

スクリーンショット 2020-12-19 18.10.13.png
上のような画面が表示できれば成功

※ 以上の設定をして、再ビルドしても、「Webpage not available」のエラーページが出る場合は、エミュレーターの対象アプリを一度アンインストールして、再度ビルドすることで表示されるようになるかもしれません。私はこれで、なおりました。

Chrome Developer Toolsで確認する

エミュレーターでWebviewを表示させる。
Google Chromeのアドレスバーchrome://inspectを入力する。

Deveicesタブから
Webview in XXXX というようにエミュレータで表示されているWebviewが選択可能になっているので、inspectをクリックする。

スクリーンショット 2020-12-19 18.23.28.png

ネイティブアプリから呼び出した、JavaScriptのLogもしっかり出力されており、ElementsタブからWebviewのDOM構成も確認することが書きます。
Chromeから更新で行っても、ネイティブで継承した、onPageFinishedはちゃんと呼び出されているようなので、安心です。

スクリーンショット 2020-12-19 18.27.09.png

の内容を押さえれば、ソースコードがあること前提になりますが、Webviewを通常のwebアプリケーションと同様に、デバッグができるかと思います。

エミュレータの実行は、多くのメモリの消費するようですので、PCのスペックも確認しておくことをお勧めします。

参考

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?