조금 더 구체적인 안드로이드 개발 패턴화
해당 포스팅에서 언급된 내용은 Ndroid 에서 제공합니다.
안녕하세요. 연휴는 잘 보내고 계신가요?
저는 4일간 연휴 중 조금 마음을 refresh 하고자, 하루는!!
기존 작성하였던 코드를 리뷰해보고, 리팩토링을 하는 시간을 가지게 되었어요.
그래서 오늘 포스팅은 이에 대한 결과물로 조금 더 구체적인 안드로이드 개발의 패턴화에 대해 생각해보는 포스팅을 해보려 합니다. (그냥 일개 초보수준 프로그래머가 작성한 것입니다. ㅡㅡ^)
일단 리팩토링 한다고 해서 아무렇게나 작성을 해보기 보단 지난 포스팅 때 다뤘던 두 가지 사례를 적극적으로 사용해보는 시간을 가졌습니다. 아래의 두 포스팅을 말이죠. :-)
작업을 했던 내용은 다음과 같습니다.
1. Activity에 조금 더 최적화된 EasyWeakReference
코드를 작성하다보면 종종 어떤 일들은 안드로이드의 메인스레드에서 실행되어야할 경우가 많이 존재합니다. 특히 병렬처리를 하려 한다면, 멀티스레드 작업의 결과를 View 에 반영해야하는데 이 작업은 꼭 메인스레드에서 이루어 져야 하는 작업이죠!!
안드로이드에서 이에 접근할 수 있는 방법은 여러 종류가 있지만 저는 그중에서 runOnUiThread 를 주로 애용하는 편입니다.
이 개념을 저는 일전에 제작한 NxWeakReference 에 추가하고 싶었으며(보다 더 쉽게 일하기 위해 ㅡㅡ^), 이를 위해 저는 해당 클래스의 확장체를 만들게 되었습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /** * Activity 전용 약한참조 객체 * * Created by Doohyun on 2017. 1. 28.. */ public class NxActivityWeakReference<T extends Activity> extends NxWeakReference<T> { /** * 단순한 상속에 대한 생성자 제작 * @param referent */ public NxActivityWeakReference(T referent) { super(referent); } /** * 단순한 상속에 대한 생성자 제작 * * @param referent * @param q */ public NxActivityWeakReference(T referent, ReferenceQueue<T> q) { super(referent, q); } /** * Activity 에서 runOnUiThread 로 실행함 * * @param weakReferenceConsumer */ public void runOnUiThread(IWeakReferenceConsumer<T> weakReferenceConsumer) { final T activity = this.get(); if (activity != null) { activity.runOnUiThread(() -> weakReferenceConsumer.accept(activity)); } } } | cs |
간략하게 리뷰를 하자면, 생성자에서 Activity 를 상속받은 인스턴스를 강제로 입력하도록 제작하였으며, runOnUiThread 메소드를 통해 보다 더 쉽게 mainThread 에서 비지니스로직을 수행할 수 있습니다.
2. 안드로이드 MVP 규칙 정의
일전에 한번 JANDI 블로그(http://tosslab.github.io/android/2015/03/01/01.Android-mvc-mvvm-mvp.html)를 통해 protoType 화 했던 MVP 패턴에 대한 규칙을 조금 더 명세하였습니다. 규칙은 다음과 같습니다.
- View 에 해당하는 Activity는 오직 View 자체 변경 메소드만 작성할 것!
View 는 버튼 클릭 등에 의한 이벤트로 부터 해야할 비지니스 로직을 Presenter 로 위임합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public class LoginActivity extends MitActivity { // 하위 View private RelativeLayout ryDialogBg; private LoginLoadingDialog loginLoadingDialog; private Button btnLogin; // Presenter private LoginPresenter loginPresenter; ..... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); loginPresenter = new LoginPresenter(this); // 이벤트에 대한 비지니스 로직을 Presenter 로 위임! btnLogin.setOnClickListener(loginPresenter::clickLoginButton); } /** * View 자체의 변경 메소드 * * 키보드 숨기기 */ public void showLoginLoadingDialog() { loginLoadingDialog.show(); ryDialogBg.setVisibility(View.VISIBLE); } } | cs |
- Presenter 는 View 와 Model 의 연결고리, 즉 기존의 Controller
Presenter 는 View 로 부터의 요청 시, Modeler 로 명령을 위임합니다.
또한 Modeler 로부터 받은 정보를 View 로 반영하기 위해, View 에 정의된 메소드를 직접 실행합니다. 이를 위해 Presenter 는 View 에 대한 약한참조를 가지고 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | public class LoginPresenter implements ILoginServiceObserver { private NxActivityWeakReference <LoginActivity> loginActivityEasyWeakReference; public LoginPresenter(LoginActivity loginActivity) { // Activity 와 약한참조 유지 loginActivityEasyWeakReference = new NxActivityWeakReference <>(loginActivity); } /** * 로그인 시도 * * <pre> * View 인 Activity에서 Click Event로 할 일을 위임한 메소드 * </pre> * @param view */ public void clickLoginButton(View view) { final UserInfo userInfo = getCurrentInputUserInfo(); if (userInfo != null) { // View 변경 메소드 사용 loginActivityMidasWeakReference.run(LoginActivity::showLoginLoadingDialog); // Modeler 로 실제 비지니스 로직 위임. LoginModeler.GetInstance().tryLogin(userInfo); } } } | cs |
Presenter 는 다른 컴포넌트의 옵저버 역할을 수행합니다. 다른 컴포넌트의 알람의 결과(멀티스레드의 콜백결과 등..)로 View 가 변경되야 할 때, 이를 Presenter 로 전달을 하여 View 를 변경시킬 수 있도록 합니다.
- Model 을 담당하는 Modeler 개념 추가
Modeler 는 스프링에 있던 Service 와 같은 역할을 수행합니다. (안드로이드에는 서비스가 있으니 서비스라는 이름은 차마 사용할 수 없었습니다. ㅜㅡㅜ)
Presenter 의 요청으로 내부DB 의 CRUD 를 수행하는 DAO 클래스를 사용하거나, network 작업을 수행합니다. 물론 요청이 콜백이었다면, 데이터를 전달하는 역할도 수행하겠죠?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | public class LoginModeler { /** * 로그인을 시도한다 * * <pre> * userInfo 가 넘어오는 것에 성공했다면 정확한 정보를 보장함! * 유효성 검사 필요 없음 * </pre> * * @param userInfo */ public void tryLogin(@NonNull UserInfo userInfo) { // 실제 비지니스 로직 수행 (네트워크 접근) BindServiceManager.GetInstance().runRMI(iMessengerBinder -> iMessengerBinder.loginProcess(userInfo.getUserId(), userInfo.getPwd())); } /** * 유저 정보를 저장한다. * * <pre> * userInfo 는 null 이 아닌 한 정확한 정보를 보장함! * 유효성 검사 필요 없음 * </pre> * @param userInfo (null 허용) * @return */ public boolean saveUserInfo(@Nullable UserInfo userInfo) { // 실제 비지니스 로직 수행 (DB CRUD) final boolean resultYn; if (userInfo != null) { resultYn = TxManager.GetInstance().runTransaction(() -> { UserInfoServiceManager.GetInstance().insert(userInfo.getUserId(), userInfo.getPwd()); SettingServiceManager.GetInstance().insert(); }); } else { resultYn = false; } return resultYn; } } | cs |
계속해서 이 패턴화 를 견고하게 작업해 볼 생각입니다.
보다 유연하고 질 좋은 코드를 꿈꾸며 말이죠. :-)
이상 설연휴 중 삽질이었습니다. ㅡㅡ^
'개발이야기 > 오픈소스 N series ' 카테고리의 다른 글
예외처리를 간소화하여, 코드를 이쁘게 해보자! (9) | 2017.05.10 |
---|---|
Calendar 를 조금 더 쉽게 써보자, TimeUtil & TimeBuilder (0) | 2017.03.26 |
RxJava2 의 Maybe를 조금 더 적극적으로 써보자! MaybeUtil! (2) | 2017.03.16 |
안드로이드 람다 조합 (RxJava2 호환) (0) | 2017.03.08 |
보다 쉽게 사용가능한 WeakReference! (0) | 2017.01.26 |