Android MVP-应该避免在presenter中使用R.string引用吗?
为了将AndroidSDK与presenter类完全分离,我试图找出避免访问资源ID的最佳方法,我们通常使用presenter。我想我可以创建一个接口来访问字符串资源之类的东西,但我仍然需要ID来引用字符串。如果我做一些像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() {
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));
}
您正在使用视图中的上下文
——这是活动
或片段
现在,如果您被告知从更改复制内容,该怎么办