Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clean Architecture on Android

Tianming
November 22, 2016
1.1k

Clean Architecture on Android

Tianming

November 22, 2016
Tweet

Transcript

  1. Activity Fragment Service Remote Data Source Event Bus Local Data

    Source Straightforward Way (Without Architecture)
  2. Android Architecture Blueprint • todo-mvp/ - Basic Model-View-Presenter architecture. •

    todo-mvp-loaders/ - Based on todo-mvp, fetches data using Loaders. • todo-databinding/ - Based on todo-mvp, uses the Data Binding Library. • todo-mvp-clean/ - Based on todo-mvp, uses concepts from Clean Architecture. • todo-mvp-dagger/ - Based on todo-mvp, uses Dagger2 for Dependency Injection • todo-mvp-contentproviders/ - Based on todo-mvp-loaders, fetches data using Loaders and uses Content Providers • todo-mvp-rxjava/ - Based on todo-mvp, uses RxJava for concurrency and data layer abstraction. https://github.com/googlesamples/android-architecture
  3. public interface TasksDataSource {
 
 interface LoadTasksCallback {
 
 void

    onTasksLoaded(List<Task> tasks);
 
 void onDataNotAvailable();
 }
 
 interface GetTaskCallback {
 
 void onTaskLoaded(Task task);
 
 void onDataNotAvailable();
 }
 
 void getTasks(@NonNull LoadTasksCallback callback);
 
 void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);
 ... 
 }
 Data Layer (Repository Pattern)
  4. public abstract class UseCase<Q extends UseCase.RequestValues, P extends UseCase.ResponseValue> {


    
 private Q mRequestValues;
 
 private UseCaseCallback<P> mUseCaseCallback; 
 ... 
 void run() {
 executeUseCase(mRequestValues);
 }
 
 protected abstract void execute(Q requestValues); ... 
 public interface UseCaseCallback<R> {
 void onSuccess(R response);
 void onError();
 }
 } Use Case
  5. public class GetTasks extends UseCase<GetTasks.RequestValues, GetTasks.ResponseValue> {
 ... 
 @Override


    protected void executeUseCase(final RequestValues values) {
 
 mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() {
 @Override
 public void onTasksLoaded(List<Task> tasks) {
 TasksFilterType currentFiltering = values.getCurrentFiltering();
 TaskFilter taskFilter = mFilterFactory.create(currentFiltering);
 
 List<Task> tasksFiltered = taskFilter.filter(tasks);
 ResponseValue responseValue = new ResponseValue(tasksFiltered);
 getUseCaseCallback().onSuccess(responseValue);
 }
 
 @Override
 public void onDataNotAvailable() {
 getUseCaseCallback().onError();
 }
 });
 
 }
 
 } Use Case
  6. public class TasksPresenter implements TasksContract.Presenter { ... private void loadTasks(boolean

    forceUpdate, final boolean showLoadingUI) 
 
 mUseCaseHandler.execute(mGetTasks, requestValue,
 new UseCase.UseCaseCallback<GetTasks.ResponseValue>() {
 @Override
 public void onSuccess(GetTasks.ResponseValue response) {
 List<Task> tasks = response.getTasks();
 // The view may not be able to handle UI updates anymore
 if (!mTasksView.isActive()) {
 return;
 }
 if (showLoadingUI) {
 mTasksView.setLoadingIndicator(false);
 }
 
 processTasks(tasks);
 }
 
 @Override
 public void onError() {
 // The view may not be able to handle UI updates anymore
 if (!mTasksView.isActive()) {
 return;
 }
 mTasksView.showLoadingTasksError();
 }
 });
 } 
 ... 
 } Presenter
  7. Data Layer (Rxjava) public interface TasksDataSource {
 
 Observable<List<Task>> getTasks();


    
 Observable<Task> getTask(@NonNull String taskId);
 ... 
 }
 

  8. Use Case Rxjava public abstract class RxUseCase<Q extends RxUseCase.RequestValues, P

    extends RxUseCase.ResponseValues> {
 
 protected abstract Observable<P> buildUseCase(Q requestValues);
 
 /**
 * Data passed to a request.
 */
 public interface RequestValues {
 }
 
 /**
 * Data received from a response.
 */
 public interface ResponseValues {
 }
 }

  9. public abstract class SimpleUseCase<Q extends RxUseCase.RequestValues,
 P extends RxUseCase.ResponseValues> extends

    RxUseCase<Q, P> {
 
 protected Scheduler mSubscribeOn;
 protected Scheduler mObserveOn;
 protected Subscription mSubscription;
 
 public SimpleUseCase(Scheduler subscribeOn, Scheduler observeOn) {
 mSubscribeOn = checkNotNull(subscribeOn, "subscribeOn cannot be null!");
 mObserveOn = checkNotNull(observeOn, "observeOn cannot be null!");
 mSubscription = Subscriptions.empty();
 }
 
 public void execute(Q requestValues, Subscriber<P> subscriber) {
 unsubscribe();
 mSubscription = buildUseCase(requestValues)
 .subscribeOn(mSubscribeOn)
 .observeOn(mObserveOn)
 .subscribe(subscriber);
 }
 
 public void unsubscribe() {
 if (!mSubscription.isUnsubscribed()) {
 mSubscription.unsubscribe();
 }
 }
 } Base Use Case (Rxjava)
  10. Use Case(Rxjava) public class GetTasks extends SimpleUseCase<GetTasks.RequestValues, GetTasks.ResponseValues> { ...

    
 @Override
 public Observable<ResponseValues> buildUseCase(final RequestValues values) {
 
 return mTasksRepository.getTasks()
 .flatMap(new Func1<List<Task>, Observable<Task>>() {
 @Override
 public Observable<Task> call(List<Task> tasks) {
 return Observable.from(tasks);
 }
 })
 .filter(new Func1<Task, Boolean>() {
 @Override
 public Boolean call(Task task) {
 switch (values.getCurrentFiltering()) {
 case ACTIVE_TASKS:
 return task.isActive();
 case COMPLETED_TASKS:
 return task.isCompleted();
 case ALL_TASKS:
 default:
 return true;
 }
 }
 })
 .toList()
 .map(new Func1<List<Task>, ResponseValues>() {
 @Override
 public ResponseValues call(List<Task> tasks) {
 return new ResponseValues(tasks);
 }
 });
 } }
  11. public class TasksPresenter implements TasksContract.Presenter { ... private void loadTasks(boolean

    forceUpdate, final boolean showLoadingUI) {
 
 mGetTasks.unsubscribe();
 GetTasks.RequestValues requestValue = new GetTasks.RequestValues(forceUpdate,
 mCurrentFiltering);
 mGetTasks.execute(requestValue, new Subscriber<GetTasks.ResponseValues>() {
 @Override
 public void onCompleted() {
 mTasksView.setLoadingIndicator(false);
 }
 
 @Override
 public void onError(Throwable e) {
 mTasksView.showLoadingTasksError();
 }
 
 @Override
 public void onNext(GetTasks.ResponseValues values) {
 processTasks(values.getTasks());
 }
 });
 } 
 ... 
 } Presenter (Rxjava)
  12. Dagger2 public class SaveTask extends SimpleUseCase<SaveTask.RequestValues, SaveTask.ResponseValues> { 
 Inject

    public SaveTask(@NonNull TasksRepository tasksRepository,
 @NonNull BaseSchedulerProvider schedulerProvider) {
 super(schedulerProvider.io(), schedulerProvider.ui());
 mTasksRepository = tasksRepository; 
 } 

  13. Dagger2 public class AddEditTaskPresenter implements AddEditTaskContract.Presenter {
 
 private final

    AddEditTaskContract.View mAddTaskView;
 
 private final GetTask mGetTask;
 
 private final SaveTask mSaveTask;
 
 @Inject
 public AddEditTaskPresenter(@Nullable String taskId,
 @NonNull AddEditTaskContract.View addTaskView,
 @NonNull GetTask getTask, @NonNull SaveTask saveTask) {
 mTaskId = taskId;
 mAddTaskView = addTaskView;
 mGetTask = getTask;
 mSaveTask = saveTask;
 } }
  14. Turns out: Testability : Super high. Maintainability : High. Ease

    of amending or adding a feature Boilerplate : Lots of classes