爆速でandroidアプリを ビルドするための仕組み dena techcon #denatechcon
Post on 16-Apr-2017
7.750 Views
Preview:
TRANSCRIPT
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
Jan 29, 2016
TOYAMA, Yosaku System Gr.DeNA Life Science, Inc.
爆速でAndroidアプリをビルドするための仕組み
⁃ 氏名: 外山 要作
⁃ 所属: DeNAライフサイエンス システムグループ
⁃ 入社: 2012年5月• 新規サービスの開発、運用
• Android、iOS
⁃ 好きな言語: Ruby と C#
⁃ 趣味: 競プロ、ショートコーディング
⁃ お酒
自己紹介
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
2
Instant Run など
❌ 爆速でビルドして Android 開発の効率UP
◯ 爆速でビルドするための仕組みがどうやって成り立っているのか
本日の内容
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
3
⁃ gradle はビルドに時間がかかる• 上手くやればなんとかできるのでは
• 細々と改良していた
⁃ ところが• テーマを決めたら → Instant Run !!!
• スライド書いたら → cold swap !!!
経緯
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
4
⁃ 背景⁃ トライしていた手法• hot deploy の困難性
• 実現方法
⁃ Instant Run について• 推測
⁃ まとめ
アジェンダ
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
5
ビルド時間を減らすのが大事な理由⁃ 無駄な時間が減る⁃ 開発効率の向上⁃ 小さな単位で結果を確認
背景
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
6
トライしていた手法
7Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
⁃ Java のソースコードを書き換えた結果を手早く確認したい
• ClassLoader をハックできないか
⁃ 上手く行かなかった• なぜ?
hot deploy の難しさ
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
8
DexClassLoader
BaseDexClassLoader#findClass
DexPathList#findClass
DexFile#loadClassBinaryName
DexFile.defineClass
(ここから native)
Dalvik_dalvik_system_DexFile_defineClassNative
dvmDefineClass
findClassNoInit
Android の ClassLoader
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
9※ dalvik VM での話
// ...
clazz = dvmLookupClass(descriptor, loader, true); if (clazz == NULL) { // ... if (!dvmAddClassToHash(clazz)) { // ...
bool dvmAddClassToHash(ClassObject* clazz) { // ... found = dvmHashTableLookup( gDvm.loadedClasses, hash, clazz, hashcmpClassByClass, true ); // ... }
どこにキャッシュされるか
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
10
⁃ gDvm はグローバルな変数を保持している
• つまり gDvm.loadedClasses はプロセスとライフサイクルが同じ
アプリのプロセスを再起動すればなんとかなるのでは!
じゃあどうする?
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
11
⁃ コンパイルした class ごとに dex 化
⁃ 起動時に dex からクラスをロード
• cf. Multidex
⁃ 変更された class のみ転送
具体的な方法
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
12
⁃ 転送するファイルの特定方法• Java のコンパイラの性質
⁃ コンパイラに任せてしまえる
優れている点
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
13
⁃ 通常の gradle そのまま
• クリーン後にビルド+インストール → 26 秒
⁃ cf. daemon=false だと 40 秒
• 変更してビルド + インストール → 4.5 秒
⁃ cf. daemon=false だと 18 秒
⁃ トライしていた手法• クリーン後にビルド + インストール → 27.5 秒
• 変更してビルド + インストール + 再起動 → 2.5 秒
⁃ Instant Run• 変更してビルド+インストール → 3 秒 (Activity の再起動なし)
実演
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
14
Instant Run
15Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
⁃ メソッドの実装の変更、クラスの追加削除は hot swap 可能
⁃ それ以外は大体 cold swap
ふむ。
Instant Run の仕様
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
16
⁃ メソッドの実装の変更には対応している⁃ シグニチャの変更やフィールドの変更に対応していない
⁃ java.lang.reflect.Proxy• 「動的プロキシのクラスおよびインスタンスを作成するstaticメソッドを提供」
⁃ ふむ。
Proxy っぽい?
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
17
interface Some { String method_1(); String method_2(); String method_3(); String method_4(); String method_5(); }
このインターフェイスに対して
説明
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
18
class QuadSome implements Some { public String method_1() { return String.valueOf(1 * 1); }
public String method_2() { return String.valueOf(2 * 2); }
public String method_3() { return String.valueOf(3 * 3); }
public String method_4() { return String.valueOf(4 * 4); }
public String method_5() { return String.valueOf(5 * 5); } }
Some instance = new QuadSome();
こういう実装をしたい
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
19
Some instance = (Some) Proxy.newProxyInstance( Some.class.getClassLoader(), new Class<?>[]{ Some.class }, (proxy, method, param) -> { int num = Integer.parseInt(method.getName().substring(7)); return String.valueOf(num * num); } );
動的にメソッドの実装ができる。
Proxy を使うことで
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
20
⁃ Proxy は interface に対してでしか使えない
⁃ あるクラスXに対して
• メソッドの実装部分を X_0 として切り出す
• X に対するメソッドの呼び出しは X_0 を参照するようにする
• 変更したメソッドの実装部分を X_1 として切り出す
• X は最新の X_n を参照するようにしておく
もう一工夫
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
21
class Foo { public int someField = 123;
public int someMethod() { return someField + 456; } }
模擬コード例
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
22
class Foo_0 { private Foo proxy;
Foo_0(Foo proxy) { this.proxy = proxy; }
public int someMethod() { return ivar.someField + 456; } }
class Foo { public int someField = 123;
private Class getDelegate() { // return Foo_0 instance }
public int someMethod() { return getDelegate().someMethod(); } }
こう変換してみる
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
23
public int someMethod() { return someField + 456; }
↓↓↓
public int someMethod() {
return someField + 789; }
メソッドの内容を変更
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
24
class Foo_1 extends Foo { private Foo proxy;
Foo_1(Foo proxy) { this.proxy = proxy; }
public int someMethod() { return proxy.someField + 789; } }
もう一度変換すると
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
25
private Class getDelegate() { File dex = findLatestDex();
if (dex != loadedDex) { delegate = loadDex(dex);
loadedDex = dex; }
return delegate; }
⁃ Foo_0 と Foo_1 は別クラス扱い• クラスがキャッシュされる問題を回避できる
委譲先を決める箇所の模擬コード
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
26※ ここまで推測
考え得る hot deploy の実現方法
⁃ Jack and Jill⁃ デバッグ時は Class Loading の制限を解除
余談
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
27
⁃ トライしていた手法• 転送するファイルの特定方法がシンプル
• プロセスの再起動が必要
• Instant Runェ…
⁃ Instant Run• hot swap は Activity の再起動すら不要
• (開発時とリリース時の同一性)
• (オーバーヘッド)
⁃ クラスがキャッシュされる問題さえなければ……
• さらなる改良に期待
まとめ
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
28
⁃ Bazel• http://bazel.io/docs/mobile-install.html• mobile-install —incremental
⁃ Buck• https://buckbuild.com/article/exopackage.html• exopackage
⁃ LayoutCast• https://github.com/mmin18/LayoutCast• IntelliJ と eclipse に対応
類似ツール
Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
29
Thanks!
30Copyright (C) DeNA Co.,Ltd. All Rights Reserved.
top related