mirror of
https://github.com/android10/Android-CleanArchitecture.git
synced 2025-08-14 07:30:44 +08:00
Refactored Interactos and use cases. Added ThreadExecutor. Some modifications to UserListPresenter
This commit is contained in:
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (C) 2014 android10.org. All rights reserved.
|
||||
* @author Fernando Cejas (the android10 coder)
|
||||
*/
|
||||
package com.fernandocejas.android10.sample.data.executor;
|
||||
|
||||
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Decorated {@link java.util.concurrent.ThreadPoolExecutor} Singleton class based on
|
||||
* 'Initialization on Demand Holder' pattern.
|
||||
*/
|
||||
public class JobExecutor implements ThreadExecutor {
|
||||
|
||||
private static class LazyHolder {
|
||||
private static final JobExecutor INSTANCE = new JobExecutor();
|
||||
}
|
||||
|
||||
public static JobExecutor getInstance() {
|
||||
return LazyHolder.INSTANCE;
|
||||
}
|
||||
|
||||
private static final int INITIAL_POOL_SIZE = 3;
|
||||
private static final int MAX_POOL_SIZE = 5;
|
||||
|
||||
// Sets the amount of time an idle thread waits before terminating
|
||||
private static final int KEEP_ALIVE_TIME = 10;
|
||||
|
||||
// Sets the Time Unit to seconds
|
||||
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
|
||||
|
||||
private final BlockingQueue<Runnable> workQueue;
|
||||
|
||||
private final ThreadPoolExecutor threadPoolExecutor;
|
||||
|
||||
private JobExecutor() {
|
||||
this.workQueue = new LinkedBlockingQueue<Runnable>();
|
||||
this.threadPoolExecutor = new ThreadPoolExecutor(INITIAL_POOL_SIZE, MAX_POOL_SIZE,
|
||||
KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, this.workQueue);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @param runnable The class that implements {@link Runnable} interface.
|
||||
*/
|
||||
@Override public void execute(Runnable runnable) {
|
||||
if (runnable == null) {
|
||||
throw new IllegalArgumentException("Runnable to execute cannot be null");
|
||||
}
|
||||
this.threadPoolExecutor.execute(runnable);
|
||||
}
|
||||
}
|
@ -5,12 +5,15 @@
|
||||
package com.fernandocejas.android10.sample.domain.executor;
|
||||
|
||||
/**
|
||||
* UI thread abstraction created to change the execution context from any thread to the UI thread.
|
||||
* Thread abstraction created to change the execution context from any thread to any other thread.
|
||||
* Useful to encapsulate a UI Thread for example, since some job will be done in background, an
|
||||
* implementation of this interface will change context and update the UI.
|
||||
*/
|
||||
public interface MainThread {
|
||||
public interface PostExecutionThread {
|
||||
/**
|
||||
* Causes the {@link Runnable} to be added to the message queue of the Main UI Thread
|
||||
* of the application.
|
||||
*
|
||||
* @param runnable {@link Runnable} to be executed.
|
||||
*/
|
||||
void post(Runnable runnable);
|
@ -12,13 +12,7 @@ import com.fernandocejas.android10.sample.domain.interactor.Interactor;
|
||||
*
|
||||
* Use this class to execute an {@link Interactor}.
|
||||
*/
|
||||
public interface Executor {
|
||||
/**
|
||||
* Executes an {@link Interactor} by creating a new runnable and execute its run() method.
|
||||
* @param interactor The {@link Interactor} to execute.
|
||||
*/
|
||||
void execute(final Interactor interactor);
|
||||
|
||||
public interface ThreadExecutor {
|
||||
/**
|
||||
* Executes a {@link Runnable}.
|
||||
* @param runnable The class that implements {@link Runnable} interface.
|
@ -9,13 +9,24 @@ import com.fernandocejas.android10.sample.domain.exception.ErrorBundle;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
*
|
||||
* This interface represents a execution unit for a use case to get a collection of {@link User}.
|
||||
* By convention this use case (Interactor) implementation will return the result using a Callback.
|
||||
* That callback should be executed in the UI thread.
|
||||
*/
|
||||
public interface GetUserListUseCase extends Interactor {
|
||||
/**
|
||||
* Callback used to be notified when either a users collection has been loaded or an error
|
||||
* happened.
|
||||
*/
|
||||
interface Callback {
|
||||
void onUserListLoaded(Collection<User> usersCollection);
|
||||
void onError(ErrorBundle errorBundle);
|
||||
}
|
||||
|
||||
void getUserList(Callback callback);
|
||||
/**
|
||||
* Executes this user case.
|
||||
*
|
||||
* @param callback A {@link GetUserListUseCase.Callback} used to notify the client.
|
||||
*/
|
||||
void execute(Callback callback);
|
||||
}
|
||||
|
@ -4,35 +4,79 @@
|
||||
*/
|
||||
package com.fernandocejas.android10.sample.domain.interactor;
|
||||
|
||||
import com.fernandocejas.android10.sample.domain.User;
|
||||
import com.fernandocejas.android10.sample.domain.exception.ErrorBundle;
|
||||
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
|
||||
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
|
||||
import com.fernandocejas.android10.sample.domain.repository.UserRepository;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
*
|
||||
* This class is an implementation of {@link GetUserListUseCase} that represents a use case for
|
||||
* retrieving a collection of all {@link User}.
|
||||
*/
|
||||
public class GetUserListUseCaseImpl implements GetUserListUseCase {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final ThreadExecutor threadExecutor;
|
||||
private final PostExecutionThread postExecutionThread;
|
||||
|
||||
public GetUserListUseCaseImpl(UserRepository userRepository) {
|
||||
if (userRepository == null) {
|
||||
private Callback callback;
|
||||
|
||||
/**
|
||||
* Constructor of the class.
|
||||
*
|
||||
* @param userRepository A {@link UserRepository} as a source for retrieving data.
|
||||
* @param threadExecutor {@link ThreadExecutor} used to execute this use case in a background thread.
|
||||
* @param postExecutionThread {@link PostExecutionThread} used to post updates when the use case has been executed.
|
||||
*/
|
||||
public GetUserListUseCaseImpl(UserRepository userRepository, ThreadExecutor threadExecutor,
|
||||
PostExecutionThread postExecutionThread) {
|
||||
if (userRepository == null || threadExecutor == null || postExecutionThread == null) {
|
||||
throw new IllegalArgumentException("Constructor parameters cannot be null!!!");
|
||||
}
|
||||
this.userRepository = userRepository;
|
||||
this.threadExecutor = threadExecutor;
|
||||
this.postExecutionThread = postExecutionThread;
|
||||
}
|
||||
|
||||
@Override public void execute(Callback callback) {
|
||||
if (callback == null) {
|
||||
throw new IllegalArgumentException("Interactor callback cannot be null!!!");
|
||||
}
|
||||
this.callback = callback;
|
||||
this.threadExecutor.execute(this);
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
|
||||
this.userRepository.getUserList(this.repositoryCallback);
|
||||
}
|
||||
|
||||
@Override public void getUserList(Callback callback) {
|
||||
//doing some fake job
|
||||
try {
|
||||
Thread.sleep(6000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
private final UserRepository.UserListCallback repositoryCallback =
|
||||
new UserRepository.UserListCallback() {
|
||||
@Override public void onUserListLoaded(Collection<User> usersCollection) {
|
||||
notifyGetUserListSuccessfully(usersCollection);
|
||||
}
|
||||
// should this be in a new thread?
|
||||
callback.onUserListLoaded(Collections.unmodifiableCollection(Collections.EMPTY_LIST));
|
||||
|
||||
@Override public void onError(ErrorBundle errorBundle) {
|
||||
notifyError(errorBundle);
|
||||
}
|
||||
};
|
||||
|
||||
private void notifyGetUserListSuccessfully(final Collection<User> usersCollection) {
|
||||
this.postExecutionThread.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
callback.onUserListLoaded(Collections.unmodifiableCollection(usersCollection));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void notifyError(final ErrorBundle errorBundle) {
|
||||
this.postExecutionThread.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
callback.onError(errorBundle);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -5,13 +5,14 @@
|
||||
package com.fernandocejas.android10.sample.domain.interactor;
|
||||
|
||||
/**
|
||||
* Common interface for an interactor declared in the application.
|
||||
* This interface represents a execution unit for different use cases.
|
||||
* Common interface for an Interactor {@link java.lang.Runnable} declared in the application.
|
||||
* This interface represents a execution unit for different use cases (this means any use case
|
||||
* in the application should implement this contract).
|
||||
*
|
||||
* By convention each interactor implementation will return the result using a Callback should be
|
||||
* executed in the UI thread.
|
||||
* By convention each Interactor implementation will return the result using a Callback that should
|
||||
* be executed in the UI thread.
|
||||
*/
|
||||
public interface Interactor {
|
||||
public interface Interactor extends Runnable {
|
||||
/**
|
||||
* Everything inside this method will be executed asynchronously.
|
||||
*/
|
||||
|
@ -17,7 +17,6 @@ public interface UserRepository {
|
||||
*/
|
||||
interface UserCallback {
|
||||
void onUserLoaded(User user);
|
||||
|
||||
void onError(ErrorBundle errorBundle);
|
||||
}
|
||||
|
||||
@ -25,8 +24,7 @@ public interface UserRepository {
|
||||
* Callback used to be notified when either a user list has been loaded or an error happened.
|
||||
*/
|
||||
interface UserListCallback {
|
||||
void onUserListLoaded(Collection<User> user);
|
||||
|
||||
void onUserListLoaded(Collection<User> usersCollection);
|
||||
void onError(ErrorBundle errorBundle);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (C) 2014 android10.org. All rights reserved.
|
||||
* @author Fernando Cejas (the android10 coder)
|
||||
*/
|
||||
package com.fernandocejas.android10.sample.presentation;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
|
||||
|
||||
/**
|
||||
* MainThread (UI Thread) implementation based on a Handler instantiated with the main
|
||||
* application Looper.
|
||||
*/
|
||||
public class UIThread implements PostExecutionThread {
|
||||
|
||||
private static class LazyHolder {
|
||||
private static final UIThread INSTANCE = new UIThread();
|
||||
}
|
||||
|
||||
public static UIThread getInstance() {
|
||||
return LazyHolder.INSTANCE;
|
||||
}
|
||||
|
||||
private final Handler handler;
|
||||
|
||||
private UIThread() {
|
||||
this.handler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the Runnable r to be added to the message queue.
|
||||
* The runnable will be run on the main thread.
|
||||
*
|
||||
* @param runnable {@link Runnable} to be executed.
|
||||
*/
|
||||
@Override public void post(Runnable runnable) {
|
||||
handler.post(runnable);
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 android10.org. All rights reserved.
|
||||
* @author Fernando Cejas (the android10 coder)
|
||||
*/
|
||||
package com.fernandocejas.android10.sample.presentation.presenter;
|
||||
|
||||
import com.fernandocejas.android10.sample.domain.interactor.Interactor;
|
||||
|
||||
/**
|
||||
* Abstract class representing a Presenter in a model view presenter (MVP) pattern.
|
||||
*/
|
||||
public abstract class BasePresenter {
|
||||
/**
|
||||
* Method that control the lifecycle of the view. It should be called in the view's
|
||||
* (Activity or Fragment) onResume() method.
|
||||
*/
|
||||
public abstract void resume();
|
||||
|
||||
/**
|
||||
* Method that control the lifecycle of the view. It should be called in the view's
|
||||
* (Activity or Fragment) onPause() method.
|
||||
*/
|
||||
public abstract void pause();
|
||||
|
||||
/**
|
||||
* Executes an {@link Interactor} asynchronously in a new thread.
|
||||
* @param interactor The {@link Interactor} to execute.
|
||||
*/
|
||||
protected void executeInteractorAsync(Interactor interactor) {
|
||||
if (interactor == null) {
|
||||
throw new IllegalArgumentException("Interactor to execute cannot be null");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (C) 2014 android10.org. All rights reserved.
|
||||
* @author Fernando Cejas (the android10 coder)
|
||||
*/
|
||||
package com.fernandocejas.android10.sample.presentation.presenter;
|
||||
|
||||
/**
|
||||
* Interface representing a Presenter in a model view presenter (MVP) pattern.
|
||||
*/
|
||||
public interface Presenter {
|
||||
/**
|
||||
* Method that control the lifecycle of the view. It should be called in the view's
|
||||
* (Activity or Fragment) onResume() method.
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* Method that control the lifecycle of the view. It should be called in the view's
|
||||
* (Activity or Fragment) onPause() method.
|
||||
*/
|
||||
void pause();
|
||||
}
|
@ -13,10 +13,7 @@ import com.fernandocejas.android10.sample.presentation.model.UserModel;
|
||||
import com.fernandocejas.android10.sample.presentation.view.UserListView;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class UserListPresenter extends BasePresenter {
|
||||
public class UserListPresenter implements Presenter {
|
||||
|
||||
private final UserListView viewListView;
|
||||
private final GetUserListUseCase getUserListUseCase;
|
||||
@ -36,9 +33,7 @@ public class UserListPresenter extends BasePresenter {
|
||||
this.loadUserList();
|
||||
}
|
||||
|
||||
@Override public void pause() {
|
||||
//nothing to do here
|
||||
}
|
||||
@Override public void pause() {}
|
||||
|
||||
/**
|
||||
* Loads all users.
|
||||
@ -78,7 +73,7 @@ public class UserListPresenter extends BasePresenter {
|
||||
}
|
||||
|
||||
private void getUserList() {
|
||||
this.getUserListUseCase.getUserList(userListCallback);
|
||||
this.getUserListUseCase.execute(userListCallback);
|
||||
}
|
||||
|
||||
private final GetUserListUseCase.Callback userListCallback = new GetUserListUseCase.Callback() {
|
||||
|
@ -24,7 +24,7 @@ public abstract class BaseFragment extends Fragment {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the {@link com.fernandocejas.android10.sample.presentation.presenter.BasePresenter}
|
||||
* Initializes the {@link com.fernandocejas.android10.sample.presentation.presenter.Presenter}
|
||||
* for this fragment in a MVP pattern used to architect the application presentation layer.
|
||||
*/
|
||||
abstract void initializePresenter();
|
||||
|
@ -13,12 +13,16 @@ import android.widget.Button;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.Toast;
|
||||
import com.fernandocejas.android10.sample.data.entity.mapper.UserEntityDataMapper;
|
||||
import com.fernandocejas.android10.sample.data.executor.JobExecutor;
|
||||
import com.fernandocejas.android10.sample.data.repository.UserDataRepository;
|
||||
import com.fernandocejas.android10.sample.data.repository.datasource.UserDataStoreFactory;
|
||||
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
|
||||
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
|
||||
import com.fernandocejas.android10.sample.domain.interactor.GetUserListUseCase;
|
||||
import com.fernandocejas.android10.sample.domain.interactor.GetUserListUseCaseImpl;
|
||||
import com.fernandocejas.android10.sample.domain.repository.UserRepository;
|
||||
import com.fernandocejas.android10.sample.presentation.R;
|
||||
import com.fernandocejas.android10.sample.presentation.UIThread;
|
||||
import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
|
||||
import com.fernandocejas.android10.sample.presentation.model.UserModel;
|
||||
import com.fernandocejas.android10.sample.presentation.presenter.UserListPresenter;
|
||||
@ -65,10 +69,18 @@ public class UserListFragment extends BaseFragment implements UserListView {
|
||||
// LEARNING EXAMPLE PURPOSE.
|
||||
UserDataStoreFactory userDataStoreFactory = new UserDataStoreFactory(this.getContext());
|
||||
UserEntityDataMapper userEntityDataMapper = new UserEntityDataMapper();
|
||||
|
||||
UserRepository userRepository = UserDataRepository.getInstance(userDataStoreFactory,
|
||||
userEntityDataMapper);
|
||||
GetUserListUseCase getUserListUseCase = new GetUserListUseCaseImpl(userRepository);
|
||||
|
||||
ThreadExecutor threadExecutor = JobExecutor.getInstance();
|
||||
PostExecutionThread postExecutionThread = UIThread.getInstance();
|
||||
|
||||
GetUserListUseCase getUserListUseCase = new GetUserListUseCaseImpl(userRepository,
|
||||
threadExecutor, postExecutionThread);
|
||||
|
||||
UserModelDataMapper userModelDataMapper = new UserModelDataMapper();
|
||||
|
||||
this.userListPresenter = new UserListPresenter(this, getUserListUseCase, userModelDataMapper);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user