Slide 1

Slide 1 text

AndroidでOutOfMemory
 にならないためのメモリ戦略 wasabeef AbemaTV Developer Conference 2016

Slide 2

Slide 2 text

About me 降矢 大地 (Daichi Furiya) Google Developer Expert AbemaTV, Inc. @wasabeef_jp wasabeef

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

10-14 00:38:00.683 13057-13057/tv.abema E/AndroidRuntime: FATAL EXCEPTION: main Process: tv.abema.debug, PID: 13057 java.lang.OutOfMemoryError: THIS IS FAKE at tv.abema.components.activity.MainActivity.onCreate(MainActivity.java:366) at android.app.Activity.performCreate(Activity.java:6664) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2599) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6077) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

Slide 5

Slide 5 text

OutOfMemoryError

Slide 6

Slide 6 text

java.lang.OutOfMemoryError Java Heapが不足している 画像(Bitmap)を持ち過ぎている場合が多い OutOfMemoryError

Slide 7

Slide 7 text

Java HeapSize Model getMemoryClass getLargeMemoryClass Nexus 5X 192MB 512MB Xperia Z1 (SOL23) 192MB 512MB Galaxy Nexus 96MB 256MB Nexus 7 (2013) 192MB 512MB Nexus 7 (2012) 64MB 384MB Nexus S 48MB 128MB

Slide 8

Slide 8 text

AbemaTV おおよその通信量(動画)

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

リリース済みアプリが 強制終了するのは そもそもテスト不足?

Slide 11

Slide 11 text

Fragmentation

Slide 12

Slide 12 text

Device Fragmentation http://opensignal.com/reports/

Slide 13

Slide 13 text

Brand Fragmentation http://opensignal.com/reports/

Slide 14

Slide 14 text

Screen Size Fragmentation (Android) http://opensignal.com/reports/

Slide 15

Slide 15 text

Screen Size Fragmentation (iOS) http://opensignal.com/reports/

Slide 16

Slide 16 text

AbemaTV

Slide 17

Slide 17 text

Distribution (AbemaTV) Version Codename Distribution Android 6.0 Marshmallow 28.38% Android 5.0 Lollipop 22.72% Android 4.4 KitKat 20.09% Android 5.1 Lollipop 13.60% Android 4.2 Jelly Bean 9.60% Android 4.1 Jelly Bean 4.29% Android 4.3 Jelly Bean 0.79% Android 7.0 Nougat 0.52% Android 7.1 Nougat 0.00% Android 4.0.3 - 4.0.4 Ice Cream Sandwich 0.00% 4.1 4.2 5.1 4.4 5.0 6.0

Slide 18

Slide 18 text

Distribution (AbemaTV) Model Brand Distribution Xperia Z3 (SO-01G) Sony 2.81% Xperia Z3 (SOL26) Sony 2.40% Xperia Z5 (SOV32) Sony 2.39% Xperia Z5 (SO-01H) Sony 2.10% Z3 Compact (SO-02G) Sony 1.96% Qua tab 01 (KYT31) 京セラ 1.47% Xperia Z4 (SOV31) Sony 1.47% Galaxy S5 (SC-04F) Samsung 1.45% Nexus 7 (2013) Google 1.45% Xperia A (SO-04E) Sony Ericsson 1.37% その他 その他 81.13% その他 Xperia A (SO-04E) Nexus 7 (flo) Galaxy S5 (SC-04F) Xperia Z4 (SOV31) Qua tab 01 (KYT31) Z3 Compact (SO-02G) Xperia Z5 (SO-01H) Xperia Z5 (SOV32) Xperia Z3 (SOL26) Xperia Z3 (SO-01G)

Slide 19

Slide 19 text

Video Resolution

Slide 20

Slide 20 text

1080p (bandwidth = 4200000) 720p (bandwidth = 2200000) 480p (bandwidth = 1400000) 360p (bandwidth = 900000) 240p (bandwidth = 300000) Video Resolution

Slide 21

Slide 21 text

Video Resolution 解像度 Wi-Fi Mobile xxxhdpi 1080p 360p 560 dpi 1080p 360p xxhdpi 720p 360p 400 dpi 720p 360p xhdp 480p 240p hdp 360p 240p mdp 240p 240p

Slide 22

Slide 22 text

ExoPlayerでは Playlistの順番を意識する

Slide 23

Slide 23 text

#EXTM3U #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=300000 240/playlist.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=900000 360/playlist.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1400000 480/playlist.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2200000 720/playlist.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=4200000 1080/playlist.m3u8 ExoPlayerではPlaylistの順番を意識する

