Android MVP – 应该避免在演示者中使用R.string引用?

为了完全分离我的演示者类Android SDK,我试图找出避免访问我们通常使用R的资源ID的最好方法。 我以为我可以创build一个接口来访问string资源的东西,但我仍然需要ID来引用string。 如果我要做一些像…

public class Presenter { private MyView view = ...; private MyResources resources = ...; public void initializeView() { view.setLabel(resources.getString(LABEL_RES_ID); } } 

我仍然必须有LABEL_RES_ID ,然后将其映射到我的资源桥中的R.string.label 。 这很酷,因为我可以用unit testingreplace它,但我不想pipe理另一个映射到string值。

如果我放弃,只是使用R.string值,我的主持人是再次绑定我的看法。 那不理想? 是否有一个更容易的解决scheme,人们用来解决这个问题,以避免他们的主持人。 我不想在Android提供的方式之外pipe理string,因为我仍然希望将它们放在布局文件中,并获得国际化的好处等。我想做一个愚蠢的unit testing,可以与这个演示者合作而不必让Android SDK生成R.java文件。 这太难问了吗?

Solutions Collecting From Web of "Android MVP – 应该避免在演示者中使用R.string引用?"

我认为没有理由在Presenter中调用任何android代码(但你总是可以做到这一点)。

所以在你的情况下:

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

演示者onCreate()调用 – > view.setTextLabel()或任何你想在视图中。

始终将Android SDK从演示者中分离出来。

在Github,你可以find关于MVP的一些例子:

最好不要在演示者中使用依赖于android sdk的上下文和所有对象。 我发送string的id和视图将其转换为string。 像这样 – >

 getview().setTitle(R.string.hello); 

并像这样看待这个

 @Override public void setTitle(int id){ String text=context.getString(id); //do what you want to do } 

通过这种方法,您可以在演示者中testing您的方法。 这取决于R对象,但没关系。 所有的MVP类都放在了叔叔的表示层中,所以你可以使用像R类这样的android对象。 但在领域层,你只能使用普通的Java对象

更新

对于那些想要在其他平台中重用代码的人,可以使用包装类将id或enumtypes映射到资源并获取string。

 getView().setTitle(myStringTools.resolve(HELLO)); 

stringparsing器的方法是这样的,这个类可以由View和DI提供给演示者。

 Public String resolve(int ourID){ return context.getString(resourceMap.getValue(ourID)); } 

但是在大多数情况下,我不推荐这样做,因为过度工程! 在大多数情况下,你绝对不需要在其他平台上使用精确的表示代码:更好的解决scheme就像嘲笑其他平台上的R类一样,因为R类已经是一个包装了。 你应该在其他平台上编写自己的R。

这将是一个很长的post,关于如何构buildMVP项目,然后在最后回答问题的时候解决你的问题。

我只是在这里报告MVP结构如何从我自己的答案结构MVP项目 。

我经常把业务逻辑代码放在模型层 (不要和数据库中的模型混淆)。 为了避免混淆,我经常将其重命名为XManager (如ProductManagerMediaManager …),因此演示者类仅用于保留工作stream。

经验法则是没有或至less限制在演示者类中导入android包 。 这个最佳实践支持您更容易地testing演示者类,因为演示者现在只是一个普通的java类,所以我们不需要android框架来testing这些东西。

例如,这里是我的mvp工作stream程。

视图类 :这是一个地方,你存储所有的视图,如button,文本视图…,你设置这个视图组件的这个层的所有侦听器。 同样在这个视图中,您稍后为演示者实现定义一个Listener类。 你的视图组件将调用这个监听器类的方法。

 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(); } } 

演示者类:这是您将视图和模型存储在其中以供稍后调用的位置。 另外主持人类将实现上面定义的ViewListener接口。 演示者的要点是控制逻辑工作stream程。

 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(); } } 

经理类:这是核心业务逻辑代码。 也许一个主持人会有很多pipe理者(取决于视图的复杂程度)。 通常我们通过一些注入框架(如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(); } } 

最后:把这些东西放在活动,碎片……这里是你初始化视图,pipe理器和分配给主持人的地方。

 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(); } } 

您会看到每个演示者,模型,视图都被一个界面包裹。 这些组件将通过接口调用。 此devise将使您的代码更健壮,更容易稍后修改。

总之,在你的情况下,我提出这样的devise:

 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相关的东西放到主讲者类。 你应该把它移到View类或者DataManager类依赖于上下文。

这是一个非常长的post,详细讨论MVP以及如何解决你的具体问题。 希望这个帮助:)