🔙 Quay lại trang tải sách pdf ebook Advanced Android App Architecture: Real-world app architecture in Kotlin 1.3
Ebooks
NhĂłm Zalo
Advanced Android App Architectures Advanced Android App Architecture
Advanced Android App Architecture
By Yun Cheng and Aldo Olivares DomĂnguez
Copyright ©2019 Razeware LLC.
Notice of Rights
All rights reserved. No part of this book or corresponding materials (such as text, images, or source code) may be reproduced or distributed by any means without prior written permission of the copyright owner.
Notice of Liability
This book and all corresponding materials (such as source code) are provided on an “as is” basis, without warranty of any kind, express of implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in action of contract, tort or otherwise, arising from, out of or in connection with the software or the use of other dealing in the software.
Trademarks
All trademarks and registered trademarks appearing in this book are the property of their own respective owners.
raywenderlich.com 2
Advanced Android App Architectures Advanced Android App Architecture
Dedications
"To my mom, the first software engineer I ever knew."
— Yun Cheng
"To my family and friends, for all the support that I got during the writing of this book."
— Aldo Olivares DomĂnguez
raywenderlich.com 3
Advanced Android App Architectures Advanced Android App Architecture
About the Authors
Yun Cheng is an author on this book. Yun is a software engineer for the Runkeeper app at ASICS Digital in Boston, MA. If she's not
running marathons or facilitating for the Girls Who Code club in
Cambridge, MA, you can probably find her setting off the kitchen fire alarm with her cooking. You can also reach out to her on Twitter at @yuncheng13.
Aldo Olivares DomĂnguez is an author of this book. Aldo is a
Software Engineer passionate about creating amazing apps with great user interfaces. He's been an Android Developer since 2012 working primarly as a Freelancer and Instructor. Twitter: @aldominio.
About the Editors
Nick Bonatsakis is a tech editor of this book. Nick is an accomplished software engineer with over a decade of experience in mobile
development across both Android and iOS. He is a passionate
technologist, musician, father and husband. He currently works as an independent consultant under his own company, Velocity Raptor Inc.
Matei Suica is a tech editor of this book. Matei is a software
developer that dreams about changing the world with his work. From his small office in Romania, Matei is trying to create an App that will help millions. When the laptop lid closes, he likes to go to the gym and read. You can find him on Twitter or LinkedIn: @mateisuica
Vijay Sharma is the final pass editor of this book. Vijay is a husband, a father and a senior mobile engineer. Based out of Canada's capital, Vijay has worked on dozens of apps for both Android and iOS. When not in front of his laptop, you can find him in front of a TV, behind a book, or chasing after his kids. You can reach out to him on Twitter and LinkedIn @vijaysharm.
raywenderlich.com 4
Advanced Android App Architectures Advanced Android App Architecture
Tammy Coron is an editor of this book. She is an independent
creative professional and the host of Roundabout: Creative Chaos. She’s also the founder of Just Write Code. Find out more at
tammycoron.com.
Manda Frederick is the managing editor of this book. She has been involved in publishing for over ten years through various creative, educational, medical and technical print and digital publications, and is thrilled to bring her experience to the raywenderlich.com family as Managing Editor. In her free time, you can find her at the climbing gym, backpacking in the backcountry, hanging with her dog, working on poems, playing guitar and exploring breweries.
About the Artist
Vicki Wenderlich is the designer and artist of the cover of this book. She is Ray’s wife and business partner. She is a digital artist who
creates illustrations, game art and a lot of other art or design work for the tutorials and books on raywenderlich.com. When she’s not
making art, she loves hiking, a good glass of wine and attempting to create the perfect cheese plate.
raywenderlich.com 5
Advanced Android App Architectures
Table of Contents: Overview What You Need....................................................................... 13 Book License ............................................................................ 14 Book Source Code & Forums............................................. 15 About the Cover ..................................................................... 16 Section I: Building a Foundation....................................... 17
Chapter 1: Introduction .............................................. 18 Chapter 2: Model View Controller ......................... 26 Chapter 3: Testing MVC.............................................. 33 Chapter 4: Android Architecture Components. 40 Chapter 5: Dependency Injection........................... 48 Chapter 6: RxJava.......................................................... 55
Section II: Fundamental UI Architectures.................... 66 Chapter 7: Model View Presenter Theory .......... 67 Chapter 8: Model View Presenter Sample .......... 74 Chapter 9: Testing MVP.............................................. 94 Chapter 10: Model-View-ViewModel Theory. 108 Chapter 11: MVVM Sample with data
binding............................................................................. 117 Chapter 12: MVVM Sample with Android Architecture Components....................................... 132
raywenderlich.com 6
Advanced Android App Architectures
Chapter 13: MVVM Testing.................................... 149 Section III: VIPER and MVI.............................................. 160 Chapter 14: VIPER Theory...................................... 161 Chapter 15: VIPER Sample...................................... 169 Chapter 16: Testing VIPER...................................... 190 Chapter 17: MVI Theory.......................................... 203 Chapter 18: MVI Sample.......................................... 214 Chapter 19: MVI Debugging................................... 236 Conclusion.............................................................................. 249
raywenderlich.com 7
Advanced Android App Architectures
Table of Contents: Extended What You Need . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Book License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Book Source Code & Forums. . . . . . . . . . . . . . . . . . . . . . . . 15 About the Cover. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Section I: Building a Foundation. . . . . . . . . . . . . . . . . . . . . 17 Chapter 1: Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
What is this book?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Why is app architecture important? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 Introducing the sample project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 WeWatch sample app walkthrough. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Chapter 2: Model View Controller . . . . . . . . . . . . . . . . . . 26 The Model-View-Controller pattern. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 Applying MVC to Android. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 WeWatch MVC code. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Chapter 3: Testing MVC. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 Android Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 Focusing on unit tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Unit testing the Movie class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 Unit testing an Android Activity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Why MVC makes unit testing hard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Chapter 4: Android Architecture Components . . . . . 40 Using the Android Architecture Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
raywenderlich.com 8
Advanced Android App Architectures
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Chapter 5: Dependency Injection . . . . . . . . . . . . . . . . . . . 48 What is a dependency?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Why dependencies can be problematic. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 Injecting dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 Dependency injection frameworks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 Chapter 6: RxJava . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 What is the Observer pattern?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Getting to know RxJava. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Observing events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Frequently Not Asked RxJava Questions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Section II: Fundamental UI Architectures. . . . . . . . . . . 66 Chapter 7: Model View Presenter Theory . . . . . . . . . . 67 The Model-View-Presenter pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 MVP advantages and concerns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Chapter 8: Model View Presenter Sample . . . . . . . . . . 74 Getting started. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Applying MVP to the Movies app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 The Main screen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 The Add Movie screen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 The Search Movie screen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
raywenderlich.com 9
Advanced Android App Architectures
Chapter 9: Testing MVP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Getting started. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Getting to know Mockito . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Testing the MainPresenter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Testing the AddMoviePresenter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 Testing the SearchPresenter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 Where to go from here? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
Chapter 10: Model-View-ViewModel Theory . . . . . 108 The Model-View-ViewModel pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 MVVM by example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 MVVM advantages and concerns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 Where to go from here? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
Chapter 11: MVVM Sample with data binding . . . . 117 What is data binding?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 Implementing data binding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 Challenge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 Where to go from here? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
Chapter 12: MVVM Sample with Android Architecture Components . . . . . . . . . . . . . . . . . . . . . . . . . 132 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 Current architecture layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 Creating a movie repository. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 Creating ViewModels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 Using LiveData with ViewModels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 MVVM architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 Where to go from here? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
raywenderlich.com 10
Advanced Android App Architectures
Chapter 13: MVVM Testing. . . . . . . . . . . . . . . . . . . . . . . . 149 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 Creating unit tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Section III: VIPER and MVI . . . . . . . . . . . . . . . . . . . . . . . . 160 Chapter 14: VIPER Theory . . . . . . . . . . . . . . . . . . . . . . . . 161 What is VIPER? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 VIPER Advantages and Concerns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 Questions you didn't think to ask. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 Where to go from here? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 Chapter 15: VIPER Sample . . . . . . . . . . . . . . . . . . . . . . . . 169 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 Implementing the Main Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 Implementing the AddMovie module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 Implementing SearchMovie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 Where to go from here? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 Chapter 16: Testing VIPER . . . . . . . . . . . . . . . . . . . . . . . . 190 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 Testing your Main presenter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 Testing the AddMovie presenter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 Testing SearchMovie Presenter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 Chapter 17: MVI Theory. . . . . . . . . . . . . . . . . . . . . . . . . . . 203 What is MVI? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 MVI Advantages and Concerns. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211 Frequently Not Asked MVI Questions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 Where to go from here? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 Chapter 18: MVI Sample. . . . . . . . . . . . . . . . . . . . . . . . . . . 214 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
raywenderlich.com 11
Advanced Android App Architectures
Going Reactive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 Creating Interactors and State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 Creating the Presenters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 Creating the Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 Final thoughts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234 Where to go from here? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
Chapter 19: MVI Debugging. . . . . . . . . . . . . . . . . . . . . . . 236 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236 Introducing Timber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 Testing the MVI architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240 Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248 Where to go from here? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
raywenderlich.com 12
WWhat You Need
To follow along with this book, you'll need the following:
• Android Studio 3.3.2, available at https://developer.android.com/studio/index.html. This is the environment in which you'll develop the apps in this book.
If you haven't installed the latest versions of Android Studio, be sure to do that before continuing on with the book.
Also, the sample app described in this book makes use of a third party API by the Movie DB to search and retrieve movie information. In order to use the search API, you must first get access to an API key from the Movie DB. To get your API own key, sign up for an account at www.themoviedb.org. Detailed steps will be given in the first chapter of the book.
raywenderlich.com 13
LBook License
By purchasing Advanced Android App Architecture, you have the following license:
• You are allowed to use and/or modify the source code in Advanced Android App Architecture in as many apps as you want, with no attribution required.
• You are allowed to use and/or modify all art, images and designs that are included in Advanced Android App Architecture in as many apps as you want, but must include this attribution line somewhere inside your app: “Artwork/images/designs: from Advanced Android App Architecture, available at www.raywenderlich.com.”
• The source code included in Advanced Android App Architecture is for your personal use only. You are NOT allowed to distribute or sell the source code in Advanced Android App Architecture without prior authorization.
• This book is for your personal use only. You are NOT allowed to sell this book without prior authorization, or distribute it to friends, coworkers or students; they would need to purchase their own copies.
All materials provided with this book are provided on an “as is” basis, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.
All trademarks and registered trademarks appearing in this guide are the properties of their respective owners.
raywenderlich.com 14
BBook Source Code &
Forums
This book comes with the source code for the starter and completed projects for each chapter. These resources are shipped with the digital edition you downloaded from store.raywenderlich.com.
We’ve also set up an official forum for the book at forums.raywenderlich.com. This is a great place to ask questions about the book or to submit any errors you may find.
raywenderlich.com 15
AAbout the Cover
Birds are, of course, perhaps most well known for their ability to build fantastic nests, and the satin bowerbird, which graces this cover, is no exception.
While many birds craft modest nests of sticks, mud and bits of softness collected here and there, the satin bowerbirds are much more ambitious with their structures. Perhaps one of nature's most creative and serious architects, these birds build "bowers" to attract a mate. They build and decorate with anything from berries to flowers to drinking straws to ballpoint pens. Interestingly, as they mature, they prefer to architect with objects of the color blue.
Like these birds, good engineers understand the importance of good architecture: It is ambitious, structurally sound, attractive and sets your work apart from other simple nesting developers.
You can learn more about these creative and intelligent birds, here: https:// en.wikipedia.org/wiki/Satin_bowerbird.
raywenderlich.com 16
Section I: Building a Foundation
This section introduces you to topics that will serve as a foundation for your understanding of the UI architecture patterns. You’ll get introduced to the sample project, an app named WeWatch, that allows users to keep track of movies to watch. You’ll also learn concepts that aid architecture, including Android Architecture Components and dependency injection.
• Chapter 1, Introduction: This chapter explains what this book is about and its intended audience. By reading this chapter, you’ll gain a better understanding of why apps need good architecture. You’ll also get an introduction to the sample app that you’ll be building throughout this book.
• Chapter 2, MVC: The sample project starts off written in the Model View Controller pattern, with the Android Activity serving as both the View and the Controller. In this chapter, you’ll learn the history of the MVC pattern as applied to Android, and you’ll learn why this UI architecture pattern fails to meet two primary standards for good code: separation of concerns and unit testability.
• Chapter 3, Testing MVC: Here, you’ll get a quick review on writing unit tests with JUnit, and you’ll learn why the MVC pattern results in poor unit testability of code.
• Chapter 4, Android Architecture Components: In this chapter, you’ll get an overview of the Android Architecture Components and go into detail on the libraries used in the sample project at various points in the book: Room, ViewModel, LiveData and Data Binding.
• Chapter 5, Dependency Injection: An important concept in writing testable code is using dependency injection to inject mock objects into code. Here, you’ll learn the theory behind dependency injection and create a practical sample project using Dagger 2, a popular dependency injection framework for both Java and Android.
• Chapter 6, RxJava: In this chapter, you’ll get an overview of RxJava and go into detail on how the library is used in the sample project at various points in the book.
raywenderlich.com 17
1Chapter 1: Introduction By Yun Cheng
Do you remember when you made your first “Hello World” app on Android? From there, you likely progressed to creating complex user interfaces to display data, made web calls to APIs and managed the persistence of data. As the Android apps you built became more complex, you might have wondered if there were coding best practices available to make your apps more extensible, maintainable and testable. Perhaps you even wondered how to architect your apps so they’re best suited to your particular needs.
Given that Google (until very recently) did not provide an opinion on app architecture, Android developers were left to come up with their own. Architecture patterns like MVC, MVP, MVVM, MVI and Viper are debated passionately among Android developers. So, what are these patterns and which one is the best?
The short answer to the latter question is that it depends on your particular app and its needs. With that in mind, this book aims to guide you to an informed decision by answering the former question in detail.
What is this book?
Throughout this book, you’ll work with one sample project named WeWatch. You’ll build this project multiple times using each of these architecture patterns. During this process, you’ll get a hands-on comparison of the patterns and gain a deeper understanding of the theory behind them.
raywenderlich.com 18
Advanced Android App Architectures Chapter 1: Introduction
Who is this book written for?
This book is for you if:
• You’re a developer who already has a basic understanding of creating Android apps in Kotlin.
• You want to take your apps to the next level with robust architecture. • You’re familiar with unit testing with JUnit and want to write unit tests for your app. How to use this book
It’s not necessary to read the chapters in this book in order. Feel free to jump to the architecture pattern that interests you the most. If there are concepts that are covered in another chapter, you’ll be directed to those chapters for more information.
For instance, the sample project uses the following Android Architecture Components at various points in the book: Room, LiveData, ViewModel and data binding, so you may want to read the Android Architecture Components chapter for more information. The project also makes use of RxJava in some chapters, so be sure to check out the RxJava chapter if you need a primer on that library.
Why is app architecture important?
The idea behind the app architecture patterns presented in this book is that they all exist to help you design your app in such a way that allows the app to be maintainable as it scales. Two concepts, in particular, are useful: separation of concerns and unit testing.
Firstly, separation of concerns deals with separating the components of your app by responsibility. For example, when you update the UI of your app with a fancy new design, you want to do so without having to change any of the other code, such as the underlying data.
As you add more features to your app, you want to do so without having to change too much of your existing code.
Finally, as your app grows, you want to be able to test the app to ensure you didn’t break the logic of existing features.
Now that you know the motivation behind app architecture, it’s time to get yourself acquainted with the sample project in the book.
raywenderlich.com 19
Advanced Android App Architectures Chapter 1: Introduction
Introducing the sample project
WeWatch, the app you’ll build in this book, keeps a list of movies you want to watch, allowing you to easily add and delete movies within the app. To add a movie, a user can manually enter the movie or search for movies from a database of movies provided by The Movie Database (www.themoviedb.org) API.
The Movie Database API key
Any app that wants access to The Movie Database API must provide an API key in the network call to identify itself to the API. You’ll need to obtain your own API key to work with the sample code within this book.
To obtain your API key, sign up for an account at www.themoviedb.org. Then, navigate to your account settings on the website, view your settings for the API and register for a developer API key.
After receiving the API key, open the starter project for this chapter and navigate to RetrofitClient.kt. There, you can replace the existing value for API_KEY with your new key.
WeWatch sample app walkthrough
From the project resources open the starter project for this chapter in Android Studio. Take a moment to familiarize yourself with the structure of the project. Build and run the app to see what you’re working with.
raywenderlich.com 20
Advanced Android App Architectures Chapter 1: Introduction
Main screen
The main screen displays the list of movies to watch. You’ll find the code for this screen in MainActivity.kt.
Main screen of sample app.
raywenderlich.com 21
Advanced Android App Architectures Chapter 1: Introduction
On this screen, you can select movies you want to watch; you can also delete movies you have already watched (or ones that received a terrible rating from rottentomatoes.com) from the list.
Select movies from list to delete.
raywenderlich.com 22
Advanced Android App Architectures Chapter 1: Introduction
Add movie screen
Clicking the floating action button from the main screen brings the user to the add movie screen where they can enter the title and release year of the movie. When they press the Add Movie button, the movie gets added to the to-watch list. You’ll find the code for this screen in AddMovieActivity.kt.
Add movie screen of sample app.
Alternatively, the user can enter the title of the movie and press the Search button to the right, which brings the user to the search movie screen.
raywenderlich.com 23
Advanced Android App Architectures Chapter 1: Introduction
Search movie screen
Search results screen of sample app.
For this screen, corresponding to SearchActivity.kt, a network call is made to the search endpoint of The Movie Database API, using the movie title passed in as the query. The resulting screen is a list of results matching the movie title. The user can then select a result to return to the add movie screen with the movie information pre populated.
raywenderlich.com 24
Advanced Android App Architectures Chapter 1: Introduction
Add movie screen of sample app with the result from the search.
Where to go from here?
Now that you know the motivation behind this book and had an introduction to the sample project, you’re ready to learn the theory behind the Model View Controller architecture. You’ll learn how the MVC pattern is ironically not really a pattern in Android at all. This is the default architecture that the sample project uses, at least for now!
raywenderlich.com 25
2Chapter 2: Model View
Controller
By Yun Cheng
In this chapter, you will learn about the Model-View-Controller pattern and be familiarized with the sample app that you will build throughout this book: WeWatch.
The Model-View-Controller pattern
The Model View Controller pattern is a pattern that separates components of a software system, based on responsibilities. In this pattern, three components with distinct responsibilities are defined as follows:
• Model is the data layer. This includes the data objects, database classes, and other business logic, concerned with storing the data, retrieving the data, updating the data, etc.
• View renders the data using the Model in a way suitable for user interface. It is what gets displayed to the user.
• Controller is the brains of the system. It encapsulates the logic of the system, and it controls both the Model and View. The user interacts with the system through this controller.
The general idea is that the Model should not be concerned with how it's ultimately displayed to the user. On the flip side, a View should not be concerned with the values of the actual data it is displaying, only that it needs to be displayed. The Controller then acts as the glue between these two components, orchestrating how the data should be displayed
raywenderlich.com 26
Advanced Android App Architectures Chapter 2: Model View Controller
Organizing a system this way allows for two advantages: separation of concerns and unit testability. Because components are isolated from each other, each focused on only one responsibility, the system is more flexible and modular. Each component is able to be unit tested on its own and could be swapped out for another version without affecting the other.
For example, the View could be changed out while the underlying data and logic remained the same.
When MVC was originally conceptualized, it was initially applied to desktop and web applications. A typical application of this pattern in the web realm would look like this:
Typical MVC flow in a web application.
• The user inputs are HTTP requests.
• The Controller handles those requests by updating the data in the Model. • The View returned to the user is a rendered HTML page, displaying the data.
raywenderlich.com 27
Advanced Android App Architectures Chapter 2: Model View Controller
Applying MVC to Android
Seeing that this pattern worked well in organizing other software systems, developers naturally tried to apply the MVC pattern to the development of Android apps as well when Android was introduced. Here is one way in which the MVC pattern was adapted for Android:
Attempt to apply MVC to Android.
In MVC, the main entry point to the app is through the Controller, so it makes sense to give the role of the Controller to the Android Activity where it can take in user inputs, such as a button click, and respond accordingly. The Model consists of the data objects,
which, in Android, are just regular Kotlin data classes, as well as the classes to handle data locally and remotely. The View consists of the layout files in an Android app.
However, there is a problem with this oversimplified adaptation of MVC to Android.
When the Controller updates the View, the View cannot update if it is merely a static .xml layout. Instead, the Activity must almost always contain some view logic, such as showing or hiding views, displaying a progress bar or updating the text on screen,
in response to user input. Furthermore, not all layouts are inflated through .xml; what if your Activity dynamically loaded your layouts?
raywenderlich.com 28
Advanced Android App Architectures Chapter 2: Model View Controller
If the Activity must hold references to views and logic for changing them as well as all the logic for its Controller responsibilities, then the Activity effectively serves as both the Controller and the View in this pattern.
More realistic application of MVC to Android.
Having the Activity act as both the Controller and the View is problematic for two reasons. First, it defeats the goal of MVC, which was to separate responsibilities among three distinct components of a software system. Second, having the Activity as the Controller in MVC poses a problem for unit testing, which you will read more about in the next chapter.
As you will discover, the sample app in this book starts out with precisely those problems described above.
WeWatch MVC code
Like many Android apps out there, WeWatch follows the standard Android architecture, which attempts to follow MVC architecture but falls short due to the nature of the Android framework and the role of the Android Activity. There ends up being a mix of both Controller logic and View logic in the app's Activities, as you'll see in the code for
raywenderlich.com 29
Advanced Android App Architectures Chapter 2: Model View Controller
this app. It will, however, serve as the starting point for the refactoring you will perform in future chapters to address those issues and improve the app.
Model
The Model for WeWatch consists of the following classes:
• The Movie class itself which you'll find in the Movie.kt file,
• The classes concerned with the local database are found in LocalDatabase.kt, LocalDataSource.kt, and MovieDao.kt.
• The classes concerned with remote data can be found in RemoteDataSource.kt and TmdbResponse.kt.
Open these files in the project and familiarize yourself with the classes and their workings.
Because the app gets its search results from The Movie Database, the Movie class must contain properties matching those of the movies found in the JSON object returned from the API. If you open Movie.kt, you'll see that this class contains far more properties than you'll ever need for this app, such as originalLanguage and genreIds; these are present in this class to be compatible with the movie JSON object returned from the API.
The actual object returned from The Movie Database is represented by TmdbResponse.kt, which includes properties such as number of results, and most importantly, the results themselves, which is a List.
Next, the app needs a local database to persist the user's list of movies. The app uses the Room Persistence Library, one of the Android Architecture Components. The singleton for the local database is created in LocalDatabase.kt. Because Room does not know how to store a List, a IntegerListTypeConverter is needed to perform the conversion from List to String or String to List and is listed as a TypeConverter at the top of LocalDatabase.kt. The data access object containing the SQL statements for this database is MovieDao.kt. Finally, Activities will interact with this Model component through the LocalDataSource, which exposes the insert, delete and update functions to the rest of the app.
Networking
The search screen in this app is powered by a call to The Movie Database (www.themoviedb.org) API's search endpoint. RetrofitClient and RetrofitInterface contain all the functions needed to make this network call succeed.
raywenderlich.com 30
Advanced Android App Architectures Chapter 2: Model View Controller
Main Screen
Open MainActivity.kt and MainAdapter.kt and familiarize yourself with the code for displaying the user's list of movies to watch. MainActivity.kt sets up the views and gets an instance of the LocalDataSource. Then it gets the list of movies from the LocalDataSource through an RxJava Observable and passes the movies to the MainAdapter, which displays the movies in the RecyclerView. There is also logic to handle button clicks for adding and deleting a movie.
Add movie screen
Open AddMovieActivity.kt. This class sets up the views and gets an instance of the LocalDataSource. There are click listeners for the Search button and the add movie button. When the search button is clicked, the SearchActivity is started. Upon returning from the SearchActivity, the views get updated, including loading an image of the movie poster. When the add movie button is clicked, the movie is inserted into the local database.
Search movie screen
Open SearchActivity.kt and SearchAdapter.kt. Much of the logic in these classes is similar to those on the main screen. The SearchActivity.kt sets up the views and gets an instance of the RemoteDataSource. Then it gets the list of search results from the RemoteDataSource through an RxJava Observable and passes the movies to the MainAdapter, which displays the movies in the RecyclerView. There is also logic for clicking on a movie and returning to the previous screen.
Key points
In this chapter you gained an overview of the Model View Controller pattern as a way of separating components by responsibility in systems with user interfaces. Unfortunately, it's not easy to apply MVC to Android due to the nature of the Activity class. As you saw in the sample app, when applying MVC to Android, you end up with huge Activity classes, containing both Controller logic and View logic. As you will learn in the next chapter, one of the main drawbacks of this design is lack of unit testability of your codebase.
More specifically:
• Model View Controller is a pattern originally used in desktop and web-based applications.
raywenderlich.com 31
Advanced Android App Architectures Chapter 2: Model View Controller
• MVC divides an app into three components with their own responsibilities. • The Model consists of the data and business logic.
• The View is responsible for rendering the data in a way that is human readable on a screen.
• The Controller is the brains behind the app and communicates with both the Model and the View. The user interacts with the app via the Controller.
• When applying MVC to Android, the Android Activity ends up serving as both the View and Controller, which is problematic for separation of concerns and unit testing.
raywenderlich.com 32
3Chapter 3: Testing MVC By Yun Cheng
Testing software is important to ensure your software's behavior and to catch your bugs before your user catches them. In this chapter, you will learn about testing on Android, understand why this book will focus on unit testing and examine why it is difficult to write unit tests for an MVC app.
Android Testing
Testing in software development is often underestimated and pushed to the later stages of a project, if even at all. Unfortunately, there are some developers, clients and managers who don’t believe that serious testing is important. The truth is, mobile apps are becoming larger and more complex. They’re used to chat with our friends, play video games, socialize, take pictures, schedule appointments and much more. As our apps and games grow in complexity, so does the code base behind them, making testing even more important.
Android Testing is typically divided into three different types: Small, medium and large. In this section, you’ll get an overview of each.
Small tests
Small tests are unit tests that run independently of an emulator or physical device. Small tests generally focus on a single component as all of its dependencies are tested beforehand and are mocked or stubbed with the desired behavior.
raywenderlich.com 33
Advanced Android App Architectures Chapter 3: Testing MVC
When compared against the other two test types, small tests are the fastest because they don't require an emulator or physical device to run. That said, they’re also low fidelity, which makes it difficult to have confidence that your app will function properly in the real world.
On Android, the most commonly used tools for these tests are JUnit and Mockito. Medium tests
Medium tests are integration tests that help you check how your code interacts with other parts of the Android Framework. Medium tests are typically run after you complete unit tests on your components. That's when you need to verify that things will behave properly together.
On Android, one of the most common tools to perform integration tests is Roboelectric. Unlike traditional emulator-based Android tests, Robolectric tests run inside a sandbox and don't need a device or emulator. However, it’s best to test your app on an emulated
device, a real device or using a service like Firebase Test Lab. Device farm services are great because you’ll be able to test against different combinations of screen sizes and hardware configurations. This can help you catch bugs specific to some device categories.
Large tests
Large tests are integration and UI tests that emulate user behavior and assert UI results. These tests are the slowest and most expensive because they require the emulator or a physical device.
On Android, the most commonly used tools for UI testing are Espresso and UI Automator.
Overall, Google recommends that you create tests of each category based on your app's use cases according to the following rule: 70 percent small, 20 percent medium, and 10 percent large. The Testing Pyramid (https://developer.android.com/training/ testing/fundamentals) illustrates this.
raywenderlich.com 34
Advanced Android App Architectures Chapter 3: Testing MVC
Focusing on unit tests
Although all three types of testing play a role in ensuring a quality app, this book concerns itself primarily with unit tests, for the following reasons:
1. It is much faster to run unit tests than it is to run UI or integration tests. UI and integration tests typically run on an emulator, and starting up your app on an emulator and simulating user input takes up significantly more time than just running unit tests on the local JVM.
2. Unit testing does not require any Android testing libraries. The idea, here, is that you are testing regular Java/Kotlin code, not code that is Android framework specific. As a result, using JUnit, the testing framework for Java, is all that is needed to write unit tests. If you wish to test code that uses Android-specific classes, you'll need a testing library specifically for Android. Libraries like Robolectric (robolectric.org) and Espresso (https://developer.android.com/training/testing/ espresso/) are popular options.
3. Unit tests make up the foundation of an app's test suite. You can imagine that individual units of code work behind the scenes to display what the user ultimately sees in the app. The idea behind unit tests is that, if you can test that all the
raywenderlich.com 35
Advanced Android App Architectures Chapter 3: Testing MVC
individual units of code work as expected, you can have high confidence that, added up together, the app as a whole also works as expected.
Unit testing the Movie class
First, you will write your first unit tests for the WeWatch app. In the starter project, open Movie.kt and take a look at the getReleaseYearFromDate() method:
fun getReleaseYearFromDate(): String? {
return releaseDate?.split("-")?.get(0)
}
This method exists because the format of dates returned by The Movie Database API is YYYY-MM-DD, but the app is only interested in displaying the year. It's easy to make errors when parsing dates, so it is worthwhile to test that this method returns expected values.
In the starter project, open the file MovieTests.kt and add the following test code: class MovieTest {
@Test
fun testGetReleaseYearFromStringFormmattedDate() {
//1
val movie = Movie(title = "Finding Nemo", releaseDate = "2003-05-30") assertEquals("2003", movie.getReleaseYearFromDate())
}
@Test
fun testGetReleaseYearFromYear() {
//2
val movie = Movie(title = "FindingNemo", releaseDate = "2003") assertEquals("2003", movie.getReleaseYearFromDate())
}
@Test
fun testGetReleaseYearFromDateEdgeCaseEmpty() {
//3
val movie = Movie(title = "FindingNemo", releaseDate = "") assertEquals("", movie.getReleaseYearFromDate())
}
@Test
fun testGetReleaseYearFromDateEdgeCaseNull() {
//4
val movie = Movie(title = "FindingNemo")
assertEquals("", movie.getReleaseYearFromDate())
}
}
raywenderlich.com 36
Advanced Android App Architectures Chapter 3: Testing MVC
Here are the four test cases tested in this suite:
1. Pass in a releaseDate of 2003-05-30. The expected output would be the year extracted from the date: 2003.
2. Pass in a releaseDate of 2003. Even though the input is not in the format YYYY-MM DD, the expected output would still be 2003.
3. Edge case: pass in an empty releaseDate of "". In this case, the most appropriate output for this input would be just an empty string.
4. Edge case: pass in a null releaseDate. In this case, the best output for this would also just be an empty string.
Run the tests. You should see that only three out of four tests pass! Test case #4, which tests a null input, fails. This is because the code for getReleaseYearFromDate() does not properly handle a null input. Instead of returning an optional String?, it is better to return a String, even if the String returned is just an empty one. Make the following fix in the Movie.kt file:
fun getReleaseYearFromDate(): String {
return releaseDate?.split("-")?.get(0) ?: ""
}
The addition of the Elvis operator ensures that if the releaseDate is null, an empty string is returned. Rerun the test with these changes, and you should see that it now passes.
Unit testing an Android Activity
As you just saw, unit testing the logic in data classes — like Movie.kt — that make up the Model is pretty straightforward using JUnit. You simply instantiate the object using the class' constructor, like with val movie = Movie(title = "Finding Nemo", releaseDate = "2003-05-30"). Then, you call a method on the object, like movie.getReleaseYearFromDate(), and assert that the result is what you expect.
But there is an entire rest of the app beyond data models still left to test! What about unit testing all the Android Activities in app?
Unfortunately, testing an Activity is tricky for two reasons:
1. Android lifecycle: An Activity is an Android framework-specific class that is bound by the Android lifecycle, which is controlled by the operating system. As such, you cannot instantiate an Activity: It has no constructor. Thus, you cannot just create an
raywenderlich.com 37
Advanced Android App Architectures Chapter 3: Testing MVC
object out of that Activity and call methods on it. For example, if you wanted to test the behavior of displayMovies(movieList: List?) in MainActivity.kt given different inputs, you cannot just create an instance of that Activity like val mainActivity = MainActivity(), nor can you call methods on that instance like mainActivity.displayMovies(ArrayList()).
2. Android dependencies. A typical Android Activity will contain other Android framework-specific classes in it, such as Context, RecyclerView, Toast, etc. As you will learn in Chapter 5: "Dependency Injection", these dependencies will need to be mocked if you want to test code in isolation. It is easy enough to mock your own classes, but not so easy to mock classes that belong to the Android framework.
Again, there are libraries like Robolectric that can address the above two challenges, but these will run much slower than regular JUnit tests.
Why MVC makes unit testing hard
Recall from the previous chapter that, while the Model View Controller pattern proved useful for other software systems, it didn't quite work for Android apps. Yes, you managed to separate the Movie model in a way that allowed it to be tested, however, the Android Activity ended up serving as both the Controller and the View. The downside with that, as you just learned, is that Activities are not easily tested with unit tests. The result is that all the Controller logic is trapped inside the Activity, and it becomes untestable.
Clearly, the MVC pattern in Android is inadequate for not just separation of concerns but also for unit testing.
Key points
• There are three categories of testing in Android: unit tests, integration tests and UI tests.
• Unit tests are fast to run and don't require external Android test frameworks.
• If you can achieve high unit test coverage of your app, you can be confident that the app as a whole works as expected, because ultimately the app is made up of all those small units all working correctly.
• Unit testing a regular data class using JUnit is easy: Simply create an instance of that class, call a method on it and assert that the output is expected.
raywenderlich.com 38
Advanced Android App Architectures Chapter 3: Testing MVC
• Unit testing an Android Activity is difficult due to the Android lifecycle and the many Android dependencies in an Activity.
• There are external libraries like Robolectric and Espresso that can test classes containing Android framework-specific components, but these are significantly slower to run than unit tests.
• The MVC pattern is not conducive to unit testing because all the Controller logic is stuck inside an Android Activity unable to be tested. (The Model, however, can be tested.)
Where to go from here?
It’s impossible to cover all of the aspects of Android Testing in a single chapter. If you want to learn the ins and outs of Android Testing, check out the following links:
• Test Driven Development by Victoria Gonda: www.raywenderlich.com/7109-test driven-development-tutorial-for-android-getting-started
• Android Unit Testing with Mockito by Fernando Sproviero: www.raywenderlich.com/ 195-android-unit-testing-with-mockito
• UI Testing codelab by the Google developers team: codelabs.developers.google.com/ codelabs/android-testing/index.html?index=..%2F..%2Findex#6
In this chapter, you gained an overview of unit testing and saw how a major drawback of the MVC pattern is the lack of unit testability due to the nature of the Android Activity. In future chapters, you will learn about other patterns that address the problems of unit testing logic in Activities — by moving that logic out of the Activities.
raywenderlich.com 39
4Chapter 4: Android
Architecture Components By Yun Cheng
For many years, Android developers have had to handle database mappings, lifecycle management issues, asynchronous operations and other tricky challenges that were prone to errors and resulted in boilerplate code. In November 2017, Google introduced the Android Architecture Components, a collection of libraries to help with some of these architecture-related pain points. This book uses several of them in the sample project at various points: Room, LiveData, ViewModel and Data binding.
This chapter provides an overview of these libraries and goes into detail about using a subset of these in the sample project. While there’s no sample project for this chapter, you’ll view the sample projects from other chapters to see these libraries in action. For the Android Architecture Components that aren’t covered in the sample project, links to free tutorials are provided where you can learn more.
Room
The Room library handles persistence for your app. Room provides an abstraction layer over SQLite and makes it easy to directly map objects to raw database content, as well as easily define type-safe queries for interacting with data. This is achieved through the use of annotations that generate boilerplate code behind the scenes for you.
Lifecycle
The set of classes in this library offer lifecycle management, allowing you to create components that can automatically adjust their behavior based on the current Android lifecycle state.
raywenderlich.com 40
Advanced Android App Architectures Chapter 4: Android Architecture Components
To see an example using this library, you can read this tutorial:
www.raywenderlich.com/164-android-architecture-components-getting-started. ViewModel
A ViewModel provides data between a repository and your View. Unlike Activities, which get destroyed on configuration changes, ViewModels survive configuration changes, keeping your data safe and preserving state.
LiveData
If you’ve used RxJava before, LiveData is a similar library (with scaled down features) that allows Android Views to observe model changes and respond accordingly. LiveData is also lifecycle-aware, so it only tells the UI to update things if it’s in the correct lifecycle state. It also gets cleaned up for you, unlike RxJava subscriptions, which you must dispose of yourself.
Data binding
The data binding library allows you to observe changes in the ViewModel directly within the View XML. This save you from having to write the boilerplate code that’s normally required to bind data to Views in code.
Paging
The Paging library helps you to display lists (especially infinite ones) by loading data in small chunks and updating the UI as more data is loaded.
To learn more about this topic, read this tutorial: www.raywenderlich.com/6948- paging-library-for-android-with-kotlin-creating-infinite-lists.
Navigation
The Navigation architecture component allows you to specify navigation throughout your app using an XML graph or via a graph editor that’s built into the latest version of Android Studio. It reduces the hassle of handling it all yourself through boilerplate code.
This tutorial will help get you started using this library: www.raywenderlich.com/6014- the-navigation-architecture-component-tutorial-getting-started.
raywenderlich.com 41
Advanced Android App Architectures Chapter 4: Android Architecture Components
WorkManager
The WorkManager library manages deferrable background tasks that require guaranteed execution.
Read this tutorial for more information: www.raywenderlich.com/6040-workmanager tutorial-for-android-getting-started.
Using the Android Architecture Components
Phew! That was a long list of components. Tying them together can be a daunting task, but each one has a very important role to play when talking architecture. In this section, you'll go over how the WeWatch app makes use of some of these components.
Room
Throughout this book, the sample project uses the Room library for its database operations.
Open the starter project from the MVC chapter 2 and review the code to see how it’s implemented. Specifically, open and review the moving entity in Movie.kt, the data access object in MovieDao.kt, the database class found in LocalDatabase.kt and the data source class found in LocalDataSource.kt.
In WeWatch, the main data object of interest is Movie, and the app persists a user’s list of movies by storing each movie as a row in a database table. With Room, it’s easy to create a table named movie_table representing the Movie class in the database.
For any class whose fields you wish to directly map in a database table, use the @Entity annotation above the declaration for the class. By default, this creates a table with the same name as the class. You can also give the table a different name by explicitly setting the tableName, as is done in Movie.kt:
@Entity(tableName = "movie_table")
data class Movie(...) {...}
By default, the columns for this table have the same names as the fields in the constructor for this class, such as id, title and releaseDate. If you want a column to have a different name from the field name in the class, you can use the @ColumnInfo annotation before a field.
raywenderlich.com 42
Advanced Android App Architectures Chapter 4: Android Architecture Components
To set the primary key for a table, use the @PrimaryKey annotation. In the case of the movie table, the primary key is id:
@PrimaryKey
@SerializedName("id")
@Expose
var id: Int? = null
Note: The @SerializedName and @Expose annotations are from the Gson library, not from Room. The app uses Gson to convert JSON data from API web calls to Kotlin data objects. Thus, this single Movie class can fluidly convert between JSON to Kotlin object to Room database entity all through the use of annotations.
Next are the operations on this table, such as retrieval, insertion and deletion. The app accesses these operations through data access objects (DAO). Generally, each table has its own DAO interface, specified using the @Dao annotation.
Open MovieDao.kt, the DAO for the movie table, and look at the following CRUD (Create, Read, Update, Delete) operations it has:
@Dao
interface MovieDao {
@get:Query("SELECT * FROM movie_table")
val all: Observable>
@Insert(onConflict = REPLACE)
fun insert(movie: Movie)
@Query("DELETE FROM movie_table WHERE id = :id")
fun delete(id: Int?)
@Query("DELETE FROM movie_table")
fun deleteAll()
@Update
fun update(movie: Movie)
}
Each type of operation for a function is labeled with its own annotation: @Insert, @Query and @Update, with the corresponding SQLite query included to modify the data in the database.
Next, you need to declare the Room database object itself. Open LocalDatabase.kt and examine how this is done:
@Database(entities = [Movie::class], version = 1)
@TypeConverters(IntegerListTypeConverter::class)
abstract class LocalDatabase : RoomDatabase() {...}
raywenderlich.com 43
Advanced Android App Architectures Chapter 4: Android Architecture Components
LocalDatabase is abstract and inherits from RoomDatabase. It’s marked with a Room @Database annotation that lists the Entities that are in this database (corresponding to the tables in this database) and the version number.
The only entity in the WeWatch database is the Movie entity, so that’s the one that’s listed. The version number for the database is a number that you need to increment for database migrations if the schema of the database changes — but don’t worry about that right now.
The LocalDatabase.kt class then instantiates the Room database, like so:
LocalDatabase.INSTANCE =
Room.databaseBuilder(application, LocalDatabase::class.java, LocalDatabase.DB_NAME)
.allowMainThreadQueries()
.build()
A singleton pattern is used to create the Room database only once in the app.
The instantiation of the database occurs in LocalDataSource.kt. Open this file, and you’ll see that this is where the single instance of the database resides:
val db = LocalDatabase.getInstance(application)
LocalDataSource is the source of data for the app, providing the list of movies from the database to the rest of the app. LocalDataSource also acts as the point of contact for the rest of the app to expose the DAO’s other database calls. Any time a database call from the DAO is needed somewhere in the app, the calling Activity obtains an instance of LocalDataSource and calls insert(), delete() and update().
LiveData
In some chapters of this book, you’ll use the RxJava library to perform asynchronous web calls and database operations on the Schedulers.io() thread and to receive responses from them on the MainThread. In the MVVM chapters, an alternative to RxJava is presented: LiveData.
LiveData is a lifecycle-aware component that wraps around objects you wish to emit in a reactive way, much the same way the Observable object does in RxJava. LiveData is “live” in the sense that when the underlying data updates, anything observing that data will also receive the updates. For example, when paired with Room, LiveData retrieved from the Room database automatically updates when the data in the database changes.
raywenderlich.com 44
Advanced Android App Architectures Chapter 4: Android Architecture Components
In the MVVM with Android Architecture Components chapter project, the MovieDao.kt class returns a list of movies from the database as LiveData, like so:
@Query("select * from movie")
fun getAll(): LiveData>
To learn more about LiveData, read chapter 11: MVVM with Android Architecture Components.
ViewModel
One problem with Activities and the Android lifecycle is that if the Activity gets destroyed, data held by that Activity also gets destroyed. So, how can you return the Activity to the state it was in once it is rebuilt? It’s possible to save some state through the Activity’s onSaveInstanceState(outState: Bundle?), but this solution is cumbersome and doesn’t work for more complicated data.
Android Architecture Components now offer a more robust way for data to survive configuration changes via the ViewModel. A ViewModel is a lifecycle-aware class used to hold onto the LiveData so that it doesn’t get destroyed on configuration changes like screen rotation. The ViewModel keeps its state throughout the Activity’s lifecycle, but it’s essential to avoid any references to Views or Activities within a ViewModel because these throw a nullPoinerException when destroyed.
To see an example of how a ViewModel works, open MainViewModel.kt from the project for the MVVM with Android Architecture Components chapter.
class MainViewModel(private val repository: MovieRepository = MovieRepositoryImpl()): ViewModel() {}
Here, MainViewModel extends ViewModel and takes in a MovieRepository in its constructor.
Next, check out how the ViewModel holds onto a list of movies as LiveData: private val allMovies = MediatorLiveData>()
init {
getAllMovies()
}
fun getSavedMovies() = allMovies
fun getAllMovies() {
allMovies.addSource(repository.getSavedMovies()) { movies -> allMovies.postValue(movies)
}
}
raywenderlich.com 45
Advanced Android App Architectures Chapter 4: Android Architecture Components
The allMovies list, in this case, is of type MediatorLiveData, which is a subclass of regular LiveData that allows you to mutate the LiveData and react to updates on the LiveData’s value via an onChanged() event.
The app can then access allMovies in any Activity, such as in MainActivity.kt, like so:
override fun onCreate(savedInstanceState: Bundle?) {
...
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
viewModel.getSavedMovies().observe(this, Observer { movies -> movies?.let {
adapter.setMovies(movies)
}
})
}
Here, the ViewModel is instantiated by using a ViewModelProvider to provide the ViewModel. The Activity can then access the list of movies by calling viewModel.getSavedMovies() and observing the LiveData returned from that call. In this example, the list of movies is set on the Adapter when observed.
Be sure to read Chapter 11: MVVM with Android Architecture Components to see the code in full.
Data binding
The Data Binding library allows you to bind UI components in your Layouts to data sources. Normally, this is done in code — such as setting the text of a TextView using movieTitleTextView.setText = movie.title — but with the Data Binding library, you’ll be able to do it directly in the XML Layout files.
For example, open the project from the chapter MVVM with Data Binding and look at this snippet of item_movie_main.xml:
You can read more about data binding in chapter 10: MVVM with Data Binding chapter. raywenderlich.com 46
Advanced Android App Architectures Chapter 4: Android Architecture Components
Key points
• Android Architecture Components are a set of libraries to help with various challenges in dealing with Android architecture.
• Room handles database persistence.
• Lifecycle helps you create components that are aware of the current Android lifecycle state.
• ViewModel holds data and survives configuration changes.
• LiveData provides observable data to views.
• Data binding allows Android Views to observe changes to data in XML. • Paging handles displaying infinite lists.
• Navigation handles complex navigation.
• WorkManager manages background tasks.
Where to go from here?
Having a good understanding of the Android Architecture Components will be useful as you navigate the various chapters that use them. Next up, you’ll learn about another useful concept that’s present throughout the book: dependency injection.
raywenderlich.com 47
5Chapter 5: Dependency
Injection
By Yun Cheng
In this chapter, you’ll learn about dependencies, why they can be problematic and how you can remove them from your code using dependency injection, either manually or by using a dependency injection framework.
The truth is, you may already be using dependency injection, but you don’t recognize it as such because you’re unfamiliar with the terminology. By gaining a deeper understanding of dependency injection, you'll better deco`uple object dependencies, and as a whole, become a better programmer! Specifically in this book, you'll see the authors make an effort to use dependency injection in MVP, MVVM and MVI architecture.
What is a dependency?
A dependency occurs when an object from one class requires an object from another class in order to function properly. These dependencies are often member variables of the class. To give you a better sense of how a dependency might work, consider the following example: A movie theatre requires at least a screen, a projector and a movie, otherwise it won’t function.
In this chapter, you’ll create reusable, scalable code to support this type of dependency scenario.
Open the MovieTheatreExample starter project from the book resources in Android Studio and review MovieTheatre.kt:
class MovieTheatre {
var screen: Screen
var projector: Projector
raywenderlich.com 48
Advanced Android App Architectures Chapter 5: Dependency Injection
var movie: Movie
init {
screen = Screen()
projector = Projector()
movie = Movie()
}
fun playMovie() {
System.out.println("Playing movie now")
}
}
In this initializer block, MovieTheatre instantiates its dependencies, which includes a Screen, a Projector and a Movie. In other words, MovieTheatre depends on these objects to function.
While the instantiation of dependencies in the init may seem fine at first glance, there are some problems with this approach.
Why dependencies can be problematic
When you instantiate a class’s dependencies in its init, you create a situation where the class is tightly coupled with its dependencies. In this case, MovieTheatre is tightly coupled with its dependencies — Screen, Projector and Movie — so every time you create a new MovieTheatre instance, it needs to create a Screen, a Projector and a Movie.
The trouble is, you've given the responsibility of both creating and using these objects. As a general rule, when creating a class you should separate the creation of an object from the usage of an object.
Consider the following scenario: You have two movie theaters, one with a projector that uses 8mm film, and one with a projector that uses 16mm film. If you were to create two instances of MovieTheatre, each with its own Projector, you’d have no way to specify the type of film because the current init doesn't allow for that kind of flexibility.
To ensure reusability of the MovieTheatre class, it’s better to create two instances of Projector elsewhere and pass them into their respective MovieTheatre instance. In either case, MovieTheatre takes whatever Projector it’s handed, and then does what it needs to do to show the movie, regardless of the type of film.
Another reason to avoid instantiating dependencies in an object’s initializer block is unit testing. When unit testing MovieTheatre, you’re only interested in testing the behavior of that class, not any of its dependencies. For example, a Projector may have
raywenderlich.com 49
Advanced Android App Architectures Chapter 5: Dependency Injection
complicated inner workings involved in displaying a movie. Assuming you already performed unit testing on Projector, you don’t need to test it as part of MovieTheatre’s unit tests. If, however, you instantiate Projector in MovieTheatre’s initializer block, you’ll effectively be testing the dependency behavior along with the behavior of the object of interest.
To avoid testing dependencies in the MovieTheatre unit tests, you’ll make mock objects out of your dependencies. When testing the MovieTheatre code, you pass in these mock objects from the outside, so when the tests execute, MovieTheatre will call the methods on the mock object rather than a real object.
Injecting dependencies
It’s important to pass dependencies from an external source rather than creating them within a class; this process is known as dependency injection. The most common way to inject dependencies is through the constructor.
Rewrite the MovieTheatre class using constructor dependency injection:
class MovieTheatre(val screen: Screen, val projector: Projector, val movie: Movie) {
fun playMovie() {
System.out.println("Playing movie now")
}
}
Now, MovieTheatre is no longer responsible for creating its own dependencies; instead it receives its dependencies as parameters when it’s constructed.
MovieTheatre is instantiated in MainActivity, so open MainActivity.kt and pass in the dependencies that MovieTheatre is expecting:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//1 Instantiate dependencies
val screen = Screen()
val projector = Projector()
val movie = Movie()
//2 Instantiate MovieTheatre, passing in dependencies
val movieTheatre = MovieTheatre(screen, projector, movie) //3 Call methods on the MovieTheatre
raywenderlich.com 50
Advanced Android App Architectures Chapter 5: Dependency Injection
movieTheatre.playMovie()
}
}
Run the project and confirm that logcat displays the “Playing movie now” message.
Injecting dependencies through the constructor is fine for simple apps like the WeWatch sample app. A more complicated app, however, may have a web of dependencies.
Suppose Screen has its own dependencies like a Curtain and Backdrop, while Projector has its own dependencies like a Lens, PowerCord and Reel. Each of these dependencies need to be instantiated and passed into the respective class (Screen or Projector) upon creation of that class, and then the Screen, Projector and Movie need to be passed in to create a MovieTheatre object. The result is a lot of boilerplate code.
If it takes too many lines of code before you can instantiate your main class, it’s a sign that a dependency injection framework may help.
Dependency injection frameworks
Because the WeWatch example app is relatively simple, there’s no need to use an external framework for dependency injection, which can sometimes make it difficult to understand what’s happening. However, if an app has overly complicated dependencies, consider using a third party framework like Koin, Proton, Feather, Tiger, Lightsaber, Transfuse or Dagger 2.
In the following simplified example, you’ll briefly explore Dagger 2 to generate the dependency injection boilerplate code for you through the use of annotations. The two main annotations you’ll use are @Inject and @Component.
• @Inject: This annotation marks which dependencies to inject. You can use it on a constructor, field or method.
• @Component: This annotation is used on an interface class from which Dagger will then generate a new class that contain methods that return objects with their dependencies injected.
Open the app’s build.gradle file and set up the project. Add this plugin to the top of the file:
apply plugin: 'kotlin-kapt'
raywenderlich.com 51
Advanced Android App Architectures Chapter 5: Dependency Injection
This directive applies kapt, which is the Kotlin annotation processor. It allows you to use the Dagger annotations in your Kotlin classes.
Next, add these dependencies:
//Dagger dependencies
implementation 'com.google.dagger:dagger:2.15'
kapt 'com.google.dagger:dagger-compiler:2.15'
kapt 'com.google.dagger:dagger-android-processor:2.15'
Now, open Projector.kt and add the @Inject annotation to the constructor to expose this class as a dependency to Dagger:
class Projector @Inject constructor()
Then, open Screen.kt and make the same change:
class Screen @Inject constructor()
Next, open Movie.kt and make this change:
class Movie @Inject constructor()
Finally, open MovieTheatre.kt and add the @Inject annotation:
class MovieTheatre @Inject constructor(val screen: Screen, val projector: Projector, val movie: Movie) {
...
}
Create a new file, MovieTheatreComponent.kt, and add the following to it:
@Component
interface MovieTheatreComponent {
fun getMovieTheatre() : MovieTheatre
}
In this interface, there’s a single method, getMovieTheatre(), which returns an instance of MovieTheatre. Note that the interface is also annotated with @Component.
Rebuild the project. Dagger will generate a class for you named
DaggerMovieTheatreComponent with all of the dependency injection code added using the @Inject annotations as its guide.
When you call DaggerMovieTheatreComponent.create().getMovieTheatre() now, you’ll get an instance of MovieTheatre with all of the work of injecting its dependencies done for you.
raywenderlich.com 52
Advanced Android App Architectures Chapter 5: Dependency Injection
Open MainActivity.kt and modify the code in onCreate:
//Get the MovieTheatre from the DaggerMovieTheatreComponent val movieTheatre = DaggerMovieTheatreComponent.create().getMovieTheatre()
//Call methods on the MovieTheatre
movieTheatre.playMovie()
The lines of code needed to instantiate MovieTheatre is now replaced with a call to get the instance from DaggerMovieTheatreComponent instead. Run the project and verify that logcat displays the message “Playing movie now”.
Note that the simplified example above demonstrates only the basic features of Dagger 2 and leaves out many other annotations available in the framework.
If you'd like to learn more about Dagger 2, check out the official documentation google.github.io/dagger. You can also checkout a deep dive into dependency injection and Dagger 2 on the site www.raywenderlich.com/262-dependency-injection-in android-with-dagger-2-and-kotlin. If you're interested in using the pure Kotin library, Koin, check out the this tutorial www.raywenderlich.com/9457-dependency-injection with-koin on the site.
Key points
• A dependency of class A is any class B that is used by A.
• Generally, a class should not be responsible for both creating and using its dependencies.
• Rather than have a class create its own dependencies, you should create those dependencies outside and pass them into the class via its constructor.
• Dependency injection is a pattern where dependencies are passed into a class from an external entity.
• Injecting dependencies into a class allows for greater reusability of that class.
• Dependency injection is especially important for unit testing a class, as passing in dependencies through a class’s constructor allows for mock objects to be passed into that class during unit tests.
• For more complex dependencies in a project, you can use an external dependency injection framework, such as Dagger 2, to generate boilerplate dependency injection code.
raywenderlich.com 53
Advanced Android App Architectures Chapter 5: Dependency Injection
Where to go from here?
In this chapter, you learned the importance of using dependency injection to improve reusability and unit testing.
Although the movie theatre example used in this chapter was for demonstration purposes only, keep the concept of dependency injection in mind in the upcoming chapters. You’ll make use of it in the WeWatch sample project when you apply various architectures to this project.
raywenderlich.com 54
6Chapter 6: RxJava
By Aldo Olivares DomĂnguez
In this chapter, you won’t be writing any code. Instead, you’ll learn the theory behind one of the most popular open source libraries for Java: RxJava.
You may be wondering: Why learn about RxJava if this is a book written in Kotlin?
Even though RxJava is a Java library, it’ll work with your Kotlin code. This is because Kotlin was designed with Java interoperability built in.
You might also be wondering: I thought this was a book about architecture patterns!
True, this is a book about architecture patterns for Android, but RxJava makes use of important elements that are present amongst most of the architecture patterns you’ll see in this book, such as Observables and Reactive Programming. Also, RxJava is popular, and a lot of source code that you’ll encounter online use RxJava. So, it’s important that you understand how this library works, at least at a high level.
What is the Observer pattern?
Before you dive into the RxJava world, it’s critical that you understand the Observer pattern. When most Android developers hear the word pattern, they immediately think about MVC, MVP and MVVM. In reality, those are not technically patterns; they’re compound patterns.
raywenderlich.com 55
Advanced Android App Architectures Chapter 6: RxJava
What is a compound pattern?
A compound pattern is a pattern made up of multiple patterns. In other words, a pattern of patterns.
MVC, for example, relies on the Strategy pattern to configure the interaction between the Views and the Controller. The controller provides a strategy for the View, and the View is only concerned about displaying the UI. The View also relies on the Composite pattern under the hood to manage all the UI widgets that you see on the screen, such as buttons or lists. The models rely on the Observer pattern to make sure the controllers and Views have the latest data updates.
How does the Observer pattern work?
The Observer pattern helps your objects know when something happens — such as a database update or a network response — so they can react accordingly.
To understand the Observer pattern, imagine how a subscription to your favorite YouTube channel works:
• A content creator publishes a new YouTube video every other day.
• You like the channel's content, so you subscribe to the channel for new video notifications.
• As soon as a new video is published, you get a notification. You’ll continue to receive notifications as long as you’re a subscriber. But that doesn’t mean you have to watch the video.
• If you unsubscribe, you no longer receive notifications from that channel.
• As long as the user keeps posting videos, all subscribers to the channel will get a notification when a new video is posted.
The Observer pattern works similarly to the YouTube channel subscription described above. The analogy is that the YouTube channel is the Observable and the subscribers as the Observers. When the state of the Observable changes, all of the Observers who subscribed to it get a notification about the update, creating a one-to-many relationship between them.
Now, suppose you want to display a list of items as soon as the user presses a button in your app. You can define your button as the Observable and your list as the Observer. As soon as the button changes its state, it emits an event to the Observers. In this case, the list is automatically updated to display a list of items.
raywenderlich.com 56
Advanced Android App Architectures Chapter 6: RxJava
In Android, there are many ways to implement the Observer pattern in your apps, one of which is RxJava.
Getting to know RxJava
RxJava is an open-source library for Java and Android that helps you create reactive code. It offers the possibility to implement the Observer pattern for Observable/ Observer callbacks and gives you a range of operators that allow you to handle asynchronous and event-based programs.
Programming reactively
RxJava helps you create reactive code. In imperative programming, you often evaluate an expression linearly or line-by-line. If you want to calculate the area of a rectangle, your code would look like this:
var width = 2
var height = 3
var area = width * height // z is 6
In this code example, the area is calculated once at runtime and never changes.
Reactive programming is a different paradigm in which your code dynamically reacts to changes. The changes can be almost anything you want, such as value changes or state changes.
The truth is, you might have done a bit of reactive programming in the past without even noticing it. Every time you add an onClickListener to one of your buttons, for example, you did something like this:
button.setOnClickListener {
//Some code
}
In this case, you’re reacting to a change in the button state. Once the state changes, you can react to it by updating a list, displaying a notification or adding any action to it.
Observing events
RxJava implements the Observer pattern via two main interfaces: Observable and Observer. One of the most important methods in the Observable interface is subscribe().
raywenderlich.com 57
Advanced Android App Architectures Chapter 6: RxJava
The Observer interface, on the other hand, has three methods that the Observable calls when it changes the appropriate state:
• onNext(T value): This gets called by the Observable when it emits a new item of type T to the Observer.
• onComplete(): This notifies all of the Observers that the Observable is done with its task.
• onError(Throwable e): This notifies the Observer that the Observable has experienced an error.
As a rule of thumb, every Observable may emit one or more items that can be followed by a completion or an error.
The diagram above represents an event emitted by an Observable. The green dot represents an item that was emitted by the Observable (such as a new video notification), and the vertical black line represents a completion or an error.
However, there can be continuous events with no completion or errors. Here’s how an event caused by a mouse Observable looks:
As you can see, there are many events on this diagram that can represent mouse movements: right-clicks, left-clicks, middle-button clicks and much more.
raywenderlich.com 58
Advanced Android App Architectures Chapter 6: RxJava
But keep in mind: Events can’t be emitted after an Observable completes its tasks. Here’s an example of an Observable that’s not following the appropriate flow:
This flow violates the Observable contract by emitting an event after signaling completion.
Creating an Observable
There are many libraries in Java that help you create an Observable from almost any type of event. But sometimes it’s better to create your own. You can create an Observable using Observable.create(). Here’s its signature:
Observable create(ObservableOnSubscribe source)
That’s nice and concise, but what does it mean? What is the source? To understand that signature, you need to know what about ObservableOnSubscribe.
ObservableOnSubscribe an interface, with this contract:
public interface ObservableOnSubscribe {
void subscribe(ObservableEmitter e) throws Exception;
}
RxJava’s Emitter interface is similar to the Observer:
public interface Emitter {
void onNext(T value);
void onError(Throwable error);
void onComplete();
}
An ObservableEmitter also provides a means to cancel the subscription.
The best way to understand Observables is with simple examples that illustrate the entire process. Once you understand the basics, the complicated stuff becomes a little easier. It’s kind of like solving a jigsaw puzzle: Once you have the corners, everything else fits into place.
raywenderlich.com 59
Advanced Android App Architectures Chapter 6: RxJava
To create a simple method that returns an Observable, you can use this code:
//1
fun createYoutuber(): Observable{
//2
return Observable.create{emitter ->
//3
emitter.onNext("How to breed llamas")
}
}
The Observable emits a string (here, new video title) for its subscribers. Here’s a closer look:
1. Declare a method that returns an Observable of type String. Since Observables are generic, you can create an Observable for almost anything you want such as Strings, Doubles or Network Responses.
2. Use create() to create a new Observable.
3. Make the Observable (The Youtuber) emit the new video title to its Observers (The subscribers).
The following example declares a method that creates a new Observer object:
//1
private fun createSubscriber(): Observer {
//2
return object : Observer {
//3
override fun onSubscribe(d: Disposable) {
Log.d(TAG, " Im subscribed")
}
//4
override fun onNext(value: String) {
Log.d(TAG, "New Video : $value")
}
//5
override fun onError(e: Throwable) {
//Define onError() Action
}
//6
override fun onComplete() {
//Define onComplete() Action
}
}
}
Taking each commented section in turn
1. This method returns an Observer of type String. Just like the Observables, the Observers can observe any type of values.
raywenderlich.com 60
Advanced Android App Architectures Chapter 6: RxJava
2. Create a new Observer of type String.
3. onSubscribe() declares the action to take when your Observer is attached to the Observable.
4. onNext() declares the action to take when the emitter emits a new value. The parameter represents the value emitted by your Observable.
5. onError() declares the action to take when the Observable emits an error. 6. onComplete() declares the action to take when the Observable completes its tasks.
With these methods, you can easily create a new Observable and subscribe an Observer, like so:
createYoutuber().subscribe(createSubscriber())
When you execute that code, you’ll get these logs in the console:
D/MainActivity: Im subscribed
D/MainActivity: New Video : How to breed llamas
If you want to emit some values to your Observers, there’s an easier way to create your Observables using just().
Here’s how you can rewrite createYoutuber() to create an Observable that emits a single value to its observers:
fun createYoutuber(): Observable{
return Observable.just("How to breed Llamas")
}
The resulting log is the same:
D/MainActivity: Im subscribed
D/MainActivity: New Video : How to breed llamas
Asynchronous tasks
One common misconception about RxJava is that the tasks executed with this library are executed on a background thread. By default, RxJava does all of the tasks in the same thread from which it was called. For an Android app, this means that an Observable will usually emit all its data using the UI thread unless told otherwise.
raywenderlich.com 61
Advanced Android App Architectures Chapter 6: RxJava
You can, however, execute code on a background thread using observeOn() and subscribeOn(). observeOn() subscribes Observers to their Observable on the specified scheduler. subscribeOn() modifies the Observable to emit its events and notifications on the specified scheduler.
Suppose you want the Observers from previous examples to subscribe to their Observable on a background thread, but you still want to emit notifications on the main thread. This is how you can rewrite the subscribe/observe model to do it:
createYoutuber()
// Subscribe on a background thread
.subscribeOn(Schedulers.io())
// Observe on the main thread
.observeOn(AndroidSchedulers.mainThread())
.subscribe(createSubscriber())
The resulting output:
D/MainActivity: Im subscribed
D/MainActivity: New Video : How to breed llamas
Although the result is the same for this example, having the ability to execute different tasks on different threads is useful — especially when you’re dealing with background operations such as database updates and network calls.
Operators
YouTube is great, but there are other video streaming services available. Perhaps you’d like to subscribe to more than just a YouTube Observable, but apply the same logic to each.
You can create a method that returns a new Netflix Observable like this:
fun createNetflixChannel(): Observable {
return Observable.just("House of Cards")
}
Then, you can subscribe to both the Youtube Observable and the Netflix Observable, like so:
val subscriber = createSubscriber()
createYoutuber()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber)
createNetflixChannel()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
raywenderlich.com 62
Advanced Android App Architectures Chapter 6: RxJava
.subscribe(subscriber)
That’s a lot of duplicated code! Fortunately, there’s an easier way to do this without duplicating the code. You can merge your Observables into a single Observable using the merge operator.
Operators in RxJava are a way to modify your Observables and/or their data to facilitate your development workflow without the need to create your own methods. Most operators operate on an Observable and return an Observable, making it possible to chain them one after the other.
You’ve already learned about two operators: SubscribeOn and ObserveOn. Both of them are utility operators that operate on an Observable and return an Observable — that’s why you were able to chain them in the previous examples.
On the other hand, the merge operator lets you merge two different Observables into one by using the merge() method from RxJava. This is especially useful if you want to apply the same operations to both of them.
You can rewrite the above code using the merge() method like this:
Observable.merge(createYoutuber(), createNetflixChannel())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(createSubscriber())
This is much simpler and easier to read. Here, you’re creating a Youtuber Observable, a Netflix Observable and merging them into a new Observable using merge().
After that, you can apply the necessary operations to the new Observable just like you would to each of them separately.
The result is the following:
D/MainActivity: Im subscribed
D/MainActivity: Im subscribed
D/MainActivity: New Video : How to breed Llamas
D/MainActivity: New Video : House of Cards
Awesome! You just made your code easier to understand.
There are many other useful operators at your disposal in the RxJava library. They’re not all covered in this chapter because there are too many. If you’re interested in learnig more, check out the official documentation: http://reactivex.io/documentation/ operators.html.
raywenderlich.com 63
Advanced Android App Architectures Chapter 6: RxJava
Frequently Not Asked RxJava Questions Q. Can I use the usual listeners/callbacks instead of RxJava?
You certainly can! But using RxJava offers several benefits that can heavily pay off in the long run by reducing the amount of boilerplate code and making it more concise. You can even apply several operations to your Observables to reduce duplicate code.
Q. Are you sure that RxJava is 100% compatible with my Kotlin code?
Yes. No need to worry about compatibility issues, Kotlin was designed with Java interoperability in mind, and the RxJava library is no exception.
Q. I just read about RxKotlin, what is that?
RxKotlin is a lightweight library that takes advantage of Kotlin’s extension functions to make RxJava code more Kotlin-y. Of course, you can use RxJava with Kotlin out-of-the box, but this library makes things even easier.
Key points
• The Observer pattern helps your objects know when something interesting happens so they can react accordingly.
• The Observer pattern defines a one to many relationship between an Observable and the Observers.
• An Observable is an object that emits notifications about its state to one or more Observers.
• Reactive Programming is a declarative programming paradigm in which you dynamically react to changes.
• RxJava is an open source library for Java and Android that helps you create reactive code. It’s heavily inspired by functional programming.
• RxJava implements the Observer pattern with two main interfaces: Observable and Observer.
• RxJava executes all tasks on the same thread from which it was called. raywenderlich.com 64
Advanced Android App Architectures Chapter 6: RxJava
Where to go from here?
RxJava is a powerful and popular library that lets you write reactive code in your Android Projects. Even though it was written in Java, it’s 100% interoperable with Kotlin. If you want to learn more about RxJava, check out Irina Galata's on the site Reactive Programming with RxAndroid in Kotlin raywenderlich.com/384-reactive programming-with-rxandroid-in-kotlin-an-introduction
raywenderlich.com 65
Section II: Fundamental UI
Architectures
In this section, you’ll rewrite WeWatch three different ways, first using MVP, then using MVVM with data binding, and finally using MVVM with Android Architecture Components. Though a user of the app won’t be able to see any difference after each of these changes, you as a developer will see how they solve some of the problems of the original WeWatch app written in MVC.
• Chapter 7, MVP Theory: The first architecture pattern you’ll learn is Model View Presenter. Here, you’ll learn the theory behind pulling presentation logic out of the Android Activity and why it results in a significant improvement over the MVC pattern in terms of separation of concerns and testability of code.
• Chapter 8, MVP Sample: With theory in tow, you’ll then refactor WeWatch to the MVP pattern step-by-step.
• Chapter 9, Testing MVP: Now that you’ve refactored the app to MVP, you can write unit tests on the logic that previously was untestable when it was in the MVC pattern.
• Chapter 10, MVVM Theory: The next architecture pattern you’ll learn is Model View ViewModel, a pattern which centers on exposing a stream of events to Views. Architecting your app this way helps you write loosely coupled and easily testable code.
• Chapter 11, MVVM Sample with Data Binding: In this chapter, you’ll refactor WeWatch to the MVVM pattern using the Data Binding library from Android Architecture Components.
• Chapter 12, MVVM Sample with AAC: In this chapter, you’ll again refactor WeWatch to the MVVM pattern, this time using a combination of the ViewModel and LiveData libraries from Android Architecture Components.
• Chapter 13, Testing MVVM: Having completed the refactoring of the app to MVVM, you’ll write unit tests on the ViewModel to ensure it behaves as expected.
raywenderlich.com 66
7Chapter 7: Model View
Presenter Theory
By Yun Cheng
As you saw in the MVC chapter, although the MVC architecture pattern theoretically allows an app to achieve separation of concerns and unit testability, in practice, MVC didn’t quite work on Android. The problem was that the Android Activity, unfortunately, served the role of both View and Controller. Ideally, you would want to somehow move the Controller out of the Activity into its own class so that the Controller can be unit testable.
One architecture pattern that achieves this goal is MVP, which stands for Model View Presenter, and is made up of the following parts:
• Model is the data layer, responsible for the business logic.
• View displays the UI and listens to user actions. This is typically an Activity (or Fragment).
• Presenter talks to both Model and View and handles presentation logic.
The Model-View-Presenter pattern
With MVP, the Model remains the same as with the MVC architecture. The Model is the data layer, and is responsible for the business logic: retrieving the data, storing the data and changing the data. In your sample project, your business logic revolves around movies, so the Model is composed of the Movie data object, the LocalDataSource, which interacts with the local database. It is also composed of the RemoteDataSource, which interacts with the Movie Database API over the network.
The View is also the same as with the MVC architecture, responsible for displaying the UI, except that, with MVP, this role is specifically designated to an Activity or
raywenderlich.com 67
Advanced Android App Architectures Chapter 7: Model View Presenter Theory
Fragment. The View will hide and display views, handle navigating to other Activities through intents, and listen for operating system interactions and user input.
The Presenter is the glue class that talks to both the Model and View. Any code that does not directly handle UI or other Android framework-specific logic should be moved out of the View and into the Presenter class. For example, while the View will listen for button clicks, the presentation logic of what happens after a user clicks a button should go into the Presenter class. Similarly, when the Model has updated, it is not the responsibility of the Model to know how the View ultimately wants to display the data.
It is the Presenter’s job to do any additional mapping or formatting of the data before handing it to the View to display it. This kind of logic is known as presentation logic, and it is handled by the aptly named Presenter.
While the View extends from san Activity or Fragment, the Model and Presenter do not extend Android framework-specific classes, and, for the most part, they should not contain Android framework-specific classes. In other words, there should be no references to the com.android.* package in the Model or Presenter. As you will read later, this rule allows for both the Model and the Presenter to be unit tested with this pattern.
The Presenter should not contain Android framework-specific classes
raywenderlich.com 68
Advanced Android App Architectures Chapter 7: Model View Presenter Theory
In summary, with the MVP pattern, the roles of the Model and the View are equivalent to those in MVC, except that now the Activity or Fragment is explicitly designated the role of the View. The Controller has now been replaced with a Presenter class, which, based on the definition above, seems to serve a similar role as the theoretical Controller.
However, there are several key characteristics of the MVP pattern that differentiate it from MVC and allow for it to work in Android: the order in which events occur, the breaking of the ties between View and Model and the use of interfaces.
The MVP flow
To use MVP, you’ll need to reimagine the order in which events occur in the flow of an Android app. Unlike in MVC wherein the Controller is supposed to be the main entry point for the app, the main entry point is the View.
If the View serves as the entry point for user input — such as a button tap — that implies that the View must be an Activity or Fragment, as these Android classes are able to handle user input. The View can then inform the Presenter of the user input, and the Presenter can handle it by updating the Model. Once the Model has been updated, the Presenter can then update the View with the most up-to-date data.
MVC flow
raywenderlich.com 69
Advanced Android App Architectures Chapter 7: Model View Presenter Theory
For example, in your movie app, the user adds a new movie to the To-Watch list by entering the title and release year of the movie, and then they tap a button to add. In Step One, the Activity listens for user inputs and tells the Presenter that the user tapped the button:
fun onClickAddMovie(view: View) {
...
presenter.addMovie(title, releaseDate, posterPath)
}
In Step Two, the Presenter updates the Model (here, served by the localDataSource) by giving it the new Movie to insert into the database:
override fun addMovie(title: String, releaseDate: String, posterPath: String) {
val movie = Movie(title, releaseDate, posterPath)
localDataSource.insert(movie)
...
}
Once the data in the Model has been updated, the Presenter then updates the View in Step Three. In this particular example of adding a new movie, the Presenter simply instructs the View to finish and return to the main screen:
override fun addMovie(title: String, releaseDate: String, posterPath: String) {
val movie = Movie(title, releaseDate, posterPath)
dataSource.insert(movie)
view.returnToMain()
}
Separation of concerns and unit testing
As the example above demonstrates, the Presenter serves as a middleman between the Model and the View, shuttling information and updates between the two classes. Because the Presenter handles these responsibilities, the Model and the View do not need to be aware of each other. By breaking the direct tie between the Model and the View, MVP allows for better separation of concerns.
Furthermore, with this pattern, the Presenter can now be unit tested. Because it does not extend any class specific to the Android framework, the Presenter can be instantiated through a constructor like var presenter = Presenter(), allowing us to call methods on an instance of the Presenter like
presenter.onDeleteTapped(moviesToDelete) to test that it behaves as expected in unit tests.
raywenderlich.com 70
Advanced Android App Architectures Chapter 7: Model View Presenter Theory
Using interfaces
With the exception of Android Architecture Component classes, which are allowable in Presenters, the Presenter should not contain references to any Android framework specific classes, such as Context, View, or Intent. This rule allows you to write regular JUnit tests on the Presenter. However, the Presenter, of course, needs to talk to the View, which means it needs a reference to the Activity, an Android framework-specific class. How can you get around this reference to an Android class when you write your unit tests?
The way to resolve this problem is to create interfaces for the Presenter and View, keeping the interfaces in a single class called the Contract class. The Presenter class implements the PresenterInterface and the Activity implements the ViewInterface.
The Presenter class will then hold a reference to an instance of the ViewInterface interface, rather than a reference directly to the Activity. This way, the Presenter and View interact with each other through interfaces rather than actual implementations, which is, in general, a good software engineering principle that allows decoupling of the two classes. In this case, it also has the additional benefits of removing the Android specific framework class Activity from the Presenter and allows you to mock the ViewInterface in the Presenter for unit testing.
Create interfaces for the Presenter and View
raywenderlich.com 71
Advanced Android App Architectures Chapter 7: Model View Presenter Theory
While it is necessary to create an interface for the View, strictly speaking, the same is not true of the Presenter interface. It would be perfectly fine to allow the View to interact with the actual implementation of the Presenter, especially since it is unlikely to have changing implementations of the Presenter. However, you choose to have a Presenter interface in this project so that the Contract class neatly documents the relationship between the Presenter and the View, clearly outlining the interactions.
MVP advantages and concerns
By dividing an Activity into separate Model, View, and Presenter classes with interfaces, you are able to achieve separation of concerns as well as unit-testable Models and Presenters. These are certainly considerable achievements, even if it means having to navigate through interfaces, which can be confusing at first. In Android Studio, CMD + OPTION + B on Mac or CTRL + ALT + B on PC is the keyboard shortcut for finding your way to actual implementations of interface methods.
Additionally, it is important to give some thought to what happens when the operating system destroys the Activity. When your Activity is destroyed, you must be sure to have your Presenter destroy any subscriptions or AsyncTasks or else that will cause problems when the task completes and the Activity is no longer there.
You’ll also need to think about what happens to the Presenter upon destruction of the Activity. If you want the memory held by the Presenter to be freed and garbage collected when the Activity is destroyed, you must make sure there are no references to the Presenter in any other classes that will continue to live in memory.
If you do want your presenter survive Activity destruction, you can try to save some state through the onSaveInstanceState, or use loaders, which survive configuration changes. In our example project, we will allow the Presenter to be destroyed when the Activity is destroyed.
Key points
• MVP stands for Model-View-Presenter.
• MVP is an architecture pattern whose main objective is the separation of concerns and increased unit testability.
• Unlike MVC wherein the main entry point is the Controller, in MVP the main entry point is the View.
raywenderlich.com 72
Advanced Android App Architectures Chapter 7: Model View Presenter Theory
• The Model is the data layer that handles business logic.
• The View displays the UI and informs the Presenter about user actions. • The View extends Activity or Fragment.
• The Presenter tells the Model to update the data and tells the View to update the UI. • The Presenter should not contain Android framework-specific classes. • The Presenter and the View interact with each other through interfaces.
Where to go from here?
In the next chapter, you will apply your knowledge by rewriting the Movies app using MVP. The exercise will set you up for writing more unit tests for the app that previously were not possible without this architecture pattern.
raywenderlich.com 73
8Chapter 8: Model View
Presenter Sample
By Yun Cheng
In this chapter, you will rewrite the Movies app using the Model View Presenter (MVP pattern. Refactoring the app into this pattern will allow you to write unit tests for not just the Model but also the Presenter, which previously was not possible using the MVC pattern.
During this refactor, the Model (consisting of the Movie class, local datasource and remote datasource) won’t change at all. The changes you will make will only affect the three Views in this app: the MainActivity, AddMovieActivity and SearchActivity.
Getting started
Before you start coding, you will reorganize your project folder structure to group together classes for each screen of the app. Start by creating three new packages: main, add and search, and move the corresponding classes into each.
Before and after directory structure.
raywenderlich.com 74
Advanced Android App Architectures Chapter 8: Model View Presenter Sample
Applying MVP to the Movies app For each of the three screens, you need to do the following:
1. Create a new Presenter class and connect it to the View and Model via dependency injection.
2. Create a new Contract class that contains the PresenterInterface and ViewInterface. 3. Move all presentation logic into the Presenter.
Moving the presentation logic out of the View and into the Presenter class will achieve a greater degree of separation of concerns. You will reduce the role of the View to be that of displaying the data, listening for user input and handling navigation. Any logic that does not fall under one of those categories should be moved out into the Presenter so that the logic can be unit tested.
For each of the Views, you will also create a corresponding Contract class that contains interfaces for the Presenter and View. It is necessary for the Presenter to interact with an interface of the View because you must keep the Presenter free of any Android framework-specific classes if you wish to write unit tests on the Presenter.
Although the Presenter doesn’t strictly need an interface, you will also create one for it anyway and treat the Contract class as a document that describes communication between the View and Presenter.
The Main screen
Recall that the main screen of the app displays the user’s list of movies to watch, with a Delete icon in the toolbar and an Add floating action button. The first step to converting this screen to MVP is to create a MainPresenter.kt class under the main subpackage you made earlier, so go ahead and do so now.
Note: Whenever you add code, make sure to import the appropriate packages by pressing Alt+Enter on Windows or Option+Enter on Mac.
class MainPresenter(private var view: MainActivity, private var dataSource: LocalDataSource) {
private val TAG = "MainPresenter"
...
}
raywenderlich.com 75
Advanced Android App Architectures Chapter 8: Model View Presenter Sample
This Presenter will need to interact with both the View and the Model, so the MainActivity and the LocalDataSource must be passed into the constructor. As you learned in Chapter 5: “Dependency Injection,” it is important to inject dependencies like the View and the Model in through the constructor for unit testing purposes. When you write unit tests for MainPresenter you will pass in mock objects for the View and the Model; this way you will unit test the MainPresenter class itself and not its dependencies.
Now, create a MainContract.kt class under the main subpackage:
class MainContract {
interface PresenterInterface {
//TODO: add interface methods for Presenter
}
interface ViewInterface {
//TODO: add interface methods for View
}
}
For now, this class will contain empty definitions for a PresenterInterface and a ViewInterface. You will add methods to these interfaces later, as you build out the Presenter.
Now that you have the interfaces set up for the View and the Presenter, open MainActivity.kt and update the MainActivity to implement the
MainContract.ViewInterface like the following:
class MainActivity : AppCompatActivity(), MainContract.ViewInterface { ...
}
Next, open MainPresenter.kt and update the MainPresenter class so that it implements the MainContract.PresenterInterface and also holds a reference to a MainContract.ViewInterface for its View rather than a reference to the direct MainActivity implementation:
class MainPresenter(
private var viewInterface: MainContract.ViewInterface,
private var dataSource: LocalDataSource) :
MainContract.PresenterInterface {
private val TAG = "MainPresenter"
...
}
raywenderlich.com 76
Advanced Android App Architectures Chapter 8: Model View Presenter Sample
Throughout this Presenter class, the Presenter will interact with the MainContract.ViewInterface rather than the MainActivity implementation directly. This is done to avoid having Android framework-specific classes like Activity in the Presenter. MainActivity extends AppCompatActivity, which is specific to the Android framework and cannot be mocked, making it difficult to write unit tests.
Now that you have created the MainPresenter class, you need to instantiate the MainPresenter inside the MainActivity.
Open MainActivity.kt again, and in the MainActivity class, add the following code: private lateinit var mainPresenter: MainContract.PresenterInterface
private fun setupPresenter() {
val dataSource = LocalDataSource(application)
mainPresenter = MainPresenter(this, dataSource)
}
Here, you instantiate the MainPresenter, pass in the Activity itself using the this keyword and a local instance of the LocalDataSource.
Next add a call to setupPresenter() inside the MainActivity’s onCreate() method:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupPresenter()
setupViews()
}
Great! You’ve wired up a basic Presenter, but it doesn’t really do much yet. In the next section, you’ll add code for the Presenter to retrieve movies.
Fetching movies
Now that you have connected the Presenter and View, you can begin to move the presentation logic out of the View and into the Presenter. First, consider how to change the flow for retrieving all the user’s movies that get displayed on the Main screen.
Instead of having the View (Activity) perform both the retrieving and displaying of the movies, you can move the retrieving logic into the Presenter.
raywenderlich.com 77
Advanced Android App Architectures Chapter 8: Model View Presenter Sample
The following diagram breaks down the sequence of steps.
Flow for retrieving movies.
You can see that this is a three step process:
• Step One: The view asks the Presenter to get the movie list.
• Step Two: The Presenter gets the movie list from the Model. The Model returns a list of Movie objects to the Presenter or an error in the event that the Model cannot return the list.
• Step Three: The Presenter then uses the ViewInterface to tell the View how to display the error. If the Presenter receives an error from the Model, it will tell the ViewInterface to present an error state to the user.
Step One delegates the responsibility of retrieving movies the Presenter, so start by opening MainContract.kt and adding the getMyMoviesList() method to the Presenter’s interface as follows:
interface PresenterInterface {
fun getMyMoviesList()
}
raywenderlich.com 78
Advanced Android App Architectures Chapter 8: Model View Presenter Sample
Then, implement Step One of the flow by opening MainActivity.kt and replacing the contents of the onStart() method of MainActivity as follows:
override fun onStart() {
super.onStart()
mainPresenter.getMyMoviesList()
}
As soon as the View starts, it asks the Presenter to retrieve the list of movies.
Next, you’ll implement Step Two and Step Three of the flow, getting and displaying the list of movies.
Open MainPresenter.kt and add the following code, moved over from the MainActivity:
private val compositeDisposable = CompositeDisposable()
//1
val myMoviesObservable: Observable>
get() = dataSource.allMovies
//2
val observer: DisposableObserver>
get() = object : DisposableObserver>() {
override fun onNext(movieList: List) {
if (movieList == null || movieList.size == 0) {
viewInterface.displayNoMovies()
} else {
viewInterface.displayMovies(movieList)
}
}
override fun onError(@NonNull e: Throwable) {
Log.d(TAG, "Error fetching movie list.", e)
viewInterface.displayError("Error fetching movie list.") }
override fun onComplete() {
Log.d(TAG, "Completed")
}
}
//3
override fun getMyMoviesList() {
val myMoviesDisposable = myMoviesObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(observer)
compositeDisposable.add(myMoviesDisposable)
}
raywenderlich.com 79
Advanced Android App Architectures Chapter 8: Model View Presenter Sample
That’s a lot of code, so let’s go through it one step at a time:
1. The myMoviesObservable is an observable for the list of Movie objects from the dataSource. This code is the same as before, except now the Presenter will be the one interacting with the dataSource to get movies, which is why you passed in the dataSource to the Presenter through the constructor.
2. Inside the observer, the Presenter determines how to consume the list of movies it receives, depending on the size of the list and whether an error has occurred. What has changed in this code is that we must interact with the View by calling methods on the viewInterface property to respond to each of these scenarios. The Presenter
does not know how to display lists or errors, so it delegates those tasks to the View. If there are no movies to display, the Presenter asks the View to display no movies. If there are movies to display, the Presenter asks the View to display the list of movies.
3. The getMyMoviesList() method connects the observer to the myMoviesObservable so that it can begin observing. The method is exactly the same as before, except now it belongs to the Presenter.
As you can see, in Step Two of the retrieval flow occurs in getMyMoviesList(), where the Presenter gets the list of movies from the Model by subscribing to an observable of the LocalDataSource’s list of allMovies. Although the Presenter contains the logic that decides what to display, the actual job of displaying content belongs to the View.
As part of Step Three in the flow, the Presenter will ask the View to display the list of movies; otherwise, it will display a view that indicates that there are no movies to display or else display an error message if an error occurred.
Next, the code inside the MainActivity will need to be slightly refactored to handle these scenarios. But, first, you need to define the View interface.
Open MainContract.kt and add the following methods to
MainContract.ViewInterface:
interface ViewInterface {
fun displayMovies(movieList: List)
fun displayNoMovies()
fun displayMessage(message: String)
fun displayError(message: String)
}
raywenderlich.com 80