Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Day1 Keynote in DroidKaigi 2016
Search
Daichi Furiya (Wasabeef)
February 17, 2016
Programming
6
12k
Day1 Keynote in DroidKaigi 2016
Day1 Keynote in DroidKaigi 2016
Daichi Furiya (Wasabeef)
February 17, 2016
Tweet
Share
More Decks by Daichi Furiya (Wasabeef)
See All by Daichi Furiya (Wasabeef)
DevFest Tokyo 2025 - Flutter のアプリアーキテクチャ現在地点
wasabeef
5
2k
About Flutter Architecture
wasabeef
1
250
2023 Flutter/Dart Summary
wasabeef
0
79
I/O Extended 2023 - Dart と Flutter の新機能
wasabeef
0
180
I/O Extended 2023 - Flutter 活用事例
wasabeef
10
3k
What it Takes to be a Flutter Developer
wasabeef
0
200
FlutterKaigi 2022 Keynote
wasabeef
1
610
Flutter Hooks を使ったアプリ開発 / App Development with the Flutter Hooks
wasabeef
2
1.4k
Flutter 2021 の振り返りと今後のアプリ開発に向けて / Looking back on Flutter 2021 and for future app development.
wasabeef
4
2.2k
Other Decks in Programming
See All in Programming
[DevinMeetupTokyo2025] コード書かせないDevinの使い方
takumiyoshikawa
2
250
Claude Code と OpenAI o3 で メタデータ情報を作る
laket
0
110
GUI操作LLMの最新動向: UI-TARSと関連論文紹介
kfujikawa
0
260
大規模FlutterプロジェクトのCI実行時間を約8割削減した話
teamlab
PRO
0
440
DMMを支える決済基盤の技術的負債にどう立ち向かうか / Addressing Technical Debt in Payment Infrastructure
yoshiyoshifujii
5
750
画像コンペでのベースラインモデルの育て方
tattaka
3
970
Git Sync を超える!OSS で実現する CDK Pull 型デプロイ / Deploying CDK with PipeCD in Pull-style
tkikuc
4
520
新しいモバイルアプリ勉強会(仮)について
uetyo
1
250
あまり知られていない MCP 仕様たち / MCP specifications that aren’t widely known
ktr_0731
0
220
それ CLI フレームワークがなくてもできるよ / Building CLI Tools Without Frameworks
orgachem
PRO
17
3.6k
DatadogのArchived LogsをSnowflakeで高速に検索する方法(Archive Searchでオワコンにならないことを祈って) / How to search Datadog Archived Logs quickly with Snowflake (hoping Datadog Archive Search doesn’t make this obsolete)
civitaspo
0
110
#QiitaBash TDDで(自分の)開発がどう変わったか
ryosukedtomita
1
350
Featured
See All Featured
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
30
2.2k
The Straight Up "How To Draw Better" Workshop
denniskardys
235
140k
GraphQLとの向き合い方2022年版
quramy
49
14k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
130
19k
Building a Scalable Design System with Sketch
lauravandoore
462
33k
A better future with KSS
kneath
238
17k
Designing Experiences People Love
moore
142
24k
Building Adaptive Systems
keathley
43
2.7k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
47
9.6k
Intergalactic Javascript Robots from Outer Space
tanoku
272
27k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
Mobile First: as difficult as doing things right
swwweet
223
9.9k
Transcript
OSSの動向を捉えた実装方針 wasabeef DroidKaigi 2016
About me Daichi Furiya wasabeef @wasabeef_jp CyberAgent, Inc.
OSSの動向を捉えた実装方針?
Research
GitHub/trending Android Weekly Android Arsenal Twitter Research
Introduction
Languages
Kotlin Java • RetroLambda • Lightweight-Stream-API • ThreeTen Android Languages
Kotlin by JetBrains
Kotlin 1.0 Java連携 …more Kotlin
RetroLambda by Esko Luontola
Lambda Method Reference RetroLambda view.setOnClickListener(v -> { finish(); }); view.setOnClickListener(this::something);
Lightweight-Stream-API by Victor Melnik
Stream API Lightweight-Stream-API Stream.of(lines) .map(str -> str.split(t)) .filter(arr -> arr.length
== 2) .map(arr -> new Word(arr[0], arr[1])) .collect(Collectors.toList());
Optional Lightweight-Stream-API Optional.ofNullable(getSupportActionBar()) .ifPresent(ab -> { ab.setDisplayHomeAsUpEnabled(true); ab.setHomeButtonEnabled(true); }); Optional.ofNullable(i.getStringExtra(EXTRA_URI))
.map(Uri::parse) .orElse(null);
ThreeTen Android by Jake Wharton
JSR-310 ThreeTen Android // Now LocalDateTime.now(); // 2016.2.18 10:30:40 LocalDateTime.of(2016,
2, 18, 10, 30, 40); // +1 truncated second LocalDateTime.now() .plusHours(1).truncatedTo(ChronoUnit.HOURS); // Epoch LocalDateTime.now() .toInstant(ZoneOffset.UTC).toEpochMilli();
Views
Support Library Android-ObservableScrollView Calligraphy florent37/ViewAnimator View
Support Library by Google
View (RecyclerView...etc ) RenderScript MultiDex Annotations Support Library
Sample... Support Library compile 'com.android.support:support-v4:+' compile 'com.android.support:appcompat-v7:+' compile 'com.android.support:design:+' compile
'com.android.support:recyclerview-v7:+' compile 'com.android.support:cardview-v7:+' compile 'com.android.support:support-annotations:+' compile ‘com.android.support:percent:+'
Android ObservableScrollView by ksoichiro
ScrollView , ListView, RecyclerViewなどのスクロー ル制御を監視 Android-ObservableScrollView https://github.com/ksoichiro/Android-ObservableScrollView
Calligraphy by Christopher Jenkins
Calligraphy アプリ全体にFont適用 xmlでもjavaでも可 簡単 https://github.com/chrisjenx/Calligraphy
CalligraphyConfig Calligraphy CalligraphyConfig.initDefault(new CalligraphyConfig.Builder() .setDefaultFontPath("fonts/mplus-2p-regular.ttf") .setFontAttrId(R.attr.fontPath) .build();
ViewAnimator by Florent CHAMPIGNY
ViewAnimator AnimatorSet animSet = new AnimatorSet(); animSet.playTogether( ObjectAnimator.ofFloat(image, View.TRANSLATION_Y, -1000,0),
ObjectAnimator.ofFloat(image, View.ALPHA, 0,1), ObjectAnimator.ofFloat(text, View.TRANSLATION_X, -200,0) ); animSet.setInterpolator(new DescelerateInterpolator()); animSet.setDuration(2000); animSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { AnimatorSet anim = new AnimatorSet(); anim.playTogether( ObjectAnimator.ofFloat(image, View.SCALE_X, 1f, 0.5f, 1f), ObjectAnimator.ofFloat(image, View.SCALE_Y, 1f, 0.5f, 1f) ); anim.setInterpolator(new AccelerateInterpolator()); anim.setDuration(1000); anim.start(); } }); animSet.start();
ViewAnimator ViewAnimator .animate(image) .translationY(-1000, 0) .alpha(0, 1) .andAnimate(text) .dp().translationX(-200, 0)
.descelerate() .duration(2000) .thenAnimate(image) .scale(1f, 0.5f, 1f) .accelerate() .duration(1000) .start();
DataBinding
Android Data Binding by Google
xmlとjavaのBinding Annotation Processing たまにビルドエラーが出る.. Android Data Binding
Android Data Binding <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id=“@+id/user_name“
android:layout_width="wrap_content" android:layout_height="wrap_content"/> <ImageView android:id=“@+id/user_thumbnail“ android:layout_width="160dp" android:layout_height="120dp"/> </LinearLayout>
Android Data Binding <layout xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView
android:id=“@+id/user_name“ android:layout_width="wrap_content" android:layout_height="wrap_content"/> <ImageView android:id=“@+id/user_thumbnail“ android:layout_width="160dp" android:layout_height="120dp"/> </LinearLayout> </layout>
Android Data Binding <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data name="MainBinding"> <variable name="user" type="com.example.User"/>
</data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id=“@+id/user_name“ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.name}" android:textColor="@{user.isWomen? @color/pink : @color:blue}"/> <ImageView android:id=“@+id/user_thumbnail“ android:layout_width="160dp" android:layout_height="120dp" bind:imageUrl="@{user.thumbnail}"/> </LinearLayout> </layout>
Custom Setters Android Data Binding public final class ImageBindingAdapters {
@BindingAdapter({ "bind:image" }) public static void loadImage(ImageView view, String url) { Glide.with(view.getContext().getApplicationContext()) .load(url) .into(view); } } <ImageView android:layout_width="160dp" android:layout_height="120dp" bind:imageUrl="@{user.thumbnail}"/>
DataBindingUtil Android Data Binding @Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); MainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); binding.userName.setOnClickListener(v -> ...); } <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data name="MainBinding"> <variable name="user" type="com.example.User"/> </data> <!-- ... -- </layout>
Networking
(Volley) Retrofit + OkHttp Networking
Retrofit+OkHttp by Square
REST RxJava support Pluggable client Converters(Gson, Wire...) Retrofit+OkHttp
Retrofit Retrofit retrofit = new Retrofit.Builder() .client(okHttpClient) .baseUrl("https://api.github.com") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build(); GitHubService service = retrofit.create(GitHubService.class); service.repos("wasabeef") .subscribe(repo -> { // ... }, e -> { Timber.w(e); }); public interface GitHubService { @GET(/users/{user}/repos) Observable<List<Repo>> repos(@Path(user) String user); }
JSON & ProtoBuf
(Gson) (Jackson) (Moshi) Wire Serialisations
Wire by Square
Protocol Buffers okioを利用 Server/Clientで定義を共通化 Webからは扱いづらい Wire
Wire syntax = "proto3"; package helloworld; message HelloRequest { string
name = 1; } message HelloReply { string message = 1; } hello.proto
Wire Build # java -jar wire-compiler-2.0.1-jar-with-dependencies.jar \ —proto_path=. \ —java_out=.
hello.proto Writing helloworld.HelloRequest to . Writing helloworld.HelloReply to .
Parcelables
(android-parcelable-intellij-plugin) Parceler (Auto-Value-Parcel) Icepick Parcelables
Parceler by John Ericksen
Annotation Processing AutoValue Parceler
Parceler @Parcel public class User { public String name; public
String thumbnail; User() { } } // Wrap Bundle bundle = new Bundle(); bundle.putParcelable("user", Parcels.wrap(user)); // Unwrap User user = Parcels.unwrap(getIntent().getParcelableExtra("user"));
Icepick by Frankie Sardo
Simple Icepick public class MainActivity extends Activity { @State String
name; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Icepick.restoreInstanceState(this, savedInstanceState); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Icepick.saveInstanceState(this, outState); } }
Image Loaders
(Universal Image Loader) Picasso Glide Fresco Image Loaders
Picasso by Square
Simple Cache Transformations Debug Picasso
Simple Picasso Picasso.with(context) .load(“http://i.imgur.com/DvpvklR.png”) .setIndicatorsEnabled(true) .into(imageView);
Glide by Google (unofficial)
Glide Bitmap Pool Gif support Thumbnail Animation support Transformations
Simple Glide Glide.with(context) .load(“http://i.imgur.com/DvpvklR.png”) .crossFade() .thumbnail(.1f) .into(imageView);
Picasso? Glide?
Picasso? Glide? http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en
Picasso? Glide? http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en
Picasso? Glide? http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en Library's size
Picasso? Glide? http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en Method Count
Fresco by Facebook
Fresco Ashmem heap View Gif support Animation
Fresco
Fresco <com.facebook.drawee.view.SimpleDraweeView android:id="@+id/my_image_view" android:layout_width="20dp" android:layout_height="20dp" fresco:fadeDuration="300" fresco:actualImageScaleType="focusCrop" fresco:placeholderImage="@color/wait_color" fresco:placeholderImageScaleType="fitCenter" fresco:failureImage="@drawable/error"
fresco:failureImageScaleType="centerInside" fresco:retryImage="@drawable/retrying" fresco:retryImageScaleType="centerCrop" fresco:progressBarImage="@drawable/progress_bar" fresco:progressBarImageScaleType="centerInside" fresco:progressBarAutoRotateInterval="1000" fresco:backgroundImage="@color/blue" fresco:overlayImage="@drawable/watermark" fresco:pressedStateOverlayImage="@color/red" fresco:roundAsCircle="false" fresco:roundedCornerRadius="1dp" fresco:roundTopLeft="true" fresco:roundTopRight="false" fresco:roundBottomLeft="false" fresco:roundBottomRight="true" fresco:roundWithOverlayColor="@color/corner_color" fresco:roundingBorderWidth="2dp" fresco:roundingBorderColor="@color/border_color" />
Effects
Blurry Android StackBlur GPUImage for Android Picasso/Glide Transformations Effects
Blurry by Wasabeef
Blurry Blur RenderScript DownSampling
Android StackBlur by Enrique López Mañas
Blur RenderScript NDK Android StackBlur
Android StackBlur https://github.com/kikoso/android-stackblur
Android StackBlur https://github.com/kikoso/android-stackblur
GPUImage for Android by CyberAgent
GPUImage OpenGL ES 70 Filters Tone Curve
Picasso/Glide Transformations by Wasabeef
Transformations Picasso, Glide, Fresco Crop Blur Color Filter GPU Filter
DI
(AndroidAnnotations) (Square/Dagger) Google/Dagger Butter Knife DI
Dagger2 by Google
Annotation Processing テスト・メンテナンス Dagger2
Butter Knife by Jake Wharton
View Injection Butter Knife public class MainActivity extends Activity {
@Bind(R.id.user_name) TextView userName; @Bind(R.id.user_thumbnail) ImageView thumbnail; @BindString(R.string.title) String title; @BindDrawable(R.drawable.graphic) Drawable graphic; @BindColor(R.color.red) int red; @BindDimen(R.dimen.spacer) float spacer; }
OnClick, OnTextChange, OnFocusChange...etc Butter Knife @OnClick(R.id.user_name) public void click(View view)
{ // ... } @OnClick({ R.id.user_name, R.id.user_thumbnail }) public void click(View door) { // ... }
FRP
RxJava RxAndroid (RxBinding) (RxLifecycle) FRP
RxJava by Netflix
非同期、リスト操作が簡単 Retrolambdaがあると良い Leakが怖い Retrofitと併用 RxJava
Sample RxJava Observable.just("Google", "Apple", "MicroSoft") .map(String::toUpperCase) .subscribe(name -> { Timber.d("OnNext
" + name); }, throwable -> { Timber.d("OnError"); });
Sample RxJava Observable.just("Google", "Apple", "MicroSoft") .map(String::toUpperCase) .subscribe(name -> { Timber.d("OnNext
" + name); }, throwable -> { Timber.d("OnError"); });
lift compose rxjava-extras (by Dave Moten) RxJava
RxAndroid by RxAndroid authors
Scheduler RxAndroid
Main Thread RxAndroid Observable.just("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(/* an Observer */); final Handler handler = new Handler(); Observable.just("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(HandlerScheduler.from(handler)) .subscribe(/* an Observer */) Arbitrary Thread
DB/ORM
(ActiveAndroid) Realm (SQLBrite) Android Orma DB/ORM
Realm by Realm
Not SQLite RxJava? Thread間の制約 Realm
Android Orma by gfx
SQLite Annotation Processing RxJava Semi-automatic migration Android Orma
Event Buses
Otto EventBus (RxJava) Event Buses
Otto by Square
Deprecated! Square @Produceが少し便利 Otto
EventBus by greenrobot
EventBus EventBus3 Threading Support Annotation Processing
Debugging
Timber Hugo Debugging Stetho Leak Canary Takt Crashlytics
Timber by Jake Wharton
Logger Timber.Treeを継承して追加する ことで、出力先を自由に変えれる Timber if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); }
else { Timber.plant(new CrashReportingTree()); }
Hugo by Jake Wharton
AnnotationでMethod Callsを Loggingできる Hugo @DebugLog public String getName(String first, String
last) { return first + " " + last; } V/Example: ⇢ getName(first="Daichi", last="Furiya") V/Example: ⇠ getName [0ms] = “Daichi Furiya"
Stetho by Facebook
Chrome Developer Toolsを利 用して、Network、Dataの状 況の把握 Stetho-Realm (by uPhyca) Stetho
Stetho
Stetho
Stetho
Leak Canary by Square
メモリリークを検出 Leak Canary
Takt by Wasabeef
FPSを表示 Takt
Crashlytics by Twitter
クラッシュの検出 リアルタイム分析 NDKサポート Crashlytics
Crashlytics
Others
Multi-Dex ProGuard Others
MultiDex by Google
メソッド数の65k制限 最近は安定してきた MultiDex
Android Methods Count MultiDex https://plugins.jetbrains.com/plugin/8076
ProGuard by GuardSquare
ソースコードの難読化 未使用なリソースの削除 難しい ProGuard
ProGuard 15M sample-production.apk Total methods count: 99,280 (multidex enabled) 13M
sample-production-proguard.apk Total methods count: 54,977 Proguard あり Proguard なし
Conclusion
Thank you. twitter.com/wasabeef_jp wasabeef.jp github.com/wasabeef