首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Android MVP策略

Android MVP策略
EN

Stack Overflow用户
提问于 2015-08-16 04:06:15
回答 3查看 3.4K关注 0票数 6

我正在将我的应用程序迁移到MVP。已经从这个孔米克中获得了静态演示程序模式的提示。

这是我简单的MVP策略。为了简洁起见,删除了大多数样板和MVP侦听器。这个策略帮助我改变了方向,证明了我的背景过程。与正在完成活动的停顿相比,活动正确地从正常的暂停中恢复。而且,演示者只有应用程序上下文,因此它不保留活动上下文。

我不是java专家,这是我第一次尝试MVP,使用静态演示器让我感到不舒服。我是不是遗漏了什么?我的应用程序运行得很好,反应也更快了。

视图

代码语言:javascript
复制
public class MainActivity extends Activity{
    private static Presenter presenter;

    protected void onResume() {
        if (presenter == null)
            presenter = new Presenter(this.getApplicationContext());
        presenter.onSetView(this);
        presenter.onResume();
    }

    protected void onPause() {
        presenter.onSetView(null);
        if(isFinishing())presenter.onPause();
    }    
}

演示者

代码语言:javascript
复制
public class Presenter {
    private MainActivity view;
    Context context;
    public Model model;

    public Presenter(Context context) {
        this.context = context;
        model = new Model(context);
    }

    public void onSetView(MainActivity view) {
        this.view = view;
    }

    public void onResume(){
        model.resume();
    }
    public void onPause(){
        model.pause();
    }

}

模型

代码语言:javascript
复制
public class Model {

    public Model(Context context){
        this.context = context;
    }
    public void resume(){
        //start data acquisition HandlerThreads
    }
    public void pause(){
        //stop HandlerThreads
    }

}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-08-16 05:34:21

我建议两件事。

  1. ModelViewPresenter转换为接口。
    • 您的MVP-View ( ActivityFragmentView)应该非常简单,不需要进行测试。
    • MVP-演示者从不直接与活动/片段/视图交互,因此可以用JUnit进行测试。如果您对Android的依赖关系不利于测试,因为您需要屏蔽Android对象,使用仿真器,或者使用像Roboelectric这样的测试框架,这样的测试非常慢。

作为接口的一个例子:

代码语言:javascript
复制
interface MVPView {
    void setText(String str);
}

interface MVPPresenter {
    void onButtonClicked();
    void onBind(MVPView view);
    void onUnbind();
}

MVPPresenter类现在不依赖于Android:

代码语言:javascript
复制
class MyPresenter implements MVPPresenter{
    MVPView view;

    @Override void bind(MVPView view){ this.view = view; }
    @Override void unbind() {this.view = null; }
    @Override void onButtonClicked(){
        view.setText("Button is Clicked!");
    }
}
  1. 我不会让Presenter成为一个静态类,而是将它变成一个保留的片段。当不需要静态对象时,需要小心地跟踪它们,并手动删除它们(否则会被认为是内存泄漏)。通过使用fragment片段,可以更容易地控制演示者的生命周期。当拥有保留片段的片段完成时,保留片段也会被销毁,内存可以被GC。看这里有个例子
票数 6
EN

Stack Overflow用户

发布于 2015-09-21 18:04:18

  1. 活动,片段应该只有重叠方法的视图界面和其他Android活动,碎片的方法。
  2. 视图有navigateToHome、setError、showProgress等方法。
  3. 演示者与视图和交互器交互(有onResume、onItemClicked等方法)
  4. Interactor具有所有的逻辑和计算,做时间密集的任务,如db,网络等。
  5. Interactor是安卓免费的,可以用jUnit进行测试。
  6. 活动/片段实现视图,实例化演示者。

建议修改一下我的理解。:)

一个例子总是比语言更好,对吗?https://github.com/antoniolg

票数 4
EN

Stack Overflow用户

发布于 2016-04-22 12:52:52

您正处于正确的轨道上,询问static是正确的--每当您注意到您已经编写了该关键字时,就该暂停和反思了。

演示者的生命应该直接与活动的/片段联系在一起。因此,如果活动被GC清除,那么演示者也应该这样做。这意味着您不应该在演示者中保存对ApplicationContext的引用。在演示程序中使用ApplicationContext是可以的,但是当活动被销毁时,中断这个引用是很重要的。

演示者还应该将View作为构造函数参数:

代码语言:javascript
复制
public class MainActivity extends Activity implements GameView{
    public void onCreate(){
        presenter = new GamePresenter(this);
    }
}

主持人看起来:

代码语言:javascript
复制
public class GamePresenter {
    private final GameView view;

    public GamePresenter(GameView view){
        this.view = view;
    }
}

然后,可以将活动LifeCycle事件通知演示者,如下所示:

代码语言:javascript
复制
public void onCreate(){
    presenter.start();
}

public void onDestroy(){
    presenter.stop();
}

或者在onResume/onPause --试着保持对称。

最后,您只有3个文件:

(我从我给这里的另一个解释中提取了一些代码,但想法是一样的。)

GamePresenter:

代码语言:javascript
复制
public class GamePresenter {
    private final GameView view;

    public GamePresenter(GameView view){
        this.view = view;
        NetworkController.addObserver(this);//listen for events coming from the other player for example. 
    }

    public void start(){
        applicationContext = GameApplication.getInstance();
    }

    public void stop(){
        applicationContext = null;
    }

    public void onSwipeRight(){
        // blah blah do some logic etc etc
        view.moveRight(100);
        NetworkController.userMovedRight();
    }

    public void onNetworkEvent(UserLeftGameEvent event){
        // blah blah do some logic etc etc
        view.stopGame()
    }
}

我不清楚为什么要使用ApplicationContext而不是活动上下文,但是如果没有特殊的原因,那么可以将void start()方法更改为void start(Context context),只需使用活动的上下文即可。对我来说,这更有意义,也排除了在应用程序类中创建单例的需要。

GameView

是一个接口

代码语言:javascript
复制
public interface GameView {
    void stopGame();
    void moveRight(int pixels);
}

GameFragment是一个扩展Fragment并实现GameView的类,它有一个GamePresenter作为成员。

代码语言:javascript
复制
public class GameFragment extends Fragment implements GameView {
    private GamePresenter presenter;

    @Override
    public void onCreate(Bundle savedInstanceState){
        presenter = new GamePresenter(this);
    }
}

这种方法的关键是清楚地理解每个文件的角色。

片段控制与视图相关的任何东西(按钮、TextView等)。它向演示者通知用户交互。

--演示者是引擎,它从视图获取信息(在本例中是片段,但注意到这种模式很适合依赖注入吗?这不是巧合。演示者不知道View是一个片段--它不在乎),并将其与从‘下面’接收到的信息(通信、数据库等)结合在一起,然后相应地命令View。

视图只是演示者与视图通信的一个接口。注意,方法读取为命令,而不是作为问题(例如getViewState())和not通知(例如onPlayerPositionUpdated()) -命令(例如movePlayerHere(int位置))。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32031724

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档