Skip to content

AllanHasegawa/PassosDoImpeachment-app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

This project is deprecated

This project is being deprecated. I have no more plans to update it.

I have been working on a new project that is better organized and "cleaner". I may in the future decouple its framework and make a new opensource project with it. If you have any ideas, shout it to me ^^;

Thank you o7

Passos do Impeachment (Android app)

A full demo Android app showcasing data syncing with a RESTful server. Android SyncAdapter is used to schedule the appropriate time to sync the data, a process done with Retrofit. StorIO caches the data for offline usage. To keep devices up-to-date, the server issues GCM messages to the client when things changes. Kotlin and RxJava makes everything beautiful.

In short, the following awesome projects were used:

The app has an accompanying server to sync the data with. Check it here: PassosDoImpeachment-server

Phone1

Phone2

Phone3

Tablet1


Architecture

The architecture adopted in this project was heavily based on the Android-CleanArchitecture project. A more in-depth view of that architecture can be found in:

A layered approach is used to better understand the architecture. The following image shows the three layers (like in an onion) used in this app:

Layers

A quick description of each layer will be given soon. But, the important thing to take from this image is how the dependency arrow only points inwards. In other words, the domain layer doesn't known a thing about the presentation layer. However, the presentation depends on the domain layer.

Each layer can have many modules (as in a gradle module), and those modules are written in the image above. For instance, the domain layer has just the domain module. However, the app layer has the app, db and cloud modules.

domain layer

This is where the business logic resides. Most of the logic on how entities talk with each other and data manipulations happens here.

One concrete example: You got a list of responses from the cloud as json objects and you want to cache it with the app's local storage. The domain layer will provide a use case (interactors) to the rest of the application with that functionality, as in this piece of code.

Important points:

  • No dependencies to others layers
  • Try to keep as minimum as possible the dependencies to others frameworks/libraries/tech

By not adding dependencies here you can delay the decision on what framework/libraries/tech to use. The decision on what database (Realm, SQLite, etc) should not matter at this point. And, if one doesn't work well for your project, switching it around will be less painful.

However, there are times when you need some functionality from a framework (Android for example). And when these times come, you invert the dependency (a.k.a. Dependency Inversion Principle).

In practice, let's say you want to share a text. The domain layer can just create an interface, as in this piece of code, and use it when needed without worrying what is implementing it. So another layer, the app for instance, can then step up and implement it, like this.

presentation layer

The presentation layer depends on the domain layer. But, as in the domain layer, the presentation also tries to keep the number of dependencies to a minimum. With that, the presentation does not depends on any framework (like Android).

This layer is mainly made of two types of objects:

  • Presenter: A bridge between the MvpView and the domain layer. It holds the logic to control the view, and uses the domain layer to get the data. It also has logic to deal with inputs from users (can be a person, or any external system).

  • MvpView: Interface or abstract class with no logic at all. All it does is to describe all the actions the presenter can do on a view, and what external events it can receive. In practice, this view will be implemented by the app layer.

app layer

The app layer is where we start to see more specific implementations. This layer has three modules:

  • db: Implements the repositories.
  • cloud: Implements the rest clients.
  • app: The app module is where most of the code goes. In here we will find the MainActivity, Android services (GCMRegistration, SyncAdapters, etc) and Android views.

Architecture Diagram

The following diagram shows in more detail the different parts of the architecture and how they relate to each other:

Diagram

domain module

In the domain layer/module we have many interfaces. As commented early, its so we don't worry about specific implementations here.

The use cases are concrete classes, and they are the ones that will interact with the rest of the system. All use cases implements an abstract UseCase. This implementation was heavily based on the one used by Fernando Cejas.

I really liked his design. Instead of the use case returning an Observable, he forces the user to pass a subscriber instead. This way, if the user decides to do some more complex logic, it will be kind awkward, so it is better to just create another use case.

cloud and db modules

Those two modules are really boring. All they do is implement some of the interfaces found in the domain layer.

The db module has two implementations, one using Content Providers (example) and another In-memory (example). The cloud module also has two implementations, one using Retrofit (example) and another mock implementation that only replays what we want (example).

presentation module

The presentation module is based on the MVP design pattern. Presenters will mostly call use cases and control the MvpView. Sometimes the presenter may use a device, but its communication is mostly limited to the view and the use cases.

Each presenter will have an interface describing the events it can receive from the system. While the view will have an interface describing what it can show on screen (eg. ListStepsMvp). Presenters and Views are grouped together based on the "contract" pattern used by the android-architecture project (tip by @hussam789, thanks!).

The "Clean-way Android architecture" implementation I mentioned earlier keeps presentation + app on the same module. But, why put the presentation module on a different module than the app? To me it was because my presentation module has no dependencies to the Android framework. Also, keeping things separated was much easier to keep track of things and have an idea of what every screen and view will have to do.

app module

