РИТ++ 2017, AppsConf
Зал Касабланка, 6 июня, 16:00
Тезисы:
http://appsconf.ru/2017/abstracts/2704.html
В последнее время паттерн MVP будоражит Android-комьюнити. Уже есть несколько довольно приличных библиотек, которые помогают использовать этот подход. Но с ними вам придётся писать много boilerplate-кода. Поэтому я хочу познакомить вас с Moxy. Покажу, как использовать её компоненты для решения задач, которые будут вставать перед вами, когда вы решите использовать паттерн MVP. И расскажу, как устроены эти компоненты, и почему именно так, чтобы вы не боялись использовать Moxy из-за потенциальных подводных камней.
21. Приложение запускается
Проходит 2 секунды
На экране отображается количество оставшихся секунд
Отображается сообщение в виде AlertDialog
21
22. public class MainActivity extends MvpAppCompatActivity implements HelloWorldView {
private AlertDialog mMessageDialog;
@Override
public void showMessage(int message) {
mMessageDialog = new AlertDialog.Builder(this)
.setTitle(R.string.app_name).setMessage(message)
.setOnDismissListener(dialog -> mHelloWorldPresenter.onDismissMessage())
.show();
}
@Override
public void hideMessage() {
if (mMessageDialog != null) {
mMessageDialog.dismiss();
}
}
22
23. public class MainActivity extends MvpAppCompatActivity implements HelloWorldView {
private AlertDialog mMessageDialog;
@Override
public void showMessage(int message) {
mMessageDialog = new AlertDialog.Builder(this)
.setTitle(R.string.app_name).setMessage(message)
.setOnDismissListener(dialog -> mHelloWorldPresenter.onDismissMessage())
.show();
}
@Override
public void hideMessage() {
if (mMessageDialog != null) {
mMessageDialog.dismiss();
}
}
23
24. 06-02 04:59:00.178 14192-14192/com.arellomobile.mvp.sample.appsconf E/WindowManager:
android.view.WindowLeaked: Activity com.arellomobile.mvp.sample. MainActivity has leaked
window DecorView@c59996b[] that was originally added here
at android.view.ViewRootImpl.<init>(ViewRootImpl.java:418)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:331)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at android.app.Dialog.show(Dialog.java:322)
at android.support.v7.app.AlertDialog$Builder.show(AlertDialog.java:956)
...
24
34. • В Activity отображается 2 Fragment
• Каждый Fragment содержит счётчик нажатий на кнопку
• Изменения показаний счётчика одного фрагмента никак не
влияют на показания счётчика другого фрагмента
34
35. public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.frame_1, getFragment(0xffFF80AB))
.add(R.id.frame_2, getFragment(0xffCCFF90))
.commit();
}
}
private Fragment getFragment(int color) {
CounterFragment fragment = new CounterFragment();
Bundle args = new Bundle();
args.putInt("argColor", color);
fragment.setArguments(args);
return fragment;
}
}
35
36. @InjectViewState
public class CounterPresenter extends MvpPresenter<CounterView> {
private int mCount;
public CounterPresenter() {
getViewState().showCount(mCount);
}
public void onPlusClick() {
mCount++;
getViewState().showCount(mCount);
}
}
36
44. В Activity отображается 2 счётчика
Каждый счётчик является CustomView
Изменения показаний одного счётчика никак не влияют на
показания другого счётчика
44
54. @InjectViewState
public class DetailsPresenter extends MvpPresenter<DetailsView> {
public DetailsPresenter(long newsId) {
getViewState().showDetails("Details of "" + newsId + """);
}
}
public interface DetailsView extends MvpView {
void showDetails(String details);
}
54
55. public class DetailsActivity extends MvpAppCompatActivity implements DetailsView {
@InjectPresenter
DetailsPresenter mDetailsPresenter;
@ProvidePresenter
DetailsPresenter provideDetailsPresenter() {
return new DetailsPresenter(getIntent().getLongExtra("extraDetailsId", 0));
}
@Override
public void showDetails(String details) {
Log.i(DetailsActivity.class.getSimpleName(), details);
}
}
55
56. 0. Annotation processor: Generate PresenterFields
1. MvpDelegate: onCreate(savedInstanceState)
2. MvpDelegate: Init delegate tag
3. MvpProcessor: Collect all PresenterField for MvpDelegate
4. MvpProcessor: Init each PresenterField
1. MvpProcessor: Generate presenter tag
2. PresenterStore: Get MvpPresenter by type and tag
3. MvpProcessor: MvpPresenter exists?
1. True:
1. MvpProcessor: Init presenter field of Delegated
2. False:
1. PresenterField: Provide presenter
2. PresenterStore: Save presenter
3. MvpProcessor: Init presenter field of Delegated
56
57. 0. Annotation processor: Generate ViewState
1. MvpPresenter: Construct
2. Binder: Bind presenter
3. Binder: Find ViewState for MvpPresenter
4. Binder: Create ViewState
5. Binder: Set ViewState to MvpPresenter
57
58. 1. MvpPresenter: Send command
2. ViewState: Instantiation of ViewCommand
3. ViewState: Get StateStrategy of ViewCommand
4. StateStrategy: Called beforeApply(currentState, incomingCommand)
5. ViewState: Have a Views?
1. False: –
2. True:
1. ViewCommand: Apply to each Views
2. StateStrategy: Called afterApply(currentState, incomingState)
6. ViewState: Attached View
7. ViewState: Apply each ViewCommands
1. ViewCommand: Apply to attached View
2. StateStrategy: Called afterApply(currentState, incomingState)
58
60. 1. Нет проблем с жизненным циклом
2. Boilerplate-code генерируется в compile time
3. Можно использовать несколько Presenter в одном месте
4. Можно любой компонент превратить в MvpView
Присоединяйтесь к проекту на github.com!
PS: https://github.com/senneco/MoxyCases
60