Skip to content

Fork of the Google Android architecture app modified to use Conductor and a single activity architecture

License

Notifications You must be signed in to change notification settings

grepx/android-architecture

This branch is 40 commits ahead of, 780 commits behind android/architecture-samples:main.

Folders and files

NameName
Last commit message
Last commit date

Latest commit

b994696 · Oct 16, 2016

History

87 Commits
Sep 29, 2016
Sep 7, 2016
Apr 7, 2016
Oct 16, 2016

Repository files navigation

TODO-MVP-Conductor

Based on the TODO-MVP sample and uses the Conductor framework to refactor to a single Activity architecture.

Project maintainer: grepx.

The basic components of the Conductor framework architecture are outlined here.

Activity and Fragment are replaced with Controller classes, which have a simpler lifecycle outlined in the Conductor documentation. All classes below the View layer stayed the same as in the original TODO-MVP app.

Diagram

Code

Router setup

Navigation between Controllers is handled by the Router class. Routers are responsible rendering Controllers, navigating between Controllers, and managing the backstack of Controllers.

In this example, we only use a single Router, which is configured in MainActivity and given a container view to render it's content in - much like Fragment in a vanilla Android architecture.

mRouter = Conductor.attachRouter(this, mContainer, savedInstanceState);

The Router and backstack of Controllers is automatically kept safe in a retained Fragment during configuration changes and can also be automatically serialised/deserialised during other destructive events.

Therefore, when we attach the main Router in MainActivity we check to see if it already has a Controller stack from a previous session, otherwise push the first Controller to the stack.

if (!mRouter.hasRootController()) {
  mRouter.setRoot(RouterTransaction.with(new TasksController()));
}

Back presses are communicated from MainActivity to the main Router.

@Override
public void onBackPressed() {
  if (!mRouter.handleBack()) {
    super.onBackPressed();
  }
}

Controller navigation

We can get a reference to the current Router from any Controller class by calling the getRouter method. We can get backstack support by pushing a new Controller to the Router. We can also animate the push/pop transition to/from the new Controller by supplying an AnimatorChangeHandler:

getRouter().pushController(RouterTransaction.with(addEditTaskController)
  .pushChangeHandler(new HorizontalChangeHandler())
  .popChangeHandler(new HorizontalChangeHandler()));

Note: When you are testing using Espresso, make sure you turn off animations on the device as outlined in the Espresso setup instructions.

Communicating back from a Controller

The original TODO-MVP code makes use use of Activity::setResult and Activity::onActivityResult to pass a result back from AddEditTaskFragment to TaskDetailFragment. Controllers achieve this through holding a direct reference to the previous Controller. This reference is set using setTargetController such as in TaskDetailController:

AddEditTaskController addEditTaskController = new AddEditTaskController(taskId);
addEditTaskController.setTargetController(this);

AddEditTaskController can now perform a callback on TaskDetailController when it is finished, using the ControllerResultHandler interface:

Controller targetController = getTargetController();
if (targetController instanceof ControllerResultHandler) {
  ((ControllerResultHandler) targetController).onResult(ControllerResult.OK);
}

The navigation drawer

The NavigationView used for the navigation drawer needs to be configured in the Activity layout file. A getDrawerLayout method is provided by BaseController so that each Controller can access the drawer layout and reconfigure it as required (this is only actually used to set whether or not the user should be able to slide it out while they are on that screen).

Configuring the drawer layout so that the user can't slide it out in a Controller:

DrawerLayout drawerLayout = getDrawerLayout();
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
Espresso

The navigation drawer also caused the Espresso tests in AppNavigationTest to become flakey. This is due to the time taken to animate the drawer as it opens/closes, which cannot be switched off like other animations.

The only way I could find to fix this way to use the EspressoIdlingResource to tell Espresso when the drawer has finished animating. This code is contained in EspressoDrawerListener.

Features

Complexity - understandability

Use of architectural frameworks/libraries/tools:

Conductor

Conceptual complexity

Developers need to be familiar with the Conductor framework. It is a simple framework relative to using Fragments but still non-standard Android development.

Testability

Unit testing

All the Unit tests remain the same as in the original TODO-MVP app.

UI testing

Several tweaks were made to the Espresso tests to get them working properly, the most significant is in AppNavigationTest as outlined above.

Hermetic UI tests

EditTaskScreenTest, StatisticsScreenTest and TaskDetailScreenTest were previously hermetic tests that only tested their respective screens. This was possible since you could configure the test environment and then craft an Intent to launch directly to the Activity without touching the rest of the app.

Since you can no longer launch directly to any one screen in the app, these tests are no longer hermetic. The test environment is set up as before, but we launch to MainActivity and manually navigate to the correct screen to perform the test.

Code metrics

There is roughly the same amount code as in the original app.

-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Java                            49           1129           1436           3628
XML                             31             85            291            559
-------------------------------------------------------------------------------
SUM:                            80           1214           1727           4187
-------------------------------------------------------------------------------

Maintainability

Ease of amending or adding a feature

Similar to the original app. This app doesn't have any particularly complex screens composed of multiple elements, which is really what the Conductor framework is designed for.

When considering Conductor, you should also take into account that there are some UI styles that Conductor doesn't properly support yet, such as dialogs or anything else that uses Fragments.

Learning cost

Medium as the Conductor framework is quite simple but there are far fewer resources available.

About

Fork of the Google Android architecture app modified to use Conductor and a single activity architecture

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published