Продолжение на режиме Android рамочного (1) вход на -MVP это один вводит демонстрационной MVP проблем, и как улучшить. С другой стороны, я представлю проблемы MVP реализации склонной выявить детали MVP в примечательны. С помощью этой статьи, вы будете знать, как лучше использовать MVP. (Эта статья должна иметь определенный фундамент RxJava, если не пропустить наблюдая за несколько последних предложений)
Оптимизация использования библиотеки с открытым исходным кодом
для улучшения читаемости асинхронной логики
В Android, многопоточный асинхронный код, как правило, неизбежно, например, для того, чтобы обеспечить вызов TextView View SetText в основном потоке, слой есть код
public class View extends Activity implements IView{
@override
public void setData(String data){
//保证对View的修改运行在主线程上
runOnUiThread(new Runnable(){ //ugly
public void run(){
text.setText(data);
}
}
);
}
}
Другой пример может служить Модель слой для того, чтобы избежать длительных операций в основном потоке.
public class Model implements IModel{
void getData(ICallback callback){
execute(new Runnable(){
public void run(){ //ugly
... //这里是耗时操作
callback.onResult("hello world"); //10 返回数据
}
};
}
}
Выше является использование многопоточных Java первоначальной редакции написано. Конечно, такой простой код , написанный таким образом , это не проблема. Однако, когда на самом деле используется в проекте, вы можете рассмотреть ряд других вопросов:
1. Обработка ошибок. Данные модели могут поступать из ненадежных источников данных, такие как сети, то вы должны добавить дополнительную обработку ошибок коды
2. переключателя потока. Например, когда определенное увеличение функции APP, некоторая Работница перегружена, нить Набор распределения в некоторых задачах на другие потоки для выполнения.
3. отменить операцию. После запроса данных от модели, но модель возврата к предыдущим данным, из - за какие - то изменения в состоянии View, (например, ключевой деятельность дома на задний план), то ему не нужно , чтобы получить эти данные, мы должны остановить эти данные в View.
При добавлении к существующим кодам логики, асинхронный код раздутый.
Так, в MVP должно быть , как решить эту проблему?
1. Распределение обязанностей в полной резьбой Presenter
Выступающий выбраны в расПредеЛении потока, потому что посредник с Model View Presenter есть, можно извлечь каждую асинхронную логику из кода с Типовым видом, унифицированной в классе (Presenter) для облегчения технического обслуживания. Так, на одном из демо должны быть скорректированы следующим образом:
//Model层
public class Model implements IModel{
String getData(){ //不需要Callback,因为逻辑已经一到Presenter中
... //耗时操作
return "hello world"; //删除异步逻辑,调整到Presenter中
}
}
//View 层
public class View extends Activity implements IView{
@override
public void setData(String data){
text.setText(data); //删除异步逻辑,调整到Presenter中
}
}
//Presenter 层
public class Presenter implements IPresenter{
...
@override
public void performOnClick(){
execute(new Runnable(){
public void run(){ //ugly!!! 从Model中移来的异步代码
... //这里是耗时操作
String data=model.getData(); //不需要Callback类,直接返回
String dataFromPresenter=data+" from presenter"; //8 加工数据
runOnUiThread(new Runnable(){ //ugly!!!从View中移来的异步代码
public void run(){
view.setData(dataFromPresenter);
}
}
);
}
};
}
}
}
Можно видеть , чтобы перейти к заднему Presenter асинхронной логике, существует два основных изменений
1. классы Model.Callback которые больше не нужны, непосредственно обратно к данным , полученных после модели
2.View асинхронного кода на мобильный слой Выступающих моделей, Выступающий может контролировать Модель, Вид исполнения потоков
на это, мы должны двигаться Presenter асинхронной логики управления, этот шаг является главным образом для подготовки к следующему этапу RxJava, цель состоит в том, чтобы позволить проточный RxJava не списаны.
2. Оптимизация RxJava вложенных логики Presenter
Выступающий с кодом реализации RxJava относительно проста, понять RxJava может изменить следующий код:
//Presenter 层
public class Presenter implements IPresenter{
...
@override
public void performOnClick(){
//RxJava将原本栈式的逻辑改成流式的逻辑,按顺序略读即可,当然你也可以把生成Observable得代码移动到Model中。
Observable
.create(new OnSubscribe<String>(){
public void call(Subscriber<? super String> subscriber){ //调用Model
String data=model.getData();
subscriber.onNext(data);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io()) //配置调用Model的线程
.observeOn(AndroidSchedulers.mainThread()) //配置调用View的线程
.subscribe(new Subscriber<String>(){
@Override
public void onCompleted(){}
@Override
public void onError(Throwable throwable){} //错误监听,当然在这里永远调用不到
@Override
public void onNect(String s){
view.setData(dataFromPresenter); //调用View
}
});
}
}
Устранить связь
Несмотря на предыдущей статье, мы были в соответствии с принципом Dependency Inversion использовать IView, IPresenter, IModel легко изменить интерфейс таким образом, чтобы слои MVP. Но проблема сцепления все еще существует. Мы должны расширить анализ Вид слоя, слой Presenter
//View层
private class ViewA implements IView{
private IPresenter mPresenter=new PresenterA(this);// 1
...
}
//Presenter
private class PresenterA implments IPresenter{
private IView mView;
public PresenterA(IView view){
mView=view;
}
...
}
Ибо в 1, мы предполагаем , что один день NA PresenterA, чтобы использовать PresenterB, нам просто нужно положить PresenterB, это кажется очень простым.
Тем не менее, в иерархии, каждый слой должен быть независимым. Изменить Presenter слой полностью не должно влиять на вид, но в этом случае, мы должны изменить презентатор слой не имеет никакого выбора , кроме как одновременно изменить вид слой, слой с Presenter остается связанным, конечно, Presenter с моделью есть такая проблема.
Решение требует больше , чем зависимость от помощи инструмента инжекции тока основного инъекционной зависимости инструмента Dagger2, поддерживается Google. Если вы не знаете Dagger2, вы можете посмотреть на этот популярный с открытым исходным кодом Android инструменты (1) -Dagger2 запись
поддерживать
Для Android, процесс даже убить, когда состояние активности, активность по-прежнему сохраняется, если пользователь не закроет субъективную активность. Presenter, сама модель Android не доступна, мы должны добавить дополнительный код, чтобы обеспечить их успех в процессе перезапуска процесса сохранения состояния.
Автоматический перезапуск
Система Активность Активность будет перезагружен в следующих случаях:
1. Активность в экран флип
2. активность длительное время (30 минут) , а затем в задней части деятельности (2.3 или старше)
3. Недостаточный памяти, активность была убита а затем открыть активность
в этом случае перезагрузите активность будет новый экземпляр и будет использовать savedInstanceState восстановлен в состояние до перезагрузки.
Активность Android рамки , чтобы обеспечить возможность выйти за рамки процесса восстановления, а также активность и часто мы в течение ПМК V. Если мы не будем делать что - то особенное в то же время процесс PM, а затем перезапустить , когда активность, несогласованные MVP три состояния.
Чтобы проиллюстрировать эту несообразность, простой пример: TextView изначально равна нулю, нажмите кнопку скачать, поверните нить слой загрузки Presenter, каждый второй слой приобретает ход загрузки от модели TextView и отображается, а затем нажмите на кнопку домой , чтобы фон, из памяти, активность была убита после рестарта активности, активность из - за savedInstanceState, индикатор остается в процессе расположения убит, но нашел прогресс бар не двигается. Это происходит потому , что перезапуск активности, Presenter нить слой повторно не начало.
сами
Для решения вышеуказанных проблем, мы должны положить бегущий поток связан с номером идентификатора, хранящимся в savedInstanceState, процесс будет перезапущен, когда воскрешение соответствующего числа идентификатора потока, я писал в этих общих идеях, деталь, участвующая в RxJava утилизации :( используется не писать RxJava)
// Общая идея состоит, чтобы просмотреть входящее событие соответствует ИНТУ Id типа, депозит в размере Id, чтобы сохранить это событие, Id держать этот процесс до смерти, перезапустить процесс, читая это Id перезагружать это событие.
public class Presenter implements IPresenter{
//注册一些可能重启进程后可能被重启的线程 (id,Thread)
private SparseArray<Function0<Subscription>> mRestartables=new SparseArray<>();
//运行过的线程ID
private List<Integer> mRunningIds=new ArraysList<>();
//运行过的线程
private SparseArray<Subscription> mReqeusts=new SparseArray<>();
...
public void onCreate(Bundle savedInstanceState){
//注册线程,绑定从DONWLOAD到线程的映射
mRestartables.put(DOWNLOAD_ID,new Function0<Subscription>(){
Subscription call(){
return mModel
.getDownloadObservable()
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(()->)
.subscribe(new Action1<Integer>(){
public void call(Integer progress){
mView.setProgress();
}
});
}
});
//Activity重启,可能有未完成线程可以在这里得到重启
if (savedInstanceState==null){
for (int id:savedInstanceState.getIntArrayList(KEY_RUNNINGS)){
mRestartables.get(id).call();
mRunningIds.add(id);
mReqeusts.add(id);
}
}
}
public void onSaveInstanceState(Bundle outState){
//保存未完成的线程,进程重启后能够自动重启这部分线程
for (int id:mRunningIds){
if (mRequests.get(id).isUnsubcribed()){
mRunningIds.remove((Integer)id);
}
}
outState.put(KEY_RUNNINGS,mRunningIds);
}
public void startDownload(){
//有下载任务时候,启动线程,并保留线程索引
Subscription downloadSubscription=mRestartables.get(DOWNLOAD_ID).call();
mRequests.put(DOWNLOAD_ID,downloadSubscription);
}
}
После более чем достаточно громоздкий процесс, поток перезапускается, но есть еще одна проблема:
активность переворачивать экран долгое время назад при повторном входе в активность, активность будет воссоздавать, это откроет новую нить вручную повторно не используется , прежде чем нить, потому что они не могут получить ссылку на последнюю нить и начать работать. Это приведет к серьезным проблемам: с одной стороны, если старая нить перед вами не закрыта, что старой нить держит старый View вызывает утечку памяти, с другой стороны, даже до того , как поток может быть включен, поток не постоянно перезагружать нужно, трата ресурсов.
Таким образом, я рекомендую Presenter будет делать RetainFragment, следующие формы:
//Presenter变成Retained Fragment,你将不需要再View中显式调用Presenter.onCreate();
public class Presenter extends Fragment implment IPresenter{
...
public onCreate(Bundlet bundle){
setRetainInstance(true);
}
}
public class View extends Activity implmetns IView{
private IPresenter mPresenter=new Presenter(this);
public void onCreate(Bundle b){
super.onCreate(b);
//添加Presenter,将不需要再View中显式调用Presenter.onCreate()
getFragmentManager().beginTransaction().put((Fragment)mPresenter,"Presenter").commit();
}
}
После такой настройки изменяется (поворот экрана, и т.д.) не приведет к потоку перезагрузки.
Быстрое решение
Да, я должен признать, что вышеупомянутое решение хлопотно, рамочная настоятельная необходимость решить эту проблему происходит. Иностранец написал эту структуру, что я не вижу ничего плохого места (там перед заправкой нити через ошибку я упомянул имя вопроса иностранца был решен). Эта структура называется Nucleus, я использую и рекомендую его всем.
Другие подробности рекомендаций
1. Конструкция Вид интерфейс, когда необходимо рассмотреть этот интерфейс, просто чтобы посмотреть интерфейс для передачи данных и не позволить Presenter контролировать, но и обратить внимание Presenter только получить данные из модели, например:
public interface IView{
void startSomeActivity(); //错,Presenter不能控制View,只能设置数据
void showTextView(); //错,Presenter不能控制View,只能设置数据
void setActivityPrepareData(DownloadInfo info);//对
void setViewAdapterData(List<Data> listData);//对
void showToast(int resId);//错 ResourceId不是从Model层中获取,View选取哪个ResourceId来显示是View自己的责任
}
2.Presenter разъединить два на два MV, лучше не иметь другие обязанности ,
а) организовать и сохранить логическую нить
б) получения от слоя модели Модели POJO POJO или по существу превращаются в элементе данных View слой, до и после того, как два внимания POJO должна быть разной. Model View слой не используется непосредственно POJO
3. Назовите слои интерфейса предотвратить размытость. например
public interface IPresenter{
void init(); //错 模糊不清,View层根本不知道应该什么时候调用init()
void onCreate(); //对, View层知道在onCreate的时候调用Presenter的onCreate()方法
}
---------------------
Автор: древний колокол
Источник: CSDN
Оригинал: https: //blog.csdn.net/duo2005duo/article/details/50778321
Уведомление об авторских правах Эта статья является блоггер оригинала статьи, воспроизведен, пожалуйста , приложите Боуэна ссылку!