はじめての Chrome Custom Tabs
田澤健二
この記事は RECRUIT MARKETING PARTNERS Advent Calendar 2015 の投稿記事です。
どうもこんにちわ。英単語サプリ担当 田澤です。
これまでアプリからWebサイトを表示するとなると、WebViewを使用するかIntentでブラウザアプリを呼び出すことで対応してきましたが、そういった従来の方法ですと以下のような課題が発生しがちです。
- ブラウザアプリでは遷移してからWebサイトを表示するまでが重い、遅い
- 別アプリの表示となるなのでUIに統一感を出せない
- WebViewではセキュリティ、機能面に対する懸念がある
ChromeCustomTabsでは表示速度の向上、Choromeベースのセキュリティと機能(Chromeとのcookieの共有など)の恩恵を受けることができます。また、UIのカスタマイズも可能なためシームレスにWebページを表示することができます。
本記事ではChromeCustomTabsの簡単な使用方法、カスタマイズして表示する方法、PreFetch(先読み)について紹介します。
ここで使用しているコードは Github - SampleChromeCustomTabs で公開しています。
準備
まずはbuild.gradleに下記を追加します。
サポートライブラリのみでも実装可能ですが、Googleが提供しているChromeCustomTabsのサンプルにSharedという便利なクラス群をまとめたmoduleが提供されているので追加します。
使用条件
Chromeがv45以上である必要があります。Chromeが対応していない、Chromeがインストールされていない場合は通常のIntentの挙動となり、ブラウザアプリが起動します。また、SupportLibrary 23.1.1のminSDkVersionが15となっているので、導入を検討する場合は対象となるアプリのminSDkVersionを確認しておきましょう。
使用方法
ChromeCustomTabsはWebViewのようにViewコンポーネントを配置しては使用せず、Intentベースでメッセージングする仕様になっています。
1. 簡単な使用方法
ただ表示するのみ場合だと、以下のコードのみで対応は完了します。ChromeCustomTabsを使用するのにインターネットパーミッションは不要です。
CustomTabsIntent tabsIntent = new CustomTabsIntent.Builder().build();
String packageName = CustomTabsHelper.getPackageNameToUse(this);
tabsIntent.intent.setPackage(packageName);
tabsIntent.launchUrl(this, uri);
まずCustomTabsIntent
を生成します。CustomTabsIntent
はWebサイトを表示を実行するのに使用します。後述の〈2. カスタマイズして表示する〉のとおり、設定できる内容が多いためBuilderパターンで生成できるように実装されています。
次にCustomTabsIntent.intent
にパッケージ名を明示的に指定しています。パッケージ名の取得にはCustomTabsHelper
クラスを使用します。これはSharedモジュールが提供しているクラスです。getPackageNameToUse
メソッドはCustomTabへの接続先となる適切なChromeのパッケージ名を取得してくれます。端末によってはパッケージ名を指定しないとCustomTabが正しく動作せずブラウザアプリが起動することがありました。
CustomTabsIntent
のlaunchUrl
メソッドを実行するとWebサイトが表示されます。
ChromeベースなのでデフォルトでpullToRefreshが実装されており、ページ内検索、翻訳機能も使用できます。デザイン面ではタイトル表示はURLのみ。CustomTabsを閉じるボタンのアイコンが×、Toolbarの色がグレーでアプリに統一感がなくなってしまっています。
そこで、次はCustomTabのカスタマイズ方法について紹介します。
2. カスタマイズして表示する
CustomTabsIntent.Builder
には以下のメソッドが用意されており、カスタマイズが可能となっています。UIのカスタマイズ、遷移時のアニメーションの指定、アクションボタンとメニューアイテムの追加ができます。
メソッド名 | 機能 |
---|---|
setShowTitle(boolean showTitle) | Webサイトのタイトルを表示します |
setToolbarColor(int color) | Toolbarの色を設定します |
setCloseButtonIcon(Bitmap icon) | 閉じるボタンのアイコンを設定します |
setStartAnimations(Context context, int enterResId, int exitResId) | 表示アニメーションを設定します |
setExitAnimations(Context context, int enterResId, int exitResId) | 閉じるアニメーションを設定します |
enableUrlBarHiding() | 下にスクロールしたときにToolbarが非表示になるようにします |
addMenuItem(String label, PendingIntent pendingIntent) | メニューを追加します |
setActionButton(Bitmap icon, String description, PendingIntent pendingIntent) | アクションボタンを追加します |
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
setupMenu(builder);
setupActionButton(builder);
CustomTabsIntent tabsIntent = builder.setShowTitle(true)
.enableUrlBarHiding()
.setToolbarColor(getResources().getColor(R.color.brand_sub))
.setCloseButtonIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back))
.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left)
.setExitAnimations(this, android.R.anim.slide_in_left, android.R.anim.slide_out_right)
.build();
String packageName = CustomTabsHelper.getPackageNameToUse(this);
tabsIntent.intent.setPackage(packageName);
tabsIntent.launchUrl(this, uri);
メニュー、アクションボタン共に追加するためのメソッドが用意されておりPendingIntent
をセットして使用します。
// メニューの追加
private void setupMenu(CustomTabsIntent.Builder builder) {
Intent menuIntent = new Intent();
//...省略
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, menuIntent, 0);
builder.addMenuItem("Menu Share Sample", pendingIntent);
}
// アクションボタンの追加
private void setupActionButton(CustomTabsIntent.Builder builder) {
Intent actionIntent = new Intent(Intent.ACTION_SEND);
//...省略
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, actionIntent, 0);
Bitmap icon = BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_menu_share);
builder.setActionButton(icon, "Action Share Sample ", pendingIntent);
}
上記のように各種設定することで次のように表示が変わります。
ツールバーの色をアプリのテーマカラーに合わせる、画面遷移をアプリと同様のものにする、閉じるボタンのアイコンを変更することでアプリに統一感が出て、シームレスにWebサイトを表示することが可能となります。
3. PreFetch
ChromeCustomTabsの特徴の1つとしてPreFetchがあります。これはlaunchUrl
を実行する前に、Webサイトの先読みをしておいて画面遷移時の表示速度の向上させる機能です。実行手順としてはWarm up→先読み→CustomTabを表示する、となります。
3-1. Warm upをする
mCustomTabsServiceConnection = new CustomTabsServiceConnection() {
@Override
public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient customTabsClient) {
mCustomTabsClient = customTabsClient;
// Chrome と接続したらWarm upする
mCustomTabsClient.warmup(0L);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
String packageName = CustomTabsHelper.getPackageNameToUse(this);
// Chrome と接続
CustomTabsClient.bindCustomTabsService(this, packageName, mCustomTabsServiceConnection);
まず、CustomTabsClient.bindCustomTabsService
メソッドを実行し、Chromeと接続をします。第3引数にはCustomTabsServiceConnection
をセットします。
CustomTabsServiceConnection
はserviceのbindの状況を監視しており、onCustomTabsServiceConnected
、onServiceDisconnected
でbindの結果を返してくれます。Serviceがbindされると、onCustomTabsServiceConnected
が呼ばれるのでCustomTabsClient
を使用してWram upを行います。また、CustomTabsが不要になった時にServiceのunbindを忘れないようにしましょう。
3-2. 先読みして表示
Warm upができたので次は先読みを行います。
CustomTabsClient
のnewSession
メソッドからSessionの生成を行います。newSession
メソッドの引数の型はCustomTabsCallback
クラスです。このクラスはCustomTabのNavigationEventのタイミングでコールバックを返してくれます。
NavigationEventにはNAVIGATION_STARTED
、NAVIGATION_FINISHED
、TAB_SHOWN
などがあり、Webページの読み込み開始、完了やCustomTabの表示時などのタイミングでイベントをキャッチできます。不要な場合はnullを指定します。
次に、生成したSessionを使用して先読みを行います。先読みをするにはmayLaunchUrl
メソッドを実行します。このメソッドには戻り値があり、先読みが成功したかの結果が返却されます。
先読みができたらlaunchUrl
メソッドを使用してCustomTabを表示します。この時CustomTabsIntent.Builder
の引数にSessionを指定するのを忘れないようにしましょう。
CustomTabsIntent tabsIntent = new CustomTabsIntent.Builder(mCustomTabsSession).build();
// 省略
tabsIntent.launchUrl(this, uri);
これでWebサイトの先読みがされた状態でCustomTabが表示されます。
先読みの効果を確認するため、ブラウザ、PreFetchをしたChromeCustomTabs、WebViewで表示速度を比較しました。
( クリックして動画を再生 )
PreFetchによる表示速度の向上がよくわかります。(使用端末:Galaxy S5)
まとめ
ChromeCustomTabsはUIの容易なカスタマイズによりシームレスな画面遷移を表現できる、Chromeの機能が使用できる、WebViewのセキュリティリスク回避ができる、PreFetchによる表示高速化ができる点など有用なコンポーネントです。しかし、PreFetchはどのタイミングで実行するのかが悩ましいです。
無闇矢鱈にPreFetchを行うと実際はWebサイトを読み込むところまでユーザーが到達しないのに、先読みだけしてしまい無駄な通信を発生させることになってしまいます。
また、Chromeがインストールされていない、バージョンが古いとブラウザが起動することになるのでChromeCustomTabsありきの実装はできません。対象外の端末に対してどのようにアプローチするのかを考える必要があります。
便利と言えば便利ですが、どのように扱うか、使用することで何を実現したいのかをよく検討してから取り入れたいところです。