by ampatspell
in Code
GWT-2.0 has great new feature called Code Splitting what allows to split module in smaller compiled parts and request parts only when (if) they’re needed. But I’m using GIN to wire all internal application object graph (models, presenters, views, service classes) and of corse if it’s left as is (one Ginjector) no code is split.
So I came up with this simple “design pattern” to continue using GIN modules while taking advantage of code splitting:
void load(LoaderCallback cb) methodLoader:
LoaderCallback methodIn this example (live version is deployed on AppEngine) I have one “base” StoriesModule:
public class StoriesModule extends AbstractGinModule { @Override protected void configure() { bind(EventBus.class).to(EventBusImpl.class); bind(EventBusListener.class).to(LoggingEventBusListener.class); bind(AdminLoader.class).to(AdminLoaderImpl.class); } }
and AdminModule:
public class AdminModule extends AbstractGinModule { @Override protected void configure() { bind(AdminPresenter.class).to(AdminPresenterImpl.class); bind(AdminView.class).to(AdminViewImpl.class); } }
Pretty basic stuff.
But AdminModule needs EventBus instance from StoriesModule. To connect both modules add 2 new interfaces:
public interface SharedServices { EventBus getEventBus(); }
public interface SharedServicesAware { void setSharedServices(SharedServices services); }
In “base” module add SharedServices provider:
@Provides @Singleton public SharedServices sharedServices(final EventBus eventBus) { return new SharedServices() { public EventBus getEventBus() { return eventBus; } }; }
In “child” module add class what implements both SharedServicesAware and Provider<SharedServices>:
@Singleton public static class SharedServicesAdapter implements Provider<SharedServices>, SharedServicesAware { private SharedServices services; public void setSharedServices(SharedServices services) { this.services = services; } public SharedServices get() { return services; } }
And now EventBus in “child” module can be bound using SharedServices like this:
@Provides public EventBus eventBus(SharedServices sharedServices) { return sharedServices.getEventBus(); }
The last thing — Loader.
Child module loader should contain only one method load:
public interface AdminLoader { public interface AdminLoaderCallback { void onLoaded(AdminPresenter admin); } void load(AdminLoaderCallback callback); }
Implementation:
@Singleton public class AdminLoaderImpl implements AdminLoader { private static AdminInjector injector; private final SharedServices sharedServices; @Inject public AdminLoaderImpl(SharedServices sharedServices) { this.sharedServices = sharedServices; } public void load(final AdminLoaderCallback callback) { GWT.runAsync(AdminLoader.class, new RunAsyncCallback() { public void onSuccess() { if (injector == null) { onFirstLoad(); } callback.onLoaded(injector.presenter()); } private void onFirstLoad() { // Inject just loaded stylesheets StyleInjector.inject(AdminClientBundle.I.css().getText()); // create Injector and connect both "worlds" injector = GWT.create(AdminInjector.class); injector.sharedServicesAware().setSharedServices(sharedServices); } public void onFailure(Throwable reason) { Window.alert("Failed to load admin presenter"); } }); } }
Note I’m setting AdminInjector as static. It’s because AdminLoaderImpl can be bound in more than one Module. This ensures that only one AdminInjector instance is present in application.
Initialy downloaded code contains only AdminLoader:
All other code comes after the split point: