简体   繁体   English

在GWT活动中使用GIN

[英]using GIN in GWT Activities

Each of my Activities needs a correspoding singleton View implementation. 我的每个活动都需要一个对应的单例View实现。 What's the best strategy to inject them into activities? 将它们注入活动的最佳策略是什么?

  1. constructor injection Activity constructor is called from an ActivityMapper's getActivity(). 构造函数注入 Activity构造函数是从ActivityMapper的getActivity()调用的。 The ctor already has a parameter (a Place object). ctor已经有一个参数(一个Place对象)。 I would have to create the ActivityMapper with all possible views injected. 我必须创建ActivityMapper,注入所有可能的视图。 Not good... 不好...

  2. method injection - "A function so annotated is automatically executed after the constructor has been executed." 方法注入 - “在执行构造函数后自动执行注释的函数。” (GWT in Action, 2nd Ed.) Well, "after the ctor has been executed" is apparently not fast enough because the view (or an RPC service injected this way) is still not initialized when the Activity's start() method is called and I get a NPE. (GWT in Action,2nd Ed。)好吧,“在执行ctor之后”显然不够快,因为当调用Activity的start()方法时,视图(或以这种方式注入的RPC服务)仍​​未初始化我得到了NPE。

  3. constructing the injector with GWT.create in Activity's ctor. 在Activity的ctor中用GWT.create构造注入器。 Useless, as they would not longer be singletons. 没用,因为他们不再是单身人士。

What worked best for us was to use Assisted Inject. 对我们最有效的是使用Assisted Inject。

Depending on the case, we defined activity factories either in the activity itself, in a package (for building all the activities in that package), or in the ActivityMapper. 根据具体情况,我们在活动本身,包中(用于构建该包中的所有活动)或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));

BTW, for method injection to work, you still have to create your activities through GIN, so you'd have the same issues as with constructor injection. 顺便说一下,要使方法注入工作,你仍然需要通过GIN创建你的活动,所以你会遇到与构造函数注入相同的问题。 There's no magic, GIN won't magically inject classes that it doesn't know about and doesn't even know when they've been instantiated. 没有魔法,GIN不会神奇地注入它不知道的类,甚至不知道它们何时被实例化。 You can trigger method injection explicitly by adding methods to your Ginjector, but I wouldn't recommend it (your code would depend on the Ginjector, which is something you should avoid if you can): 您可以通过向Ginjector添加方法来显式触发方法注入,但我不推荐它(您的代码将取决于Ginjector,如果可以,您应该避免这种情况):

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);
...

A last word: I wouldn't pass the place object directly to the activity. 最后一句话:我不会将place对象直接传递给activity。 Try to decouple places and activities, that allows you to move things around (eg build a mobile or tablet version, where you switch between master and detail views, instead of displaying them side by side) just by changing your "shell" layout and your activity mappers. 尝试分离场所和活动,让你可以移动东西(例如,建立一个移动或平板电脑版本,你可以在主视图和详细视图之间切换,而不是并排显示它们)只需更改你的“shell”布局和你的活动地图。 To really decouple them, you have to build some kind of navigator though, that'll abstract your placeController.goTo() calls, so that your activities never ever deal with places. 要真正解耦它们,你必须构建某种导航器 ,这将抽象你的placeController.goTo()调用,这样你的活动永远不会处理地方。

I chose a slightly different method that has all the flexibility you need. 我选择了一种略有不同的方法,它具有您所需的所有灵活性。 I don't remember where I picked this design pattern up, but it wasn't my idea. 我不记得我选择这个设计模式的地方,但这不是我的想法。 I create the activity as such 我这样创建活动

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;
    }
...
}

Then I use this in the activity mapper like 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
...

Also make sure the View is declared singleton in the gin module file. 还要确保在gin模块文件中将View声明为singleton。

In my experience a good practice is to have separate activity mappers to deal with the places and activities (the mapping). 根据我的经验,一个好的做法是让单独的活动映射器来处理场所和活动(映射)。 In the activity you have the presenter, here is example of a activity: 在您拥有演示者的活动中,以下是活动的示例:

public class ActivityOne extends AbstractActivity {

  @Inject
  private Presenter presenter;

  @Override
  public void start(AcceptsOneWidget panel, EventBus eventBus) {
    presenter.go(panel);
  }

}

The presenter have the view injected inside, it is constructed(the presenter) when "go" method is called. 演示者将视图注入内部,在调用“go”方法时构建(演示者)。 The presenter is declared as singleton in the GIN module and views are usually singletons(with some exceptions like small widgets that appear in many places). 演示者在GIN模块中被声明为单例,视图通常是单例(有些例外,例如在许多地方出现的小小部件)。

The idea is to move the contact with view inside the presenter (as the goal of the presenter is to deal with the logic and retrieve/update data to/from the view, according to MVP ). 我们的想法是在展示者内部移动联系人视图(因为演示者的目标是处理逻辑并根据MVP从视图中检索/更新数据)。 Inside the presenter you will have also the RPC services, you do not have to declare them because GIN will "magically" make instance for you, by calling GWT.create Here is an example of a simple presenter: 在演示者内部,您还将拥有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();
      }
}

At the end I must note that there are some activities, like the one for the menu, which deal with places and the view directly in order to display the current state. 最后我必须注意,有一些活动,比如菜单的活动,它们直接处理场所和视图以显示当前状态。 These activities are cached inside the mapper to avoid new instance every time the place is changed. 这些活动缓存在映射器内,以避免每次更改位置时出现新实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM