30
Copyright (C) DeNA Co.,Ltd. All Rights Reserved. Jan 29, 2016 TOYAMA, Yosaku System Gr. DeNA Life Science, Inc. 爆速でAndroidアプリを ビルドするための仕組み

爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

Embed Size (px)

Citation preview

Page 1: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

Jan 29, 2016

TOYAMA, Yosaku System Gr.DeNA Life Science, Inc.

爆速でAndroidアプリをビルドするための仕組み

Page 2: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

⁃ 氏名: 外山 要作

⁃ 所属: DeNAライフサイエンス システムグループ

⁃ 入社: 2012年5月• 新規サービスの開発、運用

• Android、iOS

⁃ 好きな言語: Ruby と C#

⁃ 趣味: 競プロ、ショートコーディング

⁃ お酒

自己紹介

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

2

Page 3: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

Instant Run など

❌ 爆速でビルドして Android 開発の効率UP

◯ 爆速でビルドするための仕組みがどうやって成り立っているのか

本日の内容

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

3

Page 4: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

⁃ gradle はビルドに時間がかかる• 上手くやればなんとかできるのでは

• 細々と改良していた

⁃ ところが• テーマを決めたら → Instant Run !!!

• スライド書いたら → cold swap !!!

経緯

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

4

Page 5: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

⁃ 背景⁃ トライしていた手法• hot deploy の困難性

• 実現方法

⁃ Instant Run について• 推測

⁃ まとめ

アジェンダ

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

5

Page 6: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

ビルド時間を減らすのが大事な理由⁃ 無駄な時間が減る⁃ 開発効率の向上⁃ 小さな単位で結果を確認

背景

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

6

Page 7: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

トライしていた手法

7Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

Page 8: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

⁃ Java のソースコードを書き換えた結果を手早く確認したい

• ClassLoader をハックできないか

⁃ 上手く行かなかった• なぜ?

hot deploy の難しさ

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

8

Page 9: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

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)) { // ...

Page 10: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

bool dvmAddClassToHash(ClassObject* clazz) { // ... found = dvmHashTableLookup( gDvm.loadedClasses, hash, clazz, hashcmpClassByClass, true ); // ... }

どこにキャッシュされるか

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

10

⁃ gDvm はグローバルな変数を保持している

• つまり gDvm.loadedClasses はプロセスとライフサイクルが同じ

Page 11: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

アプリのプロセスを再起動すればなんとかなるのでは!

じゃあどうする?

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

11

Page 12: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

⁃ コンパイルした class ごとに dex 化

⁃ 起動時に dex からクラスをロード

• cf. Multidex

⁃ 変更された class のみ転送

具体的な方法

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

12

Page 13: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

⁃ 転送するファイルの特定方法• Java のコンパイラの性質

⁃ コンパイラに任せてしまえる

優れている点

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

13

Page 14: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

⁃ 通常の 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

Page 15: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

Instant Run

15Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

Page 16: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

⁃ メソッドの実装の変更、クラスの追加削除は hot swap 可能

⁃ それ以外は大体 cold swap

ふむ。

Instant Run の仕様

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

16

Page 17: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

⁃ メソッドの実装の変更には対応している⁃ シグニチャの変更やフィールドの変更に対応していない

⁃ java.lang.reflect.Proxy• 「動的プロキシのクラスおよびインスタンスを作成するstaticメソッドを提供」

⁃ ふむ。

Proxy っぽい?

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

17

Page 18: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

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

Page 19: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

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

Page 20: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

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

Page 21: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

⁃ Proxy は interface に対してでしか使えない

⁃ あるクラスXに対して

• メソッドの実装部分を X_0 として切り出す

• X に対するメソッドの呼び出しは X_0 を参照するようにする

• 変更したメソッドの実装部分を X_1 として切り出す

• X は最新の X_n を参照するようにしておく

もう一工夫

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

21

Page 22: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

class Foo { public int someField = 123;

public int someMethod() { return someField + 456; } }

模擬コード例

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

22

Page 23: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

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

Page 24: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

public int someMethod() { return someField + 456; }

↓↓↓

public int someMethod() {

return someField + 789; }

メソッドの内容を変更

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

24

Page 25: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

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

Page 26: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

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※  ここまで推測

Page 27: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

考え得る hot deploy の実現方法

⁃ Jack and Jill⁃ デバッグ時は Class Loading の制限を解除

余談

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

27

Page 28: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

⁃ トライしていた手法• 転送するファイルの特定方法がシンプル

• プロセスの再起動が必要

• Instant Runェ…

⁃ Instant Run• hot swap は Activity の再起動すら不要

• (開発時とリリース時の同一性)

• (オーバーヘッド)

⁃ クラスがキャッシュされる問題さえなければ……

• さらなる改良に期待

まとめ

Copyright (C) DeNA Co.,Ltd. All Rights Reserved.

28

Page 29: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

⁃ 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

Page 30: 爆速でAndroidアプリを ビルドするための仕組み DeNA TechCon #denatechcon

Thanks!

30Copyright (C) DeNA Co.,Ltd. All Rights Reserved.