1 of 75

Modern Android

How to ditch Activities & Fragments

@Fabien_Devos | Wealthfront

2 of 75

3 of 75

Fragments.

4 of 75

Android Lifecycle

5 of 75

6 of 75

7 of 75

8 of 75

https://github.com/xxv/android-lifecycle

9 of 75

10 of 75

Problem

Problems

11 of 75

Problems with Activities & Fragments

  1. Complex lifecycle
  2. Passing objects requires serialization
  3. Destroyed on rotation
  4. Poor control of the backstack
  5. Hard to test

12 of 75

Solution

13 of 75

Solution

  • Single Activity Architecture
  • Make Screens simple java objects
  • Screens survive rotation, but not views
  • Write our own navigation system
  • Separate the logic from the views

14 of 75

Opinionated

  • No state restoration by default
  • Screens stay in memory when navigating
  • No injection or other framework required

15 of 75

Architecture

16 of 75

Single Activity Architecture

Activity

View for Screen B

View for Screen A

17 of 75

Screens & Views

18 of 75

MV

C

VM

P

?

19 of 75

Model View Controller

20 of 75

Model View Controller

Model

Controller

View

21 of 75

Model View Presenter

22 of 75

Model View Presenter

Model

Presenter

View

23 of 75

Model View Presenter

Model

Presenter

View

24 of 75

The (Good Old) Onion Model - aka Layers

Model

Presenter

View

25 of 75

The (Good Old) Onion Model - aka Layers

Data

Screen�(Logic)

View

26 of 75

Separate the logic from the views

27 of 75

The logic lives in the screens

28 of 75

Views are dumb

29 of 75

Screens are regular Java objects

30 of 75

Screens are easy to test

31 of 75

Logic is easy to test

32 of 75

Screens are also injection friendly

33 of 75

Navigation

34 of 75

Goals

  1. Full control of the backstack
  2. Handle animations
  3. Connect screens to the activity lifecycle
  4. Navigating is as simple as calling goTo(screen)

35 of 75

Magellan

The Navigator keeps track of screens and animate between their views.

36 of 75

Screens survive rotation, but not views

37 of 75

Rotation

MainActivity

View for Screen A

Navigator

Screen A

38 of 75

Rotation

New MainActivity

New View for Screen A

Navigator

Screen A

39 of 75

Navigating means pushing on the stack

40 of 75

Navigation

MainActivity

View for Screen A

Navigator

Screen A

41 of 75

Navigation

MainActivity

View for Screen A

Navigator

Screen A

Screen B

42 of 75

Navigation

MainActivity

View for Screen A

Navigator

Screen A

Screen B

View for Screen B

43 of 75

Navigation

MainActivity

Navigator

Screen A

Screen B

View for Screen B

44 of 75

Navigating back

MainActivity

View for Screen A

Navigator

Screen A

45 of 75

46 of 75

Magellan: Navigation

navigator.goTo(new MySimpleScreen(stuff));

47 of 75

Magellan: Navigation

navigator.goTo(new MySimpleScreen(stuff));

48 of 75

Magellan: Navigation

goTo()

49 of 75

Magellan: Navigation

show()

50 of 75

Magellan: Navigation

navigator.overrideTransition(transition);

51 of 75

52 of 75

Magellan: Navigation

Navigator

.withRoot(new HomeScreen())

.loggingEnabled(BuildConfig.DEBUG)

.build();

53 of 75

Magellan: Navigation

navigator.goBack();

navigator.goBackTo(screen);

navigator.replace(screen);

54 of 75

Magellan: Navigation

navigator.rewriteHistory((history) -> {

if (!authenticator.isUserLoggedIn()) {

history.clear();

history.push(loginScreen);

} else {

history.push(homeScreen);

}

});

55 of 75

Magellan: Navigation

navigator.rewriteHistory((history) -> {

if (!authenticator.isUserLoggedIn()) {

history.clear();

history.push(loginScreen);

} else {

history.push(homeScreen);

}

});

56 of 75

Screens have a simple lifecycle

57 of 75

Magellan: Screen Lifecyle

View Created

Shown

Hidden

Created

createView()

onShow()

Destroyed

onHide()

58 of 75

Magellan: Screens

public class MySimpleScreen extends Screen<MySimpleView> {

@Override

protected MySimpleView createView(Context context) {

return new MySimpleView(context);

}

}

59 of 75

Magellan: Screens

public class MySimpleScreen extends Screen<MySimpleView> {

@Override

protected MySimpleView createView(Context context) {

return new MySimpleView(context);

}

}

60 of 75

Magellan: Screens

public class MySimpleScreen extends Screen<MySimpleView> {

@Override

protected MySimpleView createView(Context context) {

return new MySimpleView(context);

}

}

61 of 75

Magellan: Screens

public class MySimpleScreen extends Screen<MySimpleView> {

@Override

protected MySimpleView createView(Context context) {

return new MySimpleView(context);

}

@Override

protected void onShow() {

getView().displayStuff(stuff);

}

}

62 of 75

Magellan: Screens

public class MySimpleScreen extends Screen<MySimpleView> {

@Override

protected MySimpleView createView(Context context) {

return new MySimpleView(context);

}

@Override

protected void onShow() {

getView().displayStuff(stuff);

}

@Override

public String getTitle(Context context) {

return context.getString(R.string.my_screen_title);

}

}

63 of 75

Magellan: Setup

public class MainActivity extends SingleActivity {� @Overrideprotected Navigator createNavigator() {� return Navigator.withRoot(new HomeScreen()).build();� }� @Overrideprotected void onCreate(Bundle savedInstanceState) {� super.onCreate(savedInstanceState);� setContentView(R.layout.main_activity);� }�}

64 of 75

Magellan: Setup

public class MainActivity extends SingleActivity {� @Override� protected Navigator createNavigator() {� return Navigator.withRoot(new HomeScreen()).build();� }� @Override� protected void onCreate(Bundle savedInstanceState) {� super.onCreate(savedInstanceState);� setContentView(R.layout.main_activity);� }�}

65 of 75

Magellan: Setup

public class MainActivity extends SingleActivity {� @Override� protected Navigator createNavigator() {� return Navigator.withRoot(new HomeScreen()).build();� }� @Override� protected void onCreate(Bundle savedInstanceState) {� super.onCreate(savedInstanceState);� setContentView(R.layout.main_activity);� }�}

66 of 75

Magellan: Setup

main_activity.xml:

<com.wealthfront.magellan.ScreenContainer .../>

67 of 75

Only two classes to know about

  • Screen
  • Navigator

68 of 75

Open Source

v1.0

69 of 75

More features!

70 of 75

Toolbar

onNavigate(actionBarConfig)

71 of 75

Add-on: Rx

autoUnsubscribe(observable)

72 of 75

Future

73 of 75

Coming Soon

  • Design lib add-on: tabs
  • withView(v -> {...} )

74 of 75

75 of 75

Questions?