Android MVP-应该避免在presenter中使用R.string引用吗?

Android MVP-应该避免在presenter中使用R.string引用吗?,android,testing,mvp,presenter,Android,Testing,Mvp,Presenter,为了将AndroidSDK与presenter类完全分离,我试图找出避免访问资源ID的最佳方法,我们通常使用presenter。我想我可以创建一个接口来访问字符串资源之类的东西,但我仍然需要ID来引用字符串。如果我做一些像 public class Presenter { private MyView view = ...; private MyResources resources = ...; public void initializeView() {

为了将AndroidSDK与presenter类完全分离,我试图找出避免访问资源ID的最佳方法,我们通常使用presenter。我想我可以创建一个接口来访问字符串资源之类的东西,但我仍然需要ID来引用字符串。如果我做一些像

public class Presenter {
    private MyView view = ...;
    private MyResources resources = ...;

    public void initializeView() {
        view.setLabel(resources.getString(LABEL_RES_ID);
    }
}
我仍然需要使用
LABEL\u RES\u ID
,然后将其映射到我的资源桥中的
R.string.LABEL
。这很酷,因为我可以在用其他东西进行单元测试时交换它,但我不想管理到字符串值的另一个映射


如果我放弃使用R.string值,我的演示者将再次绑定到我的视图。那不理想?有没有一个更简单的解决方案,人们可以用来绕过这一点,让他们远离演示者。我不想以Android提供的方式之外的方式管理字符串,因为我仍然希望将它们放入布局文件中,并从国际化中获益,等等。我想做一个哑单元测试,它可以与演示者一起工作,而不必让安卓SDK生成R.java文件。这是不是太多了?

< P>我认为没有理由在<强>演示者< /强>中调用任何Android代码(但你总是可以做到)。 因此,在你的情况下:

查看/活动onCreate()调用->presenter.onCreate()

Presenter onCreate()调用->view.setTextLabel()或视图中所需的任何内容

始终将Android SDK与演示者分离

Public String resolve(int ourID){
return context.getString(resourceMap.getValue(ourID));
}
在Github中,您可以找到一些关于MVP的示例:


    • 最好不要在presenter中使用上下文和所有依赖于android sdk的对象。我发送字符串的id并将其转换为字符串。像这样->

      getview().setTitle(R.string.hello);
      
      像这样把它放在电视上

      @Override
      public void setTitle(int id){
      String text=context.getString(id);
      //do what you want to do
      }
      
      使用这种方法,您可以在presenter中测试您的方法。这取决于R对象,但没关系。所有MVP类都放置在中的表示层中,以便您可以使用android对象,如R类。但在域层中,您必须只使用常规java对象

      更新

      对于那些希望在其他平台中重用代码的人,可以使用包装器类将id或enum类型映射到资源并获取字符串

      getView().setTitle(myStringTools.resolve(HELLO));
      
      字符串解析器方法是这样的,并且该类可以由视图和DI提供给presenters

      Public String resolve(int ourID){
      return context.getString(resourceMap.getValue(ourID));
      }
      
      但我不建议在大多数情况下,因为过度工程!在大多数情况下,您不需要在其他平台上使用精确的演示代码,因此:
      更好的解决方案可能类似于在其他平台上模拟该R类,因为R类已经像一个包装器了。您应该在其他平台上编写自己的R。

      这将是一篇关于如何构建MVP项目的长篇文章,然后在我最后的回答中开始解决您的问题

      我只是根据自己的回答在这里报告MVP结构

      我经常将业务逻辑代码放在模型层(不要与数据库中的模型混淆)。我经常重命名为
      XManager
      ,以避免混淆(例如
      ProductManager
      MediaManager
      …),所以presenter类仅用于保持工作流

      经验法则是presenter类中没有或至少限制导入android软件包。这个最佳实践支持您更轻松地测试presenter类,因为presenter现在只是一个普通的java类,所以我们不需要android框架来测试这些东西

      例如,这里是我的mvp工作流

      class PresenterImpl extends Presenter implements ViewListener {
          private View view;
          private MediaManager mediaManager;
      
          public PresenterImpl(View, MediaManager manager) {
             this.view = view;
             this.manager = manager;
          }
      
          @Override
          public void playSong() {
             mediaManager.playMedia();
          }
      }
      
      视图类:这是一个存储所有视图的地方,例如按钮、文本视图。。。然后为该层上的视图组件设置所有侦听器。此外,在该视图上,您将为以后的presenter实现定义一个侦听器类。视图组件将调用此侦听器类上的方法

      class ViewImpl implements View {
         Button playButton;
         ViewListener listener;
      
         public ViewImpl(ViewListener listener) {
           // find all view
      
           this.listener = listener;
      
           playButton.setOnClickListener(new View.OnClickListener() {
             listener.playSong();
           });
         }
      
         public interface ViewListener {
           playSong();
         }
      }
      
      Presenter类:这是存储视图和模型的地方,供以后调用。另外,presenter类将实现上面定义的ViewListener接口。演示者的要点是控制逻辑工作流

      class PresenterImpl extends Presenter implements ViewListener {
          private View view;
          private MediaManager mediaManager;
      
          public PresenterImpl(View, MediaManager manager) {
             this.view = view;
             this.manager = manager;
          }
      
          @Override
          public void playSong() {
             mediaManager.playMedia();
          }
      }
      
      管理类:以下是核心业务逻辑代码。也许一个演示者会有许多管理者(取决于视图的复杂程度)。通常,我们通过一些注入框架(如
      Dagger
      )获得
      Context

      Class MediaManagerImpl extends MediaManager {
         // using Dagger for injection context if you want
         @Inject
         private Context context;
         private MediaPlayer mediaPlayer;
      
         // dagger solution
         public MediaPlayerManagerImpl() {
           this.mediaPlayer = new MediaPlayer(context);
         }
      
         // no dagger solution
         public MediaPlayerManagerImpl(Context context) {
           this.context = context;
           this.mediaPlayer = new MediaPlayer(context);
         }
      
         public void playMedia() {
           mediaPlayer.play();
         }
      
         public void stopMedia() {
            mediaPlayer.stop();
         }
      }
      
      最后:将这些东西放在活动、片段中。。。这里是您初始化视图、管理器并将所有内容分配给演示者的位置

      public class MyActivity extends Activity {
      
         Presenter presenter;
      
         @Override
         public void onCreate() {
            super.onCreate();
      
            IView view = new ViewImpl();
            MediaManager manager = new   MediaManagerImpl(this.getApplicationContext());
            // or this. if you use Dagger
            MediaManager manager = new   MediaManagerImpl();
            presenter = new PresenterImpl(view, manager);
         }   
      
         @Override
         public void onStop() {
           super.onStop();
           presenter.onStop();
         }
      }
      
      您可以看到,每个演示者、模型和视图都由一个界面包装。这些组件将通过接口调用。这种设计将使您的代码更健壮,更便于以后修改

      简言之,在您的情况下,我提出以下设计:

      class ViewImpl implements View {
             Button button;
             TextView textView;
             ViewListener listener;
      
             public ViewImpl(ViewListener listener) {
               // find all view
      
               this.listener = listener;
      
               button.setOnClickListener(new View.OnClickListener() {
                 textView.setText(resource_id);
               });
             }
          }
      
      如果逻辑视图复杂,例如设置值的一些条件。因此,我将把逻辑放入
      DataManager
      ,以获取文本。例如:

      class Presenter {
         public void setText() {
            view.setText(dataManager.getProductName());
         }
      }
      
      class DataManager {
         public String getProductName() {
            if (some_internal_state == 1) return getResources().getString(R.string.value1);
            if (some_internal_state == 2) return getResources().getString(R.string.value2);
         }
      }
      
      所以你们从来并没有把android相关的东西放到presenter类中。您应该根据上下文将其移动到
      视图
      类或
      数据管理器


      这是一篇很长的文章,详细讨论了MVP以及如何解决您的具体问题。希望获得以下帮助:)

      您的
      演示者应该而不是需要了解如何显示显示UI的详细信息,以及相应的
      R.string
      参考

      假设遇到网络问题,希望向用户显示网络错误消息

      第一件(错误的IMO)事情是从
      视图
      中获取上下文,并在
      演示者中调用类似的方法

      public void showNetworkError(){
          presenter.showMessage(view.getResources().getString(R.string.res1));
      }
      
      您正在使用
      视图中的
      上下文
      ——这是
      活动
      片段

      现在,如果您被告知从更改复制内容,该怎么办