爆速でandroidアプリを ビルドするための仕組み dena techcon #denatechcon

Post on 16-Apr-2017

7.750 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

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