読者です 読者をやめる 読者になる 読者になる

AndroidのWebView.loadUrlは非同期処理なのかを調べてみた

iOSの場合のUIWebViewだが、基本的に処理は非同期らしい。
通信という不確定要素を使用するので、当然ではあるが。

Androidの場合、UIスレッドに5秒以上の処理をさせると、ANRが発生しアプリが強制終了する。
なので、重い処理の場合スレッドを別に立ち上げそこで処理を行うのが一般的である。

AndroidのWebViewは、そういう事情を考慮してできているのかがわからない(リファレンスにも触れられてない)ため、自分で調べてみた。

調べ方としては、OnCreateでloadUrlを実行し、どのスレッドが使用されているかを調べてみる感じ。

OnCreate内は、こんな感じ。

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final WebView wv = (WebView)findViewById(R.id.webView1);
        final String url = "http://www.google.com/";

		wv.setWebChromeClient(new WebChromeClient() {
			@Override
		    public void onProgressChanged(WebView view, int progress) {
				// LogCatにスレッドIDを表示
			    Log.w("WebViewTest", "loading...");
			    Log.w("webViewTest", "WebView thread Id = " + Long.toString(Thread.currentThread().getId()));
			}
		});

        wv.setWebViewClient(new WebViewClient() {  
  			@Override
  			public boolean shouldOverrideUrlLoading(WebView view, String url) {
    			return false;
    		}
      	});

        wv.getSettings().setJavaScriptEnabled(true);

        // 現在処理に使用しているスレッドIDの取得
        Log.w("Activity", "Start!");
        Log.w("Activity", "Activity thread id = " + Thread.currentThread().getId());

	// ロードする
	Log.w("Activity", "threadId" + Thread.currentThread().getId());
        wv.loadUrl(url);


Xml側でWebViewを作り、Idでひもづけた。
WebChromeClientでLoading中に使用するスレッドをLogCatに出力する。

●結果
見事にmainUIスレッドを使用していた。
つまり、非同期ではないという事である。
通信・レンダリングに5秒以上かかった場合最悪アプリがANRするのかも。

そこで、threadを使用したらどうなるかを実験してみた。
前述のソースであるロードする部分を下記に書き換えて実行してみた。

// handlerを使用してロードする
		final Handler mHandler = new Handler();
		new Thread(new Runnable() {
			@Override
			public void run() {
				Log.w("Handler", "handler thread id = " + Thread.currentThread().getId());
				mHandler.post(new Runnable() {
					@Override
					public void run() {
						Log.w("handler", "handler webview id = " + Thread.currentThread().getId());
						wv.loadUrl(url);
					}
				});
			}
		}).start();

上のソースで変なLogCatが出てきてたらこっちのrunOnUiThreadを使えば解決する。

        // runOnUiThreadを使用してロードする
		new Thread(new Runnable() {
			public void run() {
        		Log.w("UiThread", "UiThread thread id = " + Thread.currentThread().getId());
			    runOnUiThread(new Runnable() {
				    public void run() {
				        Log.w("UiThread", "uithread webview id = " + Thread.currentThread().getId());
				        wv.loadUrl(url);               
				    }
			    });
			}
		}).start();

●結果
やはりUIスレッドで処理がされている。

●結論
WebViewはUIスレッドを使用して処理する。
スレッドをたちあげても結局UIスレッドを使うので意味がないことがわかった。

ただ、DDMSでアプリ実行時のスレッド一覧を見たら、
WebViewCoreThreadとWebViewWorkerThreadが立ち上がっていることがわかる。

f:id:tatuas:20121230034859p:plain

さらに、WebViewのロード部分のソースを見ると、WebViewCore部分のコンストラクタ
threadを立ち上げていることが確認できた。

これらのことから、WebViewは
・HTTPリクエストは非同期で投げ
レンダリングでUIスレッドをつかって同期処理をする
という感じなのだと思われる。
これはANRとか発生しないのか・・・よくわからない。

今回の実験プロジェクトはGitHubに上げてある。