Slide 24

Slide 24 text

ExoPlayerではPlaylistの順番を意識する 240/playlist.m3u8 BANDWIDTH: 300000 Header 360/playlist.m3u8 BANDWIDTH: 900000 480/playlist.m3u8 BANDWIDTH: 1400000 720/playlist.m3u8 BANDWIDTH: 2200000 1080/playlist.m3u8 BANDWIDTH: 4200000

Slide 25

Slide 25 text

最初はPlaylistの一番上を読み込む 転送速度によってBandwidthを切り替える どのPlaylistを読むか決定する DefaultだとLimit指定は出来ない DefaultBandwidthMeter class

Slide 26

Slide 26 text

ExoPlayerの インスタンスは1つにする

Slide 27

Slide 27 text

ExoPlayerのインスタンスは1つにする

Slide 28

Slide 28 text

ExoPlayerのインスタンスは1つにする playing

Slide 29

Slide 29 text

ExoPlayerのインスタンスは1つにする playing

Slide 30

Slide 30 text

Heapの少ない端末では保持しきれない Nexus7などの端末はDecoder(OMX)の
 インスタンス生成制限に到達する場合がある Surfaceも破棄できてれば尚良い ExoPlayerのインスタンスは1つにする

Slide 31

Slide 31 text

ExoPlayerで OkHttp Extensionを使う

Slide 32

Slide 32 text

HttpDataSource実装 Okhttpでキャッシュサイズの指定をする HTTP/2に対応しやすい ExoPlayerでOkHttp Extensionを使う

Slide 33

Slide 33 text

DataSource

Slide 34

Slide 34 text

HttpDataSource FileDataSource AssetDataSource ContentDataSource DataSource

Slide 35

Slide 35 text

H.264/H.265のDecoderが HW実装かSW実装かを把握する

Slide 36

Slide 36 text

SW (OMX.google)か
 HW (OMX.qcom/OMX. Nvidia等)を確認する libstagefright/MediaCodec.cppなどの
 実装も読む SW実装は、再生が不安定になる場合がある H.264/H.265のDecoderがHW実装かSW実装かを把握する

Slide 37

Slide 37 text

Bitmap

Slide 38

Slide 38 text

Supported Image Formats Format Encoder Decoder Details File Type JPEG ◯ ◯ Base Progressive .jpg GIF ◯ .gif PNG ◯ ◯ .png BMP ◯ .bmp WebP ◯ 4.+ ◯ 4.+ .webp

Slide 39

Slide 39 text

画像を扱う基本的なAPI BitmapFactoryでオブジェクト生成する OutOfMemoryを起こす原因になりやすい Bitmap class

Slide 40

Slide 40 text

inSampleSize

Slide 41

Slide 41 text

inSampleSize public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; }

Slide 42

Slide 42 text

inSampleSize public static Bitmap decodeSampledBitmapFromResource( Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); }

Slide 43

Slide 43 text

inSampleSize imageView.setImageBitmap( decodeSampledBitmapFromResource(res, R.id.myimage, 100, 100));

Slide 44

Slide 44 text

inPreferredConfig Bitmap.Config

Slide 45

Slide 45 text

ARGB_8888 ARGB_4444 RGB_565 ALPHA_8 inPreferredConfig & Bitmap.Config

Slide 46

Slide 46 text

inPurgeableは使わない

Slide 47

Slide 47 text

再利用性の無いBitmapを自動で解放する ただの地雷 LOLIPOPから非推奨 UIのパフォーマンス低下につながる inPurgeableは使わない

Slide 48

Slide 48 text

Glide

Slide 49

Slide 49 text

Bump Technologies Image loading framework Media decoding Memory and disk caching Glide

Slide 50

Slide 50 text

Glide Glide.with(context) .load("https://wasabeef.jp/sample.jpg") .into(imageView);

Slide 51

Slide 51 text

ARGB_8888とRGB_565を使い分ける

Slide 52

Slide 52 text

ARGB_8888とRGB_565を使い分ける

Slide 53

Slide 53 text

ARGB_8888とRGB_565を使い分ける RGB_565

Slide 54

Slide 54 text

ARGB_8888とRGB_565を使い分ける ARGB_8888

Slide 55

Slide 55 text

