我的每个活动都需要一个相应的singleton View实现。将它们注入活动的最佳策略是什么?
-
构造函数注入活动构造函数是从ActivityMapper的getActivity()调用的。ctor已经有一个参数(Place对象)。我必须在注入所有可能的视图的情况下创建ActivityMapper。不好。。。
-
方法注入-"这样注释的函数在构造函数执行后会自动执行。"(GWT in Action,第二版)嗯,"在执行了ctor之后"显然不够快,因为当调用Activity的
start()
方法时,视图(或以这种方式注入的RPC服务)仍未初始化,而我得到了一个NPE。 -
在Activity的ctor中使用GWT.create构建注入器。没用,因为他们不会再单身了。
对我们最有效的是使用辅助注射。
根据具体情况,我们在活动本身、包(用于构建该包中的所有活动)或ActivityMapper中定义了活动工厂。
public class MyActivity extends AbstractActivity {
private final MyView view;
@Inject
MyActivity(MyView view, @Assisted MyPlace place) {
this.view = view;
...
}
...
}
public class MyActivityMapper implements ActivityMapper {
public interface Factory {
MyActivity my(MyPlace place);
FooActivity foo(FooPlace place);
...
}
// using field injection here, feel free to replace by constructor injection
@Inject
private Factory factory;
@Overrides
public Activity getActivity(Place place) {
if (place instance MyPlace) {
return factory.my((MyPlace) place);
} else if (place instance FooPlace) {
return factory.foo((FooPlace) place);
}
...
}
}
// in the GinModule:
install(new GinFactoryModuleBuilder().build(MyActivityMapper.Factory.class));
顺便说一句,要使方法注入工作,您仍然必须通过GIN创建活动,所以您会遇到与构造函数注入相同的问题。没有魔法,GIN不会神奇地注入它不知道的类,甚至不知道它们何时被实例化。您可以通过向Gijector中添加方法来显式触发方法注入,但我不建议这样做(您的代码将依赖于Gijector,如果可以的话,这是您应该避免的):
interface MyGinjector extends Ginjector {
// This will construct a Foo instance and inject its constructors, fields and methods
Foo foo();
// This will inject methods and (non-final) fields of an existing Bar instance
void whatever(Bar bar);
}
...
Bar bar = new Bar("some", "arguments");
myGinjector.whatever(bar);
...
最后一句话:我不会将place对象直接传递给活动。尝试将地点和活动解耦,这样只需更改"外壳"布局和活动映射器,就可以四处移动(例如,构建移动或平板电脑版本,在主视图和详细视图之间切换,而不是并排显示)。要真正解耦它们,您必须构建某种导航器,它将抽象您的placeController.goTo()
调用,这样您的活动就永远不会涉及位置。
我选择了一个略有不同的方法,它具有您所需的所有灵活性。我不记得我是从哪里学会这个设计模式的,但这不是我的主意。我将活动创建为这样的
public class MyActivity extends AbstractActivity{
private MyView view;
@Inject static PlaceController pc;
@Inject
public MyActivity(MyView view) {
super();
this.view = view;
}
public MyActivity withPlace(MyPlace myPlace) {
return this;
}
...
}
然后我在活动映射器中使用它,如下所示:
public class MyMapper implements ActivityMapper {
@Inject Provider<MyActivity> myActivityProvider;
public Activity getActivity(Place place) {
if ( place instanceof MyPlace){
return myActivityProvider.get().withPlace(place);
} else if
...
还要确保View在gin模块文件中声明为singleton。
根据我的经验,一个好的做法是使用单独的活动映射器来处理位置和活动(映射)。在你有演讲者的活动中,这里有一个活动的例子:
public class ActivityOne extends AbstractActivity {
@Inject
private Presenter presenter;
@Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
presenter.go(panel);
}
}
演示者在内部注入了视图,它是在调用"go"方法时构造的(演示者)。演示者在GIN
模块中被声明为singleton,视图通常是singleton(除了一些例外,比如出现在许多地方的小部件)。
其想法是在演示者内部移动带有视图的联系人(根据MVP
,演示者的目标是处理逻辑并在视图中检索/更新数据)。在演示者内部,您还将拥有RPC
服务,您不必声明它们,因为GIN
将通过调用GWT.create
"神奇地"为您创建实例下面是一个简单演示者的例子:
public class PresenterOneImpl implements Presenter {
@Inject
private MyView view;
@Inject
private SomeRpcServiceAsync someRpc;
@Override
public void go(AcceptsOneWidget panel) {
view.setPresenter(this);
panel.setWidget(view);
updateTheViewWithData();
}
}
最后,我必须注意到有一些活动,比如菜单的活动,它们直接处理位置和视图,以显示当前状态。这些活动缓存在映射器中,以避免每次更改位置时都出现新实例。