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
Clean, Easy & New Android Arch. : Devoxx-BE 17
Search
Britt Barak
November 09, 2017
Technology
3
320
Clean, Easy & New Android Arch. : Devoxx-BE 17
Britt Barak
November 09, 2017
Tweet
Share
More Decks by Britt Barak
See All by Britt Barak
[Vonage] Introducing Conversations
brittbarak
1
120
Kids, Play Nice! Kotlin-Java Interop In Mind
brittbarak
2
350
Sharing is Caring- Getting Started with Kotlin Multiplatform
brittbarak
2
1.9k
Between JOMO and FOMO: You are reshaping communication.
brittbarak
2
1.2k
Build Apps For The Ones You Love
brittbarak
1
110
What an ML-ful World! MLKit for Android dev.
brittbarak
0
130
Make your app dance with MotionLayout
brittbarak
8
1.3k
Who's afraid of ML? V2 : First steps with MlKit
brittbarak
1
440
Oh, the places you'll go! Cracking Navigation on Android
brittbarak
0
460
Other Decks in Technology
See All in Technology
Valuable Software Engineering
avandeursen
0
250
【swonet.conf_】NOCメンバーが語るSTMの実態!! ~ShowNetから若者への贈り物~
shownet
PRO
0
230
【ログ分析勉強会】EDR ログで内部不正を検出できるのか、Copilot に聞いてみた
hssh2_bin
2
270
ガバメントクラウド開発と変化と成長する組織 / Organizational change and growth in developing a government cloud
kazeburo
2
560
【shownet.conf_】持続可能な次世代Wi-Fi運用に向けて
shownet
PRO
0
260
Create Inquiry via Bedrock / 生成 AI で問い合わせ品質は変わるのか?思いついてぱっと作ったものを供養してみる
kazzpapa3
1
200
いまからでも遅くない! コンテナでWebアプリケーションを 動かしてみよう(2-1)WebAPI座学
nomu
0
140
All your memory are belong to… whom?
ennael
PRO
0
550
つよつよリーダーが 抜けたらどうする? 〜ナビタイムのAgile⽀援組織の変遷〜
navitimejapan
PRO
22
13k
10Xでのデータ基盤の変遷とこれから: データマネジメントのリアル 〜BtoB企業3社の歩みとこれから〜
10xinc
6
1.2k
Product Utilization of Large Language Models Starting Today
ymatsuwitter
3
750
Rubyはなぜ「たのしい」のか? / Why is Ruby a programmers' best friend? #tqrk15
expajp
4
1.7k
Featured
See All Featured
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
191
16k
We Have a Design System, Now What?
morganepeng
49
7.1k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.3k
Testing 201, or: Great Expectations
jmmastey
38
7k
The World Runs on Bad Software
bkeepers
PRO
65
11k
Imperfection Machines: The Place of Print at Facebook
scottboms
263
13k
A designer walks into a library…
pauljervisheath
201
24k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
41
9.2k
KATA
mclloyd
27
13k
Documentation Writing (for coders)
carmenintech
65
4.3k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
105
48k
YesSQL, Process and Tooling at Scale
rocio
167
14k
Transcript
Clean, Easy & New Android Architecture Britt Barak Devoxx Belgium
2017
Britt Barak @brittBarak Android Developer & TL Android Academy Women
Techmakers
And you...?
One thing is sure Everything will change. Quick.
My First Startup
“As You Like It” / W. Shakespeare ״All the world’s
a stage, And all the men and women merely players; They have their exits and their entrances, And one man in his time plays many parts:״
Players Exits and Entrances Plays a part → Single responsibility
→ Defined interfaces → Separation of concerns
Our Goals - Write quicker - Change easily - Rely
on the code (test) - Easy for others to understand
App Architecture with the new components
Who loves Jelly Beans?
None
None
None
None
Where to start?
Presentation Layer Data Layer Domain Layer
None
Is about: UI Is not about: logic. Presentation Layer
class JellyBeanViewModel extends ViewModel { String flavor; int r; int
g; int b;
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_add_bean); setViewModel()
setUi(); }
void setViewModel(){ this.viewModel = new JellyBeanViewModel(); }
UI represents the ViewModel state
Presentation Layer Domain Layer View ViewModel User Input
1. Update the viewModel
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { switch
(seekBar.getId()) { case R.id.sb_r: viewModel.setR(progress); break; //...
2. View Model notifies
- Callbacks - Data binding - RxJava - LiveData How
to notify?
LiveData - Observable data holder class - Lifecycle-aware - Updates
only on started / resumed state - Removes itself on destroyed state - No memory leaks https://developer.android.com/topic/libraries/architecture/livedata.html
public class JellyBeanViewModel extends ViewModel { String flavor; LiveData<Integer> r;
LiveData<Integer> g; LiveData<Integer> b;
public class JellyBeanViewModel extends ViewModel { LiveData<Integer> r; public void
setR(int r) { r = validate(r); this.r.setValue(r); }
3. Create an observer
Observer<Integer> colorChangedObserver = (Integer integer) -> { beanIv.setColorFilter(viewModel.getColor()); };
4. Observe ViewModel changes
viewModel.getR() .observe(this, colorChangedObserver); Observer is tied to this lifecycle, and
will be removed as the lifecycle ends. LivaData
Presentation Layer Domain Layer ViewModel View set() User Input notify
Persisting ViewModel
None
setViewModel() -> { this.viewModel = ViewModelProviders.of(this) .get(JellyBeanViewModel.class); }
And for more complex ViewModels?
1. Multiple LiveData Objects 2. Separate class → Lifecycle Aware
Suggestions: Peach Dream Peach Pie P
Save The Jelly Bean!
Is about: “objective” data Is not about: caring about the
consumer Data Layer
- Data Model - Repository Data Layer
class JellyBeanViewModel String flavor; int r; int g; int b;
class JellyBean String flavor; String color;
- One per data type (e.g jelly bean, recipe, user….).
- Encapsulates the logic of getting/setting the data. - CRUD operations (Create, Read, Update, Delete) Repository
class JellyBeanRepo{ }
class JellyBeanRepo{ LiveData<JellyBean> getJellyBean(String id){ }
LiveData<JellyBean> getJellyBean(String id) { return myFirebaseClient.getJellyBean(id) }
LiveData<JellyBean> getJellyBean(String id) { if (cache.hasJellyBean(id)){ return cache.getJellyBean(id); } else{
return myFirebaseClient.getJellyBean(id) } }
LiveData<JellyBean> getJellyBean(String id) { if (cache.hasJellyBean(id)){ return cache.getJellyBean(id); } else
if (appDatabase.hasJellyBean(id)) { return appDatabase.jellyBeanDao().getJellyBean(id); } else{ return myFirebaseClient.getJellyBean(id) }
LiveData<JellyBean> getJellyBean(String id) { if (cache.hasJellyBean(id)){ return cache.getJellyBean(id); } else
if (appDatabase.hasJellyBean(id)) { return appDatabase.jellyBeanDao().getJellyBean(id); } else{ return myApiClient.getJellyBean(id) }
Presentation Layer Data Layer ViewModel DataModel ?
Presentation Layer Data Layer Domain Layer DataModel Interactor ViewModel
Is about: converting models Is not about: UI, Data Domain
Layer
Save The Jelly Bean!
1. Create Use Case
class SaveJellyBean extends UseCase { void execute() { }
2. Convert Models
public class SaveJellyBean extends UseCase { public void execute(JellyBeanViewModel viewModel)
{ JellyBean data = prepareDataModel(viewModel); }
JellyBean prepareDataModel(JellyBeanViewModel viewModel){ String beanColorString = String.format("#%06X", (0xFFFFFF & viewModel.getColor()));
JellyBean data = new JellyBean(viewModel.getFlavor(), beanColorString); return data; }
3. Call Repository
public class SaveJellyBean extends UseCase { public void execute(JellyBeanViewModel viewModel)
{ JellyBean data = prepareDataModel(viewModel); repo.saveBean(data); }
4. Execute
Presentation Layer Data Layer Domain Layer DataModel Interactor ViewModel
Presentation Layer Data Layer Domain Layer SaveJellyBean .execute() JellyBeanRepo .save()
Reusing interactors - Edit Jelly Bean screen - Different UI
- Same SaveJellyBean use case
Some more about Repository...
Data Layer Network Service Local DB Cache? Domain Layer Some
Usecase
Local database - Persistence between sessions - Single source of
truth
Room - Wraps SQLite database - Generates boilerplate code -
Verifies queries at compile time - Denies calls on the UI thread
1. Create Entity
@Entity public class JellyBean { @PrimaryKey @NonNull String id; String
flavor; String color;
2. Create Dao
@Dao public interface JellyBeanDao { @Query("select * from jellyBean") LiveData<List<JellyBean>>
getAllJellyBeans(); @Query("select * from jellyBean where id = :beanId") LiveData<JellyBean> getJellyBean(String beanId); }
@Insert(onConflict = REPLACE) void insertJellyBean(JellyBean jellyBean); @Delete void deleteJellyBean(JellyBean jellyBean);
3. Create App Database instance
@Database(version = 1, entities = {JellyBean.class}) public abstract class AppDatabase
extends RoomDatabase { private static AppDatabase instance; public abstract JellyBeanDao jellyBeanDao(); //init and destroy code.. }
public static AppDatabase getInMemoryDatabase(Context appContext){ if (instance == null) {
instance = Room.inMemoryDatabaseBuilder( appContext, AppDatabase.class).build(); } return instance; } public static void destroyInstance() { instance = null; }
4. Use from Repository
public LiveData<JellyBean> getJellyBean(String id) { if (cache.hasJellyBean(id)){ //… return appDatabase.jellyBeanDao().getJellyBean(id);
//... }
Presentation Layer Data Layer Domain Layer SaveJellyBean .execute() JellyBeanRepo .save()
LocalDB JellyBeanDao
Presentation Layer • View • ViewModel • Presenter Data Layer
• Repository • DataModel Domain Layer • Interactor • Use case
Players Exits and Entrances Plays a part → Single responsibility
→ Defined interfaces → Separation of concerns
Our Goals - Write quicker - Change easily - Rely
on the code (test) - Easy for others to understand
Get my notes on - @britt.barak Keep in touch! Britt
Barak @brittBarak Thank you!
Thank You !