※ 이전 포스트 "안드로이드 mvc, mvp, mvvm" 을 보고 이 포스트를 보기를 추천한다. 안 된다면 "안드로이드 mvc, mvp, mvvm" 포스트에서 mvp 라도 봐라.
<구글 Basic MVP>의 구조이다. 왼쪽 REPOSITORY와 Remote data source, Local data source 는 Model 영역이다.
※ <MVP google sample> 예제
위 예제를 본따서 해당 포스트를 만들었다. mvp 구조만 파악할 것이기에 코드의 앞뒤가 안 맞는다는 점 이해 부탁드립니다.
위 프로젝트는 위에 링크 된 구글의 mvp 예제이다. 프로젝트 구조를 보면 각 Activity마다 하나의 패키지를 만들었다. 각 Activity 패키지 안에는 Presenter와 View를 추상화한 Contract 인터페이스, Presenter, View 역할을 하는 Activity 또는 Fragment가 있다. 또한, data 패키지 안 Model을 보면 Remote/Local을 구분하며, Memory cache를 하는 Repository 클래스, 서버를 통해 데이터를 불러오는 Remote 패키지, SQL, Realm 등을 통해 단말기 상의 데이터를 불러오는 Local 패키지로 구성되어 있다.
< Presenter와 View 구현 >
1. Contract 정의
public interface TasksContract { interface View { void setLoadingIndicator(boolean active); void showTasks(List<Task> tasks); void showSuccessfullySavedMessage(); boolean isActive(); } interface Presenter { void result(int requestCode, int resultCode); void loadTasks(boolean forceUpdate); void addNewTask(); void setFiltering(TasksFilterType requestType); void openTaskDetails(Task requestedTask); } }
Contract에는 Presenter와 View를 각각 정의한다. 하나의 interface에 View와 Presenter을 정의하고 이를 각각의 View와 Presenter 클래스에 정의하는 방식이다.
2. Presenter 구현
public class TasksPresenter implements TasksContract.Presenter { private final TasksRepository repository; private final TasksContract.View view; public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) { mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null"); mTasksView = checkNotNull(tasksView, "tasksView cannot be null!"); mTasksView.setPresenter(this); } @Override public void addNewTask() { mTasksView.showAddTask(); } @Override public void clearCompletedTasks() { mTasksRepository.clearCompletedTasks(); mTasksView.showCompletedTasksCleared(); loadTasks(false, false); } @Override public void loadTasks(boolean forceUpdate) { // Simplification for sample: a network reload will be forced on first load. loadTasks(forceUpdate || mFirstLoad, true); mFirstLoad = false; } ........ }
Contract.Presenter 인터페이스를 상속하여 구현했다. Presenter에서 View와 Repository에 접근해야 하므로 생성자를 통해서 View와 Repository 객체를 받았다. 물론 이전 포스트 "안드로이드 mvc, mvvm, mvp" 에 나오는 setView, setRepository 메서드를 사용해도 된다.
3. View 구현
- Activity
public class TasksActivity extends AppCompatActivity { private TasksPresenter mTasksPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tasks_act); ......... TasksFragment tasksFragment = (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (tasksFragment == null) { // Create the fragment tasksFragment = TasksFragment.newInstance(); ActivityUtils.addFragmentToActivity( getSupportFragmentManager(), tasksFragment, R.id.contentFrame); } mTasksPresenter = new TasksPresenter( TasksRepository.getInstance() , tasksFragment); } @Override public void onSaveInstanceState(Bundle outState) { outState.putSerializable(CURRENT_FILTERING_KEY, mTasksPresenter.getFiltering()); super.onSaveInstanceState(outState); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: // Open the navigation drawer when the home icon is selected from the toolbar. mDrawerLayout.openDrawer(GravityCompat.START); return true; } return super.onOptionsItemSelected(item); } ........ }
위와 같이 Fragment를 사용할 시 Activity는 단지 Presenter와 Model, View를 초기화해 연결해주는 역할과 Activity 생명으로써 역할만 한다. Fragment 가 실질적인 View 역할을 하므로 Contract.View는 상속하지 않았다.
- Fragment
public class TasksFragment extends Fragment implements TasksContract.View { private TasksContract.Presenter mPresenter; public TasksFragment() { // Requires empty public constructor } public static TasksFragment newInstance() { return new TasksFragment(); } public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.tasks_frag, container, false); ...... FloatingActionButton fab = (FloatingActionButton) getActivity().findViewById(R.id.fab_add_task); fab.setImageResource(R.drawable.ic_add); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mPresenter.addNewTask(); } }); ..... } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_clear: mPresenter.clearCompletedTasks(); break; case R.id.menu_refresh: mPresenter.loadTasks(true); break; } return true; } @Override public void showAddTask() { Intent intent = new Intent(getContext(), AddEditTaskActivity.class); startActivityForResult(intent, AddEditTaskActivity.REQUEST_ADD_TASK); } @Override public void showCompletedTasksCleared() { showMessage(getString(R.string.completed_tasks_cleared)); } private void showMessage(String message) { Snackbar.make(getView(), message, Snackbar.LENGTH_LONG).show(); } }
View 역할을 하므로 Contract.View 인터페이스를 사속하여 구현했다.
< Model 구현 >
1. DataSource 정의
public interface TasksDataSource { void clearCompletedTasks(); void refreshTasks(); void deleteAllTasks(); void deleteTask(@NonNull String taskId); interface LoadImageCallback { void onImageLoaded(ArrayList<ImageItem> list); } }
위 DataSource는 Repositor와 remote, local 안에 있는 메서드를 공통으로 정의한다.
2. Repository 정의
public class TasksRepository implements TasksDataSource { private static TasksRepository INSTANCE = null; private final TasksDataSource mTasksRemoteDataSource; private final TasksDataSource mTasksLocalDataSource; // Prevent direct instantiation. private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource, @NonNull TasksDataSource tasksLocalDataSource) { mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource); mTasksLocalDataSource = checkNotNull(tasksLocalDataSource); } public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource, TasksDataSource tasksLocalDataSource) { if (INSTANCE == null) { INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource); } return INSTANCE; } }
위 코드를 보면 Repository가 DataSource를 상속받는 걸 알 수 있다. Repository에서 remote와 local을 접근하기 때문에 생성자에서 초기화한다. 나머지 local과 remote도 마찬가지로 DataSource를 상속받아 구현하면 된다.
< 참고 페이지 >
'안드로이드 > 기본' 카테고리의 다른 글
NDK(2) - 안드로이드 스튜디오 NDK JNI 기본 예제 (2) | 2017.07.01 |
---|---|
NDK(1) - JNI, Android.mk (0) | 2017.06.30 |
안드로이드 mvc, mvp, mvvm (0) | 2017.06.09 |
SharedPreferences (0) | 2017.06.04 |
action.Main, category.LAUNCHER (0) | 2017.06.04 |