JavaRush /Java Blog /Random-KO /어린이를 위한 Android MVP

어린이를 위한 Android MVP

Random-KO 그룹에 게시되었습니다
안드로이드 개발자로서의 여정을 시작했을 때 "모바일 애플리케이션 아키텍처"라는 단어는 나에게 깊은 당혹감을 안겨 주었고, Google과 Habré에 관한 기사는 나를 더욱 큰 우울증에 빠뜨렸습니다. 책을 보지만 아무 것도 볼 수 없습니다. 이 기사를 읽고 계시다면 이미 이 그림을 한 번 이상 연구하고 무슨 일이 일어나고 있는지 이해하려고 노력하셨을 것입니다. 어린이를 위한 Android MVP - 1제 생각에는 모바일 개발에서 아키텍처 접근 방식을 이해하는 문제는 아키텍처 자체의 추상성에 있습니다. 각 개발자는 특정 패턴을 올바르게 구현하는 방법에 대한 자신의 비전을 가지고 있습니다. MVP 구현의 다소 괜찮은 예가 인터넷의 영어권 부문에서 발견되었는데 이는 놀라운 일이 아닙니다. 무엇이 무엇인지 간략하게 살펴보고 예제로 넘어 갑시다. 모델 - 데이터 수준. 나는 "비즈니스 로직"이라는 용어를 사용하는 것을 좋아하지 않기 때문에 내 애플리케이션에서는 이를 저장소라고 부르며 데이터베이스 및 네트워크와 통신합니다. 보기 - 표시 수준. 탬버린과 함께 춤추고 라이프 사이클과 상호작용하는 것을 좋아하지 않는다면 Activity , Fragment 또는 Custom View 가 될 것입니다 . 처음에 모든 Android 애플리케이션은 컨트롤러가 활동 또는 조각인 MVC 구조 에 종속 된다는 점을 상기시켜 드리겠습니다 . Presenter 는 View와 Model 사이의 레이어입니다. View는 발생하는 이벤트를 전송하고, Presenter는 이를 처리하며, 필요한 경우 Model에 액세스하고 렌더링을 위해 데이터를 View에 반환합니다. Android 및 구체적인 예와 관련하여 중요한 부분인 계약을 강조하겠습니다. 위의 구성 요소 간의 모든 상호 작용을 설명하는 인터페이스입니다. 이론적 부분을 요약하면 다음과 같습니다.
  • View는 Presenter에 대해 알고 있습니다.
  • 발표자는 View 및 Model(Repository)에 대해 알고 있습니다.
  • 그 자체로 모델;
  • 계약은 그들 사이의 상호 작용을 관리합니다.
실제로 예제 자체에서는 실험을 단순화하기 위해 버튼을 클릭하여 데이터베이스에서 행을 로드하고 이를 TextView에 표시 합니다 . 예를 들어, 데이터베이스에는 도시 최고의 레스토랑 목록이 포함되어 있습니다. 계약부터 시작해 보겠습니다. 인터페이스를 만들어 보겠습니다 MainContract.
public interface MainContract {
    interface View {
        void showText();
    }

    interface Presenter {
        void onButtonWasClicked();
        void onDestroy();
    }

    interface Repository {
        String loadMessage();
    }
}
지금은 미래 애플리케이션의 3가지 구성 요소와 그 기능을 강조하는 것뿐입니다. 다음으로 리포지토리에 대해 설명하겠습니다.
public class MainRepository implements MainContract.Repository {

    private static final String TAG = "MainRepository";
    @Override
    public String loadMessage() {
        Log.d(TAG, "loadMessage()");
        /** Здесь обращаемся к БД or сети.
         * Я специально ничего не пишу, чтобы не загромождать пример
         * DBHelper'ами и прочими не относяшимеся к теме an objectми.
         * Поэтому я буду возвращать строку Сосисочная =)
         */
        return "Сосисочная у Лёхи»;
    }
}
데이터를 로드하고 언로드하기만 하면 모든 것이 명확해집니다. 다음은 발표자입니다.
public class MainPresenter implements MainContract.Presenter {
    private static final String TAG = "MainPresenter";

    //Компоненты MVP applications
    private MainContract.View mView;
    private MainContract.Repository mRepository;

    //Сообщение
    private String message;


