Write better code with Conductor's Controller
Kittinun Vantasin
About me
Mobile Enthusiastic
💖 Kotlin + Open source
Work at Mercari (Merpay) in Tokyo, Japan
github.com/kittinunf
@kittinunf
Agenda
1. How to architect your components in Android app
2. How Conductor fits into this
Design Philosophy
Using classes to separate Concerns ! ! !
Storage, Networking, Parsing, Routing, Business logic, Display
Easily abused! >_< 😱
Abused ? Why?
Business Logic
View Setup / Loading
Model observation
Datasource interaction / Network
Handling permission / API differences
Screen
Problems
Hard to understand 😕
Hard to test ❌
Fragile to change 💔
7,000 lines seems acceptable 🙅
Do something?
So, Yet another arch?
NO
MVP, MVVM, MVI, M** ... (🔮 🙅♂️ we aren't talking about arch today™)
Then, what?
Slowly changing the way you design your code structure and screen
With 6 simple Concepts
Concept #1
Use appropriate data structures & code structures
Data structures
~ 80 % of the time choose List<T>
When you need something more like association e.g. A <-> B tries Map<A, B>
Ownership, does this contains that? picks Set<T>
Need something like a hierarchy use Tree<T>
Code structures
fun getFoos(): List<Foo> {
val size = bars.size
val arr = ArrayList()
for (i in 1..size) {
arr.add(Foo(bars[i].bar))
}
return arr
}
fun getFoos(): List<Foo> =
bars.map { Foo(it) }
Use the right patterns where it is appropriate
Don't overuse it
Learn tools' strength and weakness!
Concept #2
Naming affects intentions!
2 Hardest Probs in CS
1. Cache Validation
2. Naming things
3. Off-by-one errors 🤔
Naming!
*View should belong to View layer
e.g. View, TextView, CalendarView
*Manager should belong to **Controller** Layer
e.g. LocationManager, AlarmManager, SearchManager
*Data, *Cursor usually a helper to Model layer
e.g. SqlLiteDatabase, SqlLiteCursor
Please choose GOOD NAMES!
Concept #3
Activity™ and Fragment are just a regular VIEW
Responsibilities
Loads Views
Layout Views
Perform Animation on Views
Show/hide Views
Transition Views
It is all about Views ...
So ... Maybe it is View?
If they are only doing View stuffs ...
Implications?
No networking
No data persistent layer
No navigation (Activity™)
No business logic
No model observation
Just displaying stuffs ...
Concept #4
Views are usually small™
Smallness
Views are small components of a screen
They do only one job and do it really well
A View != A screen
Small Fragments
A Fragment != A screen
You don't have to fill the screen with one fragment!
Use multiple fragments where it makes sense!!
Concept #5
Views are great when they use as composable/reusable units
Composing
Adding small things to make big things
The single most important skill as software engineer
Reusing
We don't rewrite TextView? we don't rewrite RecyclerView?
We adjust the style and reuse them
We adjust the style and reuse them
We adjust the style and reuse them
We adjust the style and reuse them
WHY DO WE DO THAT WITH FRAGMENT ?
Reusing cont.
It is not 🙅 |
It is a 🙆♀️ |
---|---|
ShoppingListItemFragment |
ListItemFragment with Adapter that shows ShoppingItem |
AddressFormFragment |
Fragment that shows multiple EditTextFragment as a form item |
ImageView subclass that hits network |
ImageRemoteFragment with an image provider (local / network) |
Concept #6
We need an actual place for our logic
Case study
1. Install/Uninstall + Open app
2. Read What's new
3. Rate App
4. Contextual information
5. Related application
6. Read Description
Logic
-
Model observation
-
Determine Action
-
Observe the state
-
List related apps
Actions
-
Uninstall/Install Open
-
Rate the app
-
Go to the different app
-
See the images
-
Search and more ...
If we do this in one Fragment, it would be horrible to maintain
Then, how ?
GooglePlayStoreItemInfoFragment
StoreHeaderFragment
StoreWhatsNewFragment
StoreContextualLinkFragment
StoreRelatedItemFragment
StoreDescriptionFragment
How?
Then, how ?
StoreItemInfoF
StoreHeaderF
StoreWhatsNewF
StoreContextLinkF
StoreRelatedItemF
StoreDescriptionF
How?
?
What is this?
?
It can be anything!
MVVM - it is View Model
MVP - it is Presenter
MVC - it is a Controller™
M** - it is a something that sits in the middle managing components
A better code structure
1. Use appropriate data structures & code structures
2. Naming affects intentions!
3. Activity™ and Fragment are just a regular VIEW
4. Views are usually small™
5. Views are great when they use as composable/reusable units
6. We need an actual place for our logic
It looks like we are done here ...
but ...
Exception java.lang.IllegalStateException:
Can not perform this action after onSaveInstanceState
android.support.v4.app
.FragmentManagerImpl.checkStateLoss (FragmentManagerImpl.java:1538)
android.support.v4.app
.FragmentManagerImpl.enqueueAction (FragmentManagerImpl.java:1556)
android.support.v4.app
.BackStackRecord.commitInternal (BackStackRecord.java:696)
android.support.v4.app
.BackStackRecord.commit (BackStackRecord.java:662)
We don't like Fragments?
Entering Conductor
Promote the View-based Application
Fragment alternative (much simpler)
Just works™ (with a little setup)
Conductor
Activity
Router
Controller 1..n
View
Setup Conductor
class SampleActivity : AppCompatActivity() {
private lateinit var router: Router
override fun onCreate(bundle: Bundle?) {
super.onCreate(bundle)
setContentView(R.layout.activity_sample)
router = Conductor.attachRouter(
this,
container,
bundle)
if (!router.hasRootController()) {
router.setRoot(RouterTransaction.with(
MainController()
))
}
}
}
<com.bluelinelabs.conductor.
ChangeHandlerFrameLayout
android:id="@+id/container"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
/>
Setup Controller
class MainController : Controller() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup): View =
inflater.inflate(R.layout.controller_demo, container, false).apply {
//setup your view
}
}
Similar to Fragment counterparts
Easier lifecycle!
Navigate!
Use Router to push/pop controllers
router.pushController(RouterTransaction.with(
AnotherController()))
animation, horizontal/vertical/fade?
router.pushController(
RouterTransaction.with(AnotherController()).
pushChangeHandler(HorizontalChangeHandler()).
popChangeHandler(HorizontalChangeHandler())
)
How about Lifecycle?
New instance (init)
onCreateView
Controller is on top!
onDestroyView
Controller is on top again
Send to back / Configuration Change e.g.
Remove from Router / Got killed by System
SURVIVES CONFIGURATION CHANGE
Benefits of Controller
Simple to understand lifecycle
Transaction executed immedietely
Survive the configuration change
Extensible, Compatible with AAC, Rx ... and others
Implications?
Go back and read the whole slide from beginning again
:%s/Fragment/Controller/g
End results
Really tiny Fragment Controller (< 300 LOCs)
Almost never subclass View (android.view.View)
Reusable Controller / Clear separation
Happy Coder 🎉
Q & A
メルカリ
We are hiring!!
kittinun.f@mercari.com
Write better code with Conductor's Controller
By Kittinun Vantasin
Write better code with Conductor's Controller
- 2,326