GlideModule public class GlideModule extends OkHttpGlideModule { @Inject OkHttpClient client; @Override public void applyOptions(Context context, GlideBuilder builder) { builder.setMemoryCache(new LruResourceCache(getMaxCacheSize(context))) .setDecodeFormat(DecodeFormat.PREFER_ARGB_8888); } private int getMaxCacheSize(Context context) { ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); return (activityManager.getMemoryClass() * 1024 * 1024) / 4; } }

Slide 56

Slide 56 text

GlideModule public class GlideModule extends OkHttpGlideModule { @Inject OkHttpClient client; @Override public void applyOptions(Context context, GlideBuilder builder) { builder.setMemoryCache(new LruResourceCache(getMaxCacheSize(context))) .setDecodeFormat(DecodeFormat.PREFER_ARGB_8888); } private int getMaxCacheSize(Context context) { ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); return (activityManager.getMemoryClass() * 1024 * 1024) / 4; } }

Slide 57

Slide 57 text

ARGB_8888とRGB_565を使い分ける Glide.with(context) .load("https://wasabeef.jp/sample.jpg") .into(imageView);

Slide 58

Slide 58 text

ARGB_8888とRGB_565を使い分ける Glide.with(context) .load("https://wasabeef.jp/sample.jpg") .asBitmap() .format(DecodeFormat.PREFER_RGB_565) .into(imageView);

Slide 59

Slide 59 text

キャッシュさせるサイズを意識する

Slide 60

Slide 60 text

キャッシュさせるサイズを意識する

Slide 61

Slide 61 text

キャッシュさせるサイズを意識する

Slide 62

Slide 62 text

キャッシュさせるサイズを意識する 550 x 315

Slide 63

Slide 63 text

キャッシュさせるサイズを意識する 430 x 245

Slide 64

Slide 64 text

キャッシュさせるサイズを意識する 550 x 315 430 x 245

Slide 65

Slide 65 text

“/image.jpg?w=550&h=315” キャッシュさせるサイズを意識する

Slide 66

Slide 66 text

サイズは違うがGlideのキャッシュは 1つのサイズに統一して持っていたほうが 効率がいい場合もある キャッシュさせるサイズを意識する

Slide 67

Slide 67 text

表示させるサイズを意識する

Slide 68

Slide 68 text

表示させるサイズを意識する 550 x 315 430 x 245

Slide 69

Slide 69 text

表示させるサイズを意識する Glide.with(context) .load("https://wasabeef.jp/sample.jpg") .into(imageView);

Slide 70

Slide 70 text

表示させるサイズを意識する Glide.with(context) .load("https://wasabeef.jp/sample.jpg") .override(550, 315) .into(imageView);

Slide 71

Slide 71 text

表示させるサイズを意識する Glide.with(context) .load("https://wasabeef.jp/sample.jpg") .override(550, 315) .centerCrop() .into(imageView);

Slide 72

Slide 72 text

Webpを使おう

Slide 73

Slide 73 text

Google.webp Android 4.+ JPEG/PNGに対して約30%小さい Webpを使おう

Slide 74

Slide 74 text

LargeHeap...

Slide 75

Slide 75 text

よく使うToolsの紹介

Slide 76

Slide 76 text

Memory Monitor Allocation Tracker Debug GPU Overdraw Leak Canary Stetho Takt Tools

Slide 77

Slide 77 text

Memory Monitor

Slide 78

Slide 78 text

Memory Profiler Memory Monitor

Slide 79

Slide 79 text

Allocation Tracker

Slide 80

Slide 80 text

メモリ割り当て状況の確認 Allocation Tracker

Slide 81

Slide 81 text

Debug GPU Overdraw

Slide 82

Slide 82 text

描画回数の最適化 Debug GPU Overdraw

Slide 83

Slide 83 text

Debug GPU Overdraw

Slide 84

Slide 84 text

Debug GPU Overdraw

Slide 85

Slide 85 text

Stetho

Slide 86

Slide 86 text

AndroidでもChrome DevTool Stetho

Slide 87

Slide 87 text

Stetho

Slide 88

Slide 88 text

Stetho

Slide 89

Slide 89 text

Leak Canary

Slide 90

Slide 90 text

メモリリークを検知 Leak Canary

Slide 91

Slide 91 text

Takt

Slide 92

Slide 92 text

FPSを表示 Wasabeef Takt

Slide 93

Slide 93 text

Conclusion

Slide 94

Slide 94 text

Thank you. twitter.com/wasabeef_jp wasabeef.jp github.com/wasabeef Photo by Japan Travel And Tourism Association is licensed under CC BY 4.0 International