    //Обрати внимание на аргументы конструктора - мы передаем экземпляр View, а Repository просто создаём конструктором.
    public MainPresenter(MainContract.View mView) {
        this.mView = mView;
        this.mRepository = new MainRepository();
        Log.d(TAG, "Constructor");
    }

    //View сообщает, что кнопка была нажата
    @Override
    public void onButtonWasClicked() {
        message = mRepository.loadMessage();
        mView.showText(message);
        Log.d(TAG, "onButtonWasClicked()");
    }

    @Override
    public void onDestroy() {
        /**
         * Если бы мы работали например с RxJava, в этом классе стоило бы отписываться от подписок
         * Кроме того, при работе с другими методами асинхронного андроида,здесь мы боремся с утечкой контекста
         */

        Log.d(TAG, "onDestroy()");
    }
}
제가 탬버린과 함께 춤을 추는 것과 생활주기에 대해 쓴 것을 기억하시나요? Presenter는 View가 작동하는 동안 지속됩니다. 복잡한 사용자 시나리오를 개발할 때 Presenter에서 모든 View 콜백을 복제하고 적절한 순간에 호출하여 Activity/Fragment 수명 주기를 복제하여 무엇이 필요한지 제때 이해하는 것이 좋습니다. 현재 "중간층"에 걸려 있는 데이터를 처리해야 합니다. 마지막으로 보기:
public class MainActivity extends AppCompatActivity implements MainContract.View {

    private static final String TAG = "MainActivity";

    private MainContract.Presenter mPresenter;

    private Button mButton;

    private TextView myTv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Создаём Presenter и в аргументе передаём ему this - эта Activity расширяет интерфейс MainContract.View
        mPresenter = new MainPresenter(this);

        myTv = (TextView) findViewById(R.id.text_view);
        mButton = (Button) findViewById(R.id.button);

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.onButtonWasClicked();
            }
        });
        Log.d(TAG, "onCreate()");
    }

    @Override
    public void showText(String message) {
        myTv.setText(message);
        Log.d(TAG, "showMessage()");
    }

    //Вызываем у Presenter метод onDestroy, чтобы избежать утечек контекста и прочих неприятностей.
    @Override
    public void onDestroy() {
        super.onDestroy();
        mPresenter.onDestroy();
        Log.d(TAG, "onDestroy()");
    }
}
무슨 일이야?
  • View라고도 알려진 Activity는 onCreate()메서드에서 Presenter 인스턴스를 생성하고 생성자에 자신을 전달합니다.
  • Presenter가 생성되면 명시적으로 View를 수신하고 Repository 인스턴스를 생성합니다(단, 싱글톤으로 만들 수도 있음).
  • 버튼을 누르면 View가 발표자를 두드리며 "버튼을 눌렀습니다."라고 말합니다.
  • 발표자는 저장소로 이동합니다. "이 쓰레기를 다운로드해 주세요."
  • 저장소는 "물건"을 로드하고 발표자에게 전달합니다.
  • 발표자는 보기로 전환합니다. "여기에 데이터가 있습니다. 그려보세요."
그게 다야, 얘들 아. PS 구성 요소 간의 책임을 명확하게 설명하는 것이 중요합니다. 예를 들어, 내 교육 프로젝트 중 하나에서 버튼을 클릭하면 데이터베이스의 데이터를 변경해야 했습니다. 모델은 POJO 클래스로 설명되었으며, 화면의 객체에 대한 정보를 담당하는 뷰의 위치에 대한 정보를 전달했으며, Presenter는 목록에서 이 객체를 찾아서 Repository에 기록하도록 보냈습니다. 모든 것이 논리적으로 보입니까? 하지만 내 멘토는 다음과 같이 지적했습니다. 저장소는 쓰기와 읽기만 수행해야 하며, POJO에서 필요한 정보를 꺼내서 무엇을 써야 할지 결정해서는 안 됩니다. 발표자는 녹음 정보만 제공해야 하며 그 이상은 제공하지 않습니다. 아키텍처 구현을 위한 엄격한 프레임워크는 없습니다. 실험하고 시도하고 개인적으로 편리한 것을 찾으십시오. 그리고 코드 리뷰에서 선배 동료들에게 보여주는 것을 잊지 마세요 =) 예제는 GitHub에서 볼 수 있습니다: https://github.com/admitrevskiy/MVP_Example
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION