PageSpeed Insights スコア向上のためにやったこと

PageSpeed Insights

www.yusukezzz.net のスコアはPC版で90、モバイル向けで87となっている
github.io に置いてるサイトなので出来ることはあまり多くないのだけど、元は70点台ぐらいだったのでここまで上げるためにやったこと2点をまとめておく

CSS, JS の縮小

いわゆる minify と言われているやつ
wordpress だと plugin で出来たりするが、この blog では cloudflare の機能を利用している

Speed タブの Auto Minify から有効化出来る
CSS, JS, HTMLの3種類が選べるが、この blog だとHTMLの minify は不具合があったので CSS, JS だけ使用している

レンダリングをブロックしているコンテンツの排除

同期的に読み込まれる CSS, JS はページのレンダリングを遅くするため、非同期的に読み込むとスコアが向上する
読み込むだけで効果のある単純な JS であれば async または defer 属性を付けるだけでなんとかなる(※)が、
読み込んでから初期化処理を呼び出す必要がある場合は読み込み完了に hook して実行する必要がある
この blog では highlightjs が該当し、ページ末尾で以下のような script を実行している

※document.write しちゃってるような行儀の悪い script や読み込み順に依存関係があるものを除く

<script>
  function loadCss(url) {
    var l = document.createElement('link'); l.rel = 'stylesheet'; l.href = url;
    var h = document.getElementsByTagName('head')[0]; h.parentNode.insertBefore(l, h);
  }
  function loadJs(url, cb) {
    var d = document, t = 'script',
        o = d.createElement(t),
        s = d.getElementsByTagName(t)[0];
    o.async = true;
    o.src = url;
    if (cb) { o.addEventListener('load', function (e) { cb(null, e); }, false); }
    s.parentNode.insertBefore(o, s);
  }
  var cb = function() {
    loadCss('https://www.yusukezzz.net/wp-content/highlightjs/solarized-light.css');
    loadJs('https://www.yusukezzz.net/wp-content/highlightjs/highlight.pack.js', function() {
      hljs.initHighlightingOnLoad();
    });
  };
  var raf = requestAnimationFrame || mozRequestAnimationFrame ||
      webkitRequestAnimationFrame || msRequestAnimationFrame;
  if (raf) raf(cb);
  else window.addEventListener('load', cb);
</script>

もっとも、小規模なものであれば外部ファイル化せずに直接 head タグ内に書いたほうが良い(インライン展開)

Kotlin の Parcelize で簡単 Parcelable 実装

Kotlin 1.1.4 から Parcelable を手軽に実装できるようにする機能が追加された

KEEP/android-parcelable.md at master · Kotlin/KEEP

まだステータスは正式版ではないので gradle で

androidExtensions {
    experimental = true
}

を有効化し、

@Parcelize
class MyParcelable(val data: Int): Parcelable

このように書いておくとコンパイル時に自動的に Parcelable 相当の実装を生成してくれる
data クラスにも使用出来るので json のパース結果などを Activity 間で受け渡すのが非常に簡単になる

これまでは grandstaish/paperparcel を使っていたが CREATOR フィールドを用意する必要がないのでより手軽だ

※注意
CRETOR フィールドを用意する必要はないが、@Parcelize アノテーションを付与したクラスはIDE上で
this class implements parcelable but does not provide a CREATOR field
というエラー扱いになる(でもコンパイルは通る)
下記チケットが起票されているので、直に修正される…と思いたい
[AS3.0] Android extensions, Parcelable: editor shows warning about incomplete implementation on a class with Parcelize annotation : KT-19300

Android Studio 3.0 で Robolectric 関連の変更に追従

先日 Android Studio 3.0 が正式にリリースされたので早速ダウンロードしてビルドしたら Robolectric のテストがコケるようになってしまった
Getting Started | Robolectric に書いてある通りなのだけど以下の対応をしたら通るようになった
どうやらリソースパスを解決するAPIに変更があったらしい

1. testOptions の追加

app/build.gradle に以下の記述を追加する

android {
  testOptions {
    unitTests {
      includeAndroidResources = true
    }
  }
}

2. BuildConfig の指定を止める

テストクラスに Robolectric の BuildConfig 指定がある場合は消す
@Config(constants = BuildConfig.class)

chrome custom tabs の warmup

custom tabs を使う際は事前に chrome のプロセスを温めておくことで遷移を高速化出来るのだけど、自前で実装しようと思うと結構面倒くさい
先人の知恵を拝借しようと Github を検索すると下記の実装が見つかった

DreaminginCodeZH/CustomTabsHelper: Custom tabs, made easy.

saschpe/android-customtabs: Chrome CustomTabs for Android demystified. Simplifies development and provides higher level classes including fallback in case Chrome isn’t available on device.

CustomTabsHelper は Fragment を使う前提の実装で、今回自分は Fragment を使っていなかったので後者の android-customtabs を採用した
使い方は非常にシンプルで README と example の通りなので省略する

基本的な実装はどちらも同じ、というか下記公式の customtabs 使用例が元になっていて単にアプリ(Fragment)開始時に customtabs の service に接続し、停止時に切断している
GoogleChrome/custom-tabs-client: Chrome custom tabs examples

ちょっとしたことだけど非常に反応が良くなるので customtabs を使うならぜひ利用したい

jvisualvm でリモート接続

jdk をインストールすると付いてくる便利なプロファイラ jvisualvm でリモートサーバの java プロセスに接続する方法についての覚書

jvisualvm でリモート接続する方法は、
jstatd(これも jdk 組み込み)という java プロセスの監視ツールを経由する方法と JMX という仕様を利用する方法の2種類がある
今回はリモートサーバに jdk が入ってなかったので JMX でやる場合

JMX 起動オプション

リモートサーバで java アプリを起動する際に以下のオプションを指定しておく(以下は jdk8 の場合)
port は他で使われてなければ何でも良いが、同じ値を指定する
127.0.0.1 は環境に合わせて変える

-Dcom.sun.management.jmxremote.port=3333
-Dcom.sun.management.jmxremote.rmi.port=3333
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Djava.rmi.server.hostname=127.0.0.1

今回接続する際は ssh のポートフォワーディングを利用した
ssh -L 3333:127.0.0.1:3333 user@remote-server
などでリモートへの接続を開いておき jvisualvm から localhost:3333 に対して繋ぐ