2012-03-29 5 views
5

Cada una de mis actividades necesita una implementación de vista singleton correspondiente. ¿Cuál es la mejor estrategia para inyectarlos en actividades?usando GIN en actividades de GWT

  1. inyección de constructor constructor actividad se llama desde getActivity de un ActivityMapper(). El ctor ya tiene un parámetro (un objeto Place). Tendría que crear ActivityMapper con todas las vistas posibles inyectadas. No es bueno ...

  2. inyección método-"Una función por lo anotado se ejecuta automáticamente después de que el constructor ha sido ejecutado" (GWT en acción, 2nd Ed.) Bueno, "después de que se ejecutó el ctor" aparentemente no es lo suficientemente rápido porque la vista (o un servicio RPC inyectado de esta manera) aún no se inicializa cuando se llama el método start() de Activity y yo obtener un NPE.

  3. construyendo el inyector con GWT.create en el Activity ctor. Inútil, ya que ya no serían simples.

Respuesta

7

Lo que funcionó mejor para nosotros fue el uso de Inyección asistida.

Según el caso, definimos las fábricas de actividades en la actividad en sí, en un paquete (para desarrollar todas las actividades en ese paquete) o en el 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)); 

Por cierto, para inyección método funcione, usted todavía tiene que crear sus actividades a través de GIN, por lo que tendría los mismos problemas que con la inyección de constructor. No hay magia, GIN no inyectará mágicamente clases de las que no tenga conocimiento y ni siquiera sepa cuándo se las ha instanciado. Puede desencadenar la inyección método de forma explícita mediante la adición de métodos para su Ginjector, pero yo no lo recomendaría (su código dependería de la Ginjector, que es algo que se debe evitar si es posible):

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

Un último palabra: no pasaría el objeto de lugar directamente a la actividad. Intente desacoplar lugares y actividades, que le permitan mover cosas (por ejemplo, cree una versión para móvil o tableta, donde cambie entre vistas maestra y de detalles, en lugar de mostrarlas una al lado de la otra) simplemente cambiando su diseño de "caparazón" y su mapeadores de actividades. Para desacoplarlos realmente, tiene que construir algún tipo de navigator, que abstraerá sus llamadas placeController.goTo(), de modo que sus actividades nunca se ocupen de lugares.

+1

Hola Thomas, eso es exactamente lo que hacemos en nuestra aplicación (usando fábrica) y funciona bien. Sin embargo, ¿cómo sugeriría integrarlo con AsyncProvider de GIN para la división de código? (estamos usando ActivityAsyncProxy http://ars-codia.raphaelbauer.com/2011/04/gwt-gin-and-simple-split-points.html) –

2

En mi experiencia, una buena práctica es tener mapeadores de actividades separados para tratar con los lugares y las actividades (el mapeo). En la actividad que tiene el presentador, aquí es un ejemplo de actividad:

public class ActivityOne extends AbstractActivity { 

    @Inject 
    private Presenter presenter; 

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

} 

El presentador tienen la vista inyecta en el interior, que se construye (el presentador) cuando "ir" método se llama. El presentador se declara como singleton en el módulo GIN y las vistas suelen ser simples (con algunas excepciones, como pequeños widgets que aparecen en muchos lugares).

La idea es mover el contacto con la vista dentro del presentador (ya que el objetivo del presentador es tratar con la lógica y recuperar/actualizar datos a/desde la vista, según MVP). Dentro del presentador también le los RPC servicios, usted no tiene que declarar porque GIN se "mágicamente" hacer instancia para usted, llamando GWT.create Aquí está un ejemplo de un simple presentador:

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

Al final, debo señalar que hay algunas actividades, como la del menú, que trata directamente con los lugares y la vista para mostrar el estado actual. Estas actividades se almacenan en caché dentro del asignador para evitar nuevas instancias cada vez que se cambia el lugar.

3

Elegí un método ligeramente diferente que tiene toda la flexibilidad que necesita. No recuerdo dónde escogí este patrón de diseño, pero no fue idea mía. Creo la actividad como tal

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

Entonces yo uso esto en el asignador de actividad como esta:

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

También asegúrese de que la vista se declara Singleton en el archivo de módulo de ginebra.

Cuestiones relacionadas