Skip to content
Branch: master
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
src
.gitignore
README.md
build.gradle
proguard-rules.pro

README.md

(Taken from Zhuinden/ExampleGithubClient)

Flowless MVP Example: "Example Github Client"

In reality, login just waits 3 seconds, and the only repository you download is "square" repository.

This is a sample to show data-domain-presentation layering, subscoping via Dagger2, and MVP architecture; in a single-Activity setup provided by flowless.

Some tests are included, although they aren't complete.


Data layer:

  • data layer is reactive, currently in-memory and doesn't actually persist itself anywhere. It should be replaced with SqlBrite + RxJava, or Realm

  • data is held by the DataSource, it is manipulated via the "Repository"


Domain layer:

  • the Service that communicates with the Github API, provided via Retrofit

  • threading is provided by Bolts-Android

  • the manipulation of data is done in the interactors


Presentation layer:

  • contains the presenters which store state and tell the view what to do when an event occurs

  • also contains the views that delegate all callbacks to their presenters


Utils:

  • contains anything else that is not domain specific

  • also contains TransitionDispatcher, which is what the application uses to determine what to do on an application state change



The application is driven by the keys.

For example,

@AutoValue
@Title(R.string.title_login)
@Layout(R.layout.path_login)
@ComponentFactory(LoginComponentFactory.class)
@LeftDrawerEnabled(false)
@ToolbarButtonVisibility(false)
public abstract class LoginKey
        implements Parcelable {
    public static LoginKey create() {
        return new AutoValue_LoginKey();
    }
}

The dispatcher creates the view and the scoped component, the view injects itself, and attaches itself to the presenter.

public class RepositoriesView
        extends RelativeLayout
        implements FlowLifecycles.ViewLifecycleListener, RepositoriesPresenter.ViewContract {

    // constructors
        
    public void init() {
        if(!isInEditMode()) {
            RepositoriesComponent repositoriesComponent = DaggerService.getComponent(getContext());
            repositoriesComponent.inject(this);
        }
    }

    @Inject
    RepositoriesPresenter repositoriesPresenter;
        
    @Override
    public void onViewRestored() {
        repositoriesPresenter.attachView(this);
    }

    @Override
    public void onViewDestroyed(boolean removedByFlow) {
        repositoriesPresenter.detachView();
    }
}

The presenter provides a view contract based on which it can call the callbacks inside the view, to bring it "up-to-date" or to navigate.

@KeyScope(RepositoriesKey.class)
public class RepositoriesPresenter
        extends BasePresenter<RepositoriesPresenter.ViewContract> {
        
    @Inject
    public RepositoriesPresenter() {
    }
        
    public interface ViewContract
            extends Presenter.ViewContract {
        void updateRepositories(List<Repository> repositories);

        void openRepository(String url);
    }

    @Override
    protected void initializeView(ViewContract view) {
        if(repositories == null || repositories.isEmpty()) {
            downloadPage();
        } else {
            updateRepositoriesInView();
        }
    }
}

The interactors download the data via the service, and save it to the model. The presenters are subscribed to changes inside the model.

@ActivityScope
public class GetRepositoriesInteractorImpl
        implements GetRepositoriesInteractor {
    @Inject
    GithubService githubService;

    @Inject
    RepositoryRepository repositoryRepository;

    @Inject
    public GetRepositoriesInteractorImpl() {
    }

    @Override
    public Task<List<Repository>> getRepositories(final String user, int page) {
        return githubService.getRepositories(user, page).continueWith(task -> {
            if(task.isFaulted()) {
                throw task.getError();
            }
            return repositoryRepository.saveOrUpdate(task.getResult());
        });
    }
}
You can’t perform that action at this time.