JavaRush /Java блогу /Random-KY /Кичинекейлер үчүн Androidдеги MVP
Алексей Дмитревский
Деңгээл
Москва

Кичинекейлер үчүн Androidдеги MVP

Группада жарыяланган
Мен Android иштеп чыгуучусу катары саякатымды баштаганда, "Мобилдик тиркемелердин архитектурасы" деген сөздөр мени катуу таң калтырды, Google жана Хабредеги макалалар мени ого бетер депрессияга алып келди - мен китепти карасам, эч нерсе көргөн жокмун. Менимче, эгер сиз бул макаланы окуп жатсаңыз, анда сиз бул сүрөттү бир нече жолу изилдеп, эмне болуп жатканын түшүнүүгө аракет кылгансыз: Кичинекейлер үчүн Androidдеги MVP - 1Мобилдик өнүгүүдө архитектуралык мамилени түшүнүү маселеси, менин оюмча, архитектуранын абстракттуулугунда. Ар бир иштеп чыгуучунун тигил же бул үлгүнү кантип туура ишке ашыруу боюнча өзүнүн көз карашы бар. MVP ишке ашыруунун аздыр-көптүр татыктуу мисалдары Интернеттин англис тилдүү секторунда табылган, бул таң калыштуу эмес. Келгиле, эмне экенин кыскача карап көрөлү жана бир мисалга өтөбүз. Модель - маалымат деңгээли. Мен "бизнес логикасы" деген терминди колдонгонду жактырбайм, андыктан менин тиркемелеримде мен аны Репозиторий деп атайм жана ал маалымат базасы жана тармак менен байланышат. Көрүү — дисплей деңгээли. Бул Activity , Fragment же Custom View болот , эгерде сиз дап менен бийлегенди жана жашоо цикли менен өз ара аракеттенүүнү жактырбасаңыз. Эске сала кетейин, алгач бардык Android тиркемелери MVC түзүмүнө баш ийген , мында Controller Activity же Fragment болуп саналат . Алып баруучу - Көрүнүш менен Моделдин ортосундагы катмар. Көрүнүш өзүнө келип түшкөн окуяларды өткөрүп берет, алып баруучу аларды иштетет, зарыл болсо, Моделге кире алат жана көрсөтүү үчүн Көрүнүшкө маалыматтарды кайтарат. Android жана конкреттүү мисал менен байланыштуу, мен маанилүү бөлүгүн баса белгилей кетүү - Келишим. Бул жогоруда аталган компоненттердин ортосундагы бардык өз ара аракеттенүүнү сүрөттөгөн интерфейс. Теориялык бөлүгүн жыйынтыктоо үчүн:
  • Көрүү Алып баруучу жөнүндө билет;
  • Алып баруучу Көрүү жана Модель (Репозиторий) жөнүндө билет;
  • өзүнчө модели;
  • Келишим алардын ортосундагы өз ара мамилелерди жөнгө салат.
Чынында, мисалдын өзү, эксперименттин жөнөкөйлүгү үчүн, баскычты чыкылdateу менен, биз маалымат базасынан сапты жүктөйбүз жана аны 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()");
    }
}
Мен дап менен бийлөө жана жашоо цикли жөнүндө жазганым эсиңдеби? Алып баруучу өзүнүн Көрүнүшүнө жараша жашайт, татаал колдонуучу сценарийлерин иштеп чыгууда, мен сизге Алып баруучудагы бардык View кайра чалууларды кайталоону жана аларды өз убагында түшүнүү үчүн Активдүүлүк/Фрагменттин жашоо циклин кайталап, тийиштүү учурларда чалууну сунуштайм. Учурда “аралык катмарда” orнип турган маалыматтар менен аткарылышы керек. Акыр-аягы, көрүү:
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()");
    }
}
Эмне болуп жатат?
  • Көрүнүш катары да белгилүү болгон активдүүлүк onCreate()методдо Презентациянын инстанциясын түзүп, анын конструкторуна өтөт.
  • Алып баруучу түзүлгөндө, ал ачык түрдө Көрүнүштү кабыл алат жана Репозиторийдин инстанциясын түзөт (айтмакчы, аны Singleton кылса болот)
  • Бир баскыч басылганда, Көрүнүш алып баруучуну тыкылдатып: "Баскылды" дейт.
  • Алып баруучу Репозиторийге кайрылат: "Мага бул келесоону жүктөп алыңыз."
  • Репозиторий "заттарды" жүктөйт жана алып баруучуга жеткирет.
  • Алып баруучу Көрүнүшкө кайрылат: "Мына маалымат сиз үчүн, аны тартыңыз"
Болду, балдар. PS Компоненттердин ортосундагы жоопкерчorкти так аныктоо маанилүү. Мисалы, менин окутуу долбоорлорумдун биринде, баскычты басканда, маалымат базасындагы маалыматтарды өзгөртүү керек болчу. Модель POJO классы тарабынан сүрөттөлгөн, мен экрандагы an object жөнүндө маалымат үчүн жооптуу болгон көрүнүштүн жайгашкан жери жөнүндө маалыматты өткөрүп бердим, Алып баруучу бул an objectти тизмеден издеп, аны репозиторийге жазуу үчүн жөнөттү. Баары логикалуу окшойт? Бирок менин устатым төмөнкүлөргө көңүл бурду: Репозиторий ГАНА жазуу жана окуу менен алектениши керек, ал POJOдан керектүү маалыматты чыгарбашы керек жана эмне жазуу керектигин чечиши керек. Алып баруучу ага жаздыра турган маалыматты гана бериши керек, андан башка эч нерсе жок. Архитектураны ишке ашыруу үчүн эч кандай катуу алHow жок: эксперимент, аракет, жеке өзүң үчүн ыңгайлуу болгон нерсени изде. Кодду карап чыгууда улук жолдошторуңузду көрсөтүүнү унутпаңыз =) Мисал GitHub сайтында жеткorктүү: https://github.com/admitrevskiy/MVP_Example
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION