Android MVP 策略
Android MVP Strategy
我正在将我的应用程序迁移到 MVP。从这个 konmik
中获得了有关静态演示者模式的提示
这是我的简要 MVP 策略。为简洁起见,删除了大部分样板和 MVP 侦听器。这个策略帮助我改变了方向,证明了我的后台进程。与完成 activity 的暂停相比,activity 从正常暂停中正确恢复。此外,Presenter 只有应用程序上下文,因此它不会保留 activity 上下文。
我不是 java 专家,这是我第一次涉足 MVP,使用静态演示者让我感到不舒服。我错过了什么吗?我的应用运行良好,响应速度更快。
查看
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();
}
}
主持人
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();
}
}
型号
public class Model {
public Model(Context context){
this.context = context;
}
public void resume(){
//start data acquisition HandlerThreads
}
public void pause(){
//stop HandlerThreads
}
}
我有两点建议。
- 使
Model
、View
、Presenter
成为接口。
- 您的 MVP-View(
Activity
、Fragment
或 View
)应该非常简单,不需要测试。
- 您的 MVP-Presenter 从不直接与 Activity/Fragment/View 交互,因此可以使用 JUnit 对其进行测试。如果您依赖于 Android 框架不利于测试,因为您需要模拟 Android 对象,使用模拟器,或使用像 Roboelectric 这样的测试框架,它可能非常慢。
接口示例:
interface MVPView {
void setText(String str);
}
interface MVPPresenter {
void onButtonClicked();
void onBind(MVPView view);
void onUnbind();
}
MVPPresenter class 现在不依赖于 Android 框架:
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!");
}
}
- 我不会将
Presenter
设为静态 class,而是将其设为保留片段。需要仔细跟踪静态对象,并在不需要时手动将其删除以供 GC 使用(否则将被视为内存泄漏)。通过使用保留片段,可以更轻松地控制呈现器的生命周期。当拥有保留片段的片段完成时,保留片段也被销毁并且内存可以被GC'。 See here for an example.
- Activity,Fragments 应该只覆盖 View 接口的方法和其他 Android Activity,Fragment 的方法。
- View 有 navigateToHome、setError、showProgress 等方法
- Presenter 与 View 和 Interactor 交互(具有 onResume、onItemClicked 等方法)
- Interactor 拥有所有的逻辑和计算,执行数据库、网络等时间密集型任务
- Interactor android 免费,可以使用 jUnit 进行测试。
- Activity/fragment 实现视图,实例化演示者。
建议修改我的理解。 :)
举例胜于雄辩,对吧?
https://github.com/antoniolg
你走在正确的轨道上,你问 static
是正确的 - 每当你注意到你已经写了那个关键字时,是时候停下来反思一下了。
主持人的生活应该与 Activity's/Fragment 的生活直接相关。因此,如果 Activity 被 GC 清理,那么演示者也应该清理。这意味着您不应该在演示者中持有对 ApplicationContext 的引用。在 Presenter 中 使用 ApplicationContext 是可以的,但是当 Activity 被销毁时切断这个引用很重要。
Presenter也应该将View作为构造函数参数:
public class MainActivity extends Activity implements GameView{
public void onCreate(){
presenter = new GamePresenter(this);
}
}
主持人看起来像:
public class GamePresenter {
private final GameView view;
public GamePresenter(GameView view){
this.view = view;
}
}
然后你可以像这样通知 Presenter Activity 生命周期事件:
public void onCreate(){
presenter.start();
}
public void onDestroy(){
presenter.stop();
}
或 onResume/onPause
- 尽量保持对称。
最后你只有3个文件:
(我从我给出的另一个解释中提取了一些代码 但想法是一样的。)
游戏主持人:
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 而不是 Activity 上下文,但如果没有特殊原因,那么你可以将 void start()
方法更改为 void start(Context context)
并只使用 Activity 的上下文。对我来说,这更有意义,也排除了在您的应用程序中创建单例的需要 class.
游戏视图
是一个接口
public interface GameView {
void stopGame();
void moveRight(int pixels);
}
GameFragment 是一个 class,它扩展了 Fragment
并实现了 GameView 并且有一个 GamePresenter 作为成员。
public class GameFragment extends Fragment implements GameView {
private GamePresenter presenter;
@Override
public void onCreate(Bundle savedInstanceState){
presenter = new GamePresenter(this);
}
}
这种方法的关键是要清楚地了解每个文件的作用。
Fragment 控制任何与视图相关的内容(按钮、TextView 等)。它 通知 用户交互的演示者。
Presenter是引擎,它从View获取信息(在本例中是Fragment,但请注意这种模式很适合依赖注入?那是不是巧合。Presenter 不知道 View 是一个片段——它不在乎)并将它与从 'below'(通信、数据库等)接收的信息结合起来,然后 命令相应的视图。
View 只是一个接口,Presenter 通过它与 View 进行通信。请注意,这些方法读作 commands,not 读作 questions(例如 getViewState())和 not 到 inform(例如 onPlayerPositionUpdated())- commands(例如 movePlayerHere(int position))。
我正在将我的应用程序迁移到 MVP。从这个 konmik
中获得了有关静态演示者模式的提示这是我的简要 MVP 策略。为简洁起见,删除了大部分样板和 MVP 侦听器。这个策略帮助我改变了方向,证明了我的后台进程。与完成 activity 的暂停相比,activity 从正常暂停中正确恢复。此外,Presenter 只有应用程序上下文,因此它不会保留 activity 上下文。
我不是 java 专家,这是我第一次涉足 MVP,使用静态演示者让我感到不舒服。我错过了什么吗?我的应用运行良好,响应速度更快。
查看
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();
}
}
主持人
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();
}
}
型号
public class Model {
public Model(Context context){
this.context = context;
}
public void resume(){
//start data acquisition HandlerThreads
}
public void pause(){
//stop HandlerThreads
}
}
我有两点建议。
- 使
Model
、View
、Presenter
成为接口。- 您的 MVP-View(
Activity
、Fragment
或View
)应该非常简单,不需要测试。 - 您的 MVP-Presenter 从不直接与 Activity/Fragment/View 交互,因此可以使用 JUnit 对其进行测试。如果您依赖于 Android 框架不利于测试,因为您需要模拟 Android 对象,使用模拟器,或使用像 Roboelectric 这样的测试框架,它可能非常慢。
- 您的 MVP-View(
接口示例:
interface MVPView {
void setText(String str);
}
interface MVPPresenter {
void onButtonClicked();
void onBind(MVPView view);
void onUnbind();
}
MVPPresenter class 现在不依赖于 Android 框架:
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!");
}
}
- 我不会将
Presenter
设为静态 class,而是将其设为保留片段。需要仔细跟踪静态对象,并在不需要时手动将其删除以供 GC 使用(否则将被视为内存泄漏)。通过使用保留片段,可以更轻松地控制呈现器的生命周期。当拥有保留片段的片段完成时,保留片段也被销毁并且内存可以被GC'。 See here for an example.
- Activity,Fragments 应该只覆盖 View 接口的方法和其他 Android Activity,Fragment 的方法。
- View 有 navigateToHome、setError、showProgress 等方法
- Presenter 与 View 和 Interactor 交互(具有 onResume、onItemClicked 等方法)
- Interactor 拥有所有的逻辑和计算,执行数据库、网络等时间密集型任务
- Interactor android 免费,可以使用 jUnit 进行测试。
- Activity/fragment 实现视图,实例化演示者。
建议修改我的理解。 :)
举例胜于雄辩,对吧? https://github.com/antoniolg
你走在正确的轨道上,你问 static
是正确的 - 每当你注意到你已经写了那个关键字时,是时候停下来反思一下了。
主持人的生活应该与 Activity's/Fragment 的生活直接相关。因此,如果 Activity 被 GC 清理,那么演示者也应该清理。这意味着您不应该在演示者中持有对 ApplicationContext 的引用。在 Presenter 中 使用 ApplicationContext 是可以的,但是当 Activity 被销毁时切断这个引用很重要。
Presenter也应该将View作为构造函数参数:
public class MainActivity extends Activity implements GameView{
public void onCreate(){
presenter = new GamePresenter(this);
}
}
主持人看起来像:
public class GamePresenter {
private final GameView view;
public GamePresenter(GameView view){
this.view = view;
}
}
然后你可以像这样通知 Presenter Activity 生命周期事件:
public void onCreate(){
presenter.start();
}
public void onDestroy(){
presenter.stop();
}
或 onResume/onPause
- 尽量保持对称。
最后你只有3个文件:
(我从我给出的另一个解释中提取了一些代码
游戏主持人:
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 而不是 Activity 上下文,但如果没有特殊原因,那么你可以将 void start()
方法更改为 void start(Context context)
并只使用 Activity 的上下文。对我来说,这更有意义,也排除了在您的应用程序中创建单例的需要 class.
游戏视图
是一个接口
public interface GameView {
void stopGame();
void moveRight(int pixels);
}
GameFragment 是一个 class,它扩展了 Fragment
并实现了 GameView 并且有一个 GamePresenter 作为成员。
public class GameFragment extends Fragment implements GameView {
private GamePresenter presenter;
@Override
public void onCreate(Bundle savedInstanceState){
presenter = new GamePresenter(this);
}
}
这种方法的关键是要清楚地了解每个文件的作用。
Fragment 控制任何与视图相关的内容(按钮、TextView 等)。它 通知 用户交互的演示者。
Presenter是引擎,它从View获取信息(在本例中是Fragment,但请注意这种模式很适合依赖注入?那是不是巧合。Presenter 不知道 View 是一个片段——它不在乎)并将它与从 'below'(通信、数据库等)接收的信息结合起来,然后 命令相应的视图。
View 只是一个接口,Presenter 通过它与 View 进行通信。请注意,这些方法读作 commands,not 读作 questions(例如 getViewState())和 not 到 inform(例如 onPlayerPositionUpdated())- commands(例如 movePlayerHere(int position))。