The app module is composed of a single activity named MainActivity. It uses Conductor to control the screens (the views the users can see). With Conductor, a controller can be like a fragment or an activity. A controller will have a presenter (but will not communicate with it) and will implement a view.

The controller is like a bridge that connects the MvpView from the presentation layer to Android Views (like a ViewPager or a TextView).

The only way for a controller to communicate with a presenter is through listeners in the view, this way the presenter can keep track of what is going on.

The services that run on background, like synchronization and GCM registration, will mostly delegate its work to use cases. Also in this layer you will find the implementations for the devices interfaces.


Reactive

The entire architecture is built from the ground up to support reactive programming. Let's see how the list of news is automatically update when the data changes.

First the domain declares repositories that return Observables (NewsRepository).

The GetNewsUseCase will just return the same observable from the repository. So the repository implementation must be aware of it. For the Content Provider version StorIO was used as it provides reactive queries. For the In-memory db, the behavior was simulated using BehaviorSubjects.

The ListNewsPresenter will subscribe to the GetNewsUseCase and group the news by date prior to sending them to the MvpView. This operation is reactively done every time the data changes.

The MvpView is implemented by the ListNewsController, and when it gets a request to render news, all it does is update the data in the RecyclerView.Adapter and call notifyDataSetChanged().


Dependency Injection

The example above shows one simple presenter, the ListNewsPresenter.

It has one problem though, that list of parameters in its constructor. This is one problem with "dependency injection", where the objects require the instance of many others in order for it to function.

Dagger2 to the rescue \o/. Why? well, instead of manually entering all those parameters, we can declare the presenter to be injected then ask Dagger2 to inject it for us. And that is it! \o/. Dagger2 just figured out how to build all the parameters of the ListNewsPresenter, then it instantiated a ListNewsPresenter for us, and put it nicelly in our parameter. Isn't Dagger2 a sweet little.. amm thing??

Now, the question on how Dagger2 actually know what object to instantiate is more complex and will not be covered here, but it is mostly just about components and modules.


Automated Testing

domain module

Testing in the domain module is pretty straightforward because it only contains use cases as concrete classes. We can assume everything else is working perfectly.

Although, use cases depend on repositories, rest services and etc. That is where Mockito comes into play. Mockito is awesome, it allows us to quickly fake a repository (for example the when keyword is part of Mockito), and focus on the functionality of the use case being tested.

cloud module

The cloud module implements the REST client using Retrofit. This client will do HTTP requests. To properly test it, the MockWebServer project is used to create a local mock server. MockWebServer lets us quickly test the behavior of our client with different responses and can even simulate bad connections.

Here is a code example.

db module

The db module implements the repositories interfaces using Content Providers and SQLite, part of the Android Framework. But, thanks to the Robolectric project we don't have to run these tests on an Android emulator or a real device \o/. Robolectric will fake a Content Provider and SQLite database for us \o/.

Robolectric is pretty simple too, just run your tests on its environment, and use the functions as one would in Android. Here is a code example. Note how you can get a ContentResolver from the runtime environment :)

presentation module

One of the main advantages of having a separate presentation module is to better test the MVP.

Unfortunately, the app module existed long before this separation was introduced in this project, and most tests were already created for the app module. The presentation module was them created to pass these tests.

Thus, this module ended up without tests. In an app still in development, it is highly recommended to test this module.

app module

The app module is where the application is built, so testing it is a bit less straighforward. First, the app will have full syncing capabilities. It will try connect to the cloud and access the actual database on the device. Because content from the cloud is not predictable, we can't use it for testing.

This is where Dagger2 makes things a lot easier. We can create a Dagger2 component telling what kind of implementations we want use for the repositories/rest services interfaces. For testing, we want to use an in-memory implementation, and insert into it what we want just for the tests. This is the component/module that does it.

In our tests we can just use that component instead of the usual one that uses real implementations: code

With the mock implementations injected into our app, we can then populate the fake database with a predictable content: like this.

The actual testing of the app is done with Espresso, a view hierarchy based testing framework. Most of the time, we must first find a view in the hierarchy and then do some action on it, like a click or check if it is visible. This is where a predictable content comes in handy, because we can then find the view using its text, like this. Because we have many views with id R.id.step_title_tv, we can differentiate between them using its text content.

In short, the app module testing is all about mocking the data, and test a bunch of actions and check if the screen is showing what we expect it to show :)


Where are all the comments?

After applying this architecture, and making sure all the rules are followed and everything named properly, I found almost no reason to add comments in code.


Running

To get things started:

  1. Get a GCM configuration file and place it in the app folder.
    1. GCM file from here.
  2. Open the project in Android Studio and just run it.
  3. By default, the app will fetch data from the internet on first run (or when data is outdated). You can easily change the REST config in the file RestConfig.kt to use your local server.

License

Passos do Impeachment (app) is published under the Apache 2.0 license.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages