I already used Gin assisted injection a few times but I'm new to gwtp and I'm facing an error message I don't understand while trying to instantiate a PresenterWidget using my factory, even after reading several posts on the subject...
[ERROR] Error injecting #com.google.inject.assistedinject.Assisted(value=) java.lang.Integer:
Unable to create or inherit binding:
Binding requested for constant key '#com.google.inject.assistedinject.Assisted(value=) java.lang.Integer' but no explicit binding was found;
Path to required node:
org.moshika.dtp.client.application.book.page.PagePresenter [com.gwtplatform.mvp.client.gin.AbstractPresenterModule.bindPresenterWidget(AbstractPresenterModule.java:260)]
Context : I have to display the content of a book on the screen. For the moment I use a BookPresenter with proxy and place and I will try to bind / unbind pages on the fly as PresenterWidget. I don't think my pages could be implemented as CellWidget because I will implement lots of DTP / WYSIWYG functionalities.
My Module :
public class CommonModule extends AbstractGinModule {
#Override
protected void configure() {
// DefaultPlaceManager Places
install(new DefaultModule.Builder().defaultPlace(NameTokens.SEARCH).errorPlace(NameTokens.ERROR).unauthorizedPlace(NameTokens.ERROR).build());
install(new GinFactoryModuleBuilder().build(PagePresenter.Factory.class));
RestDispatchAsyncModule.Builder dispatchBuilder = new RestDispatchAsyncModule.Builder();
install(dispatchBuilder.build());
bindConstant().annotatedWith(RestApplicationPath.class).to("rest");
bind(ResourceLoader.class).asEagerSingleton();
bindPresenter(BookPresenter.class, BookPresenter.MyView.class, BookViewTablet.class, BookPresenter.MyProxy.class);
bindPresenterWidget(PagePresenter.class, PagePresenter.MyView.class, PageViewTablet.class);
}
BookPresenter :
public class BookPresenter extends Presenter<BookPresenter.MyView, BookPresenter.MyProxy>
implements BookUiHandlers {
public interface MyView extends View, HasUiHandlers<BookUiHandlers> {
}
#ProxyStandard
#NameToken(NameTokens.BOOK)
public interface MyProxy extends ProxyPlace<BookPresenter> {
}
static final Slot<PagePresenter> SLOT_BOOK = new Slot<PagePresenter>();
private ResourceDelegate<PageResources> pageDelegate;
private PagePresenter.Factory factory;
#Inject
BookPresenter(EventBus eventBus,
MyView view, MyProxy proxy,
ResourceDelegate<PageResources> pageDelegate,
PagePresenter.Factory factory) {
super(eventBus, view, proxy, ApplicationPresenter.SLOT_MAIN);
view.setUiHandlers(this);
this.pageDelegate= pageDelegate;
this.factory= factory;
}
#Override
protected void onReveal() {
super.onReveal();
NavigationVisibilityEvent.fire(this, true);
fetchPages(0, 5);
}
#Override
public void fetchPages(final int offset, int limit) {
pageDelegate.withCallback(new AsyncCallback<List<PageDto>>() {
#Override
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
#Override
public void onSuccess(List<PageDto> dtos) {
clearSlot(SLOT_BOOK);
for (PageDto dto : dtos) {
PagePresenter pagePresenter = factory.create(dto.getFolioPage());
addToSlot(SLOT_DEROULE, pagePresenter);
pagePresenter.refreshModel();
}
}
}).list(offset, limit);
}
}
PagePresenter and Factory :
public class PagePresenter extends PresenterWidget<PagePresenter .MyView>
implements PageUiHandlers {
public interface MyView extends View {
void setFolio(Integer folio);
}
public interface Factory {
CahierPageJourPresenter create(Integer folio);
}
private ResourceDelegate<PageResources> pageDelegate;
private Integer folioPage;
#Inject
PagePresenter(EventBus eventBus, MyView view,
ResourceDelegate<PageResources> pageDelegate,
#Assisted Integer folio) {
super(eventBus, view);
this.pageDelegate= pageDelegate;
this.folio= folio;
}
public void refreshModel() {
pageDelegate.withCallback(new AsyncCallback<PageDto>() {
#Override
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
#Override
public void onSuccess(PageDtodto) {
getView().setFolio(dto.getFolio());
}
}).get(folio);
}
}
It might be a very stupid mistake since I don't see what I'm doing different from all the other posts on the same subject...
If you want to use assisted injection here, don't call bindPresenterWidget(PagePresenter.class, PagePresenter.MyView.class, PageViewTablet.class); directly. Instead, bind only the view: bind(PagePresenter.MyView.class).to(PageViewTablet.class);
bindPresenterWidget actually calls bind(PagePresenter.class). Then bindings are resolved and since you don't have a constant for an Integer annotated with #Assisted, it throws.
If you only ever have one page visible at a time, you can also use a URL parameter to store the page number. Then you can override onReset() and update the content based on the requested page. This technique would avoid instantiating multiple PagePresenter and you can get rid of assisted factories.
Related
I know there are similar questions but it still doesn't answer my question in the manner I need for my current situation.
I have three activity presenters that each need to call a certain data remotely which will therefore call the activity presenter back when data arrives. To create this data listener I created an interface listener and since all three Presenters ask for the same data and retrieve it, all three presenters implement this interface listener.
Interface listener:
interface ListenerInterface {
onGotData();
}
Presenter one:
class PresenterOne implements ListenerInterface{
public void getData() {
DataManager dataManager = new DataManager();
dataManager.getData(this);
}
#Override
public void onGotData(Data data) {
//Do something with data
}
}
Presenter two very similar to presenter one:
class PresenterTwo implements ListenerInterface{
public void getData() {
DataManager dataManager = new DataManager();
dataManager.getData(this);
}
#Override
public void onGotData(Data data) {
//Do something with data
}
}
Assume Presenter three is exactly the same as the previous. The data manager class:
class DataManager {
public void getData(final ListenerInterface listener) {
//Gets data
addOnCompleteListener(new OnCompleteListener<Data data > () {
#Override
public void onComplete (#NonNull DataCall < Data > dataCall) {
listener.onGotData(dataCall.getResults());
}
});
}
}
Would doing so someone call all three presenters since the interface is the one doing the calling or only call the presenter that is passed? Is there anything I should worry about if I followed way? If anyone who knows the Android framework well could provide a detailed answer so I could learn from it more that would be great.
The reason I want to do this is I want to communicate through interfaces between classes.
Sorry if this question is simple for some people but I am still learning.
Thank you very much in advance.
you can use RxBus implementation to make global event (e.g. your onGotData).
First you have to create RxBus class.
public class RxBus {
private static RxBus instance;
private PublishSubject<Event> subject = PublishSubject.create();
public static synchronized RxBus getInstance() {
if (instance == null) {
instance = new RxBus();
}
return instance;
}
private RxBus(){}
public void postEvent(Event event){
subject.onNext(event);
}
public Observable<Event> getEvents(){
return subject;
}
}
And now, you should subscribe to it in BaseActivity or something like this (depends or your project structure).
private RxBus rxbus;
private Subscription rxBusSubscription;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rxBus = RxBus.getInstance();
}
#Override
protected void onResume() {
super.onResume();
if (shouldSubscribeRxBus()) {
rxBusSubscription = rxBus.getEvents()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(event -> {
if (event.getEventType() == Event.EventType.DATA_RECEIVED) {
onGotData(data);
}
});
}
}
Now implement you onGotData as you want.
When you catch data received call:
class DataManager {
public void getData(final ListenerInterface listener) {
//Gets data
addOnCompleteListener(new OnCompleteListener<Data data > () {
#Override
public void onComplete (#NonNull DataCall < Data > dataCall) {
RxBus.getInstance().postEvent(new GotDataEvent(dataCall.getResults()));
}
});
}
}
You can create your Event classes structure as you want.
I am developing an app where I am using clean architecture. In presenter, when something comes to method onCompleted then I must call function from Main activity.
this is my Presenter:
public class CheckInPresenter implements Presenter {
UseCase postCheckInUseCase;
Context context;
#Inject
CheckInPresenter(#Named("putCheckIn") UseCase postCheckInUseCase){
this.postCheckInUseCase = postCheckInUseCase;
}
public void initialize(){this.initializeCheckIn();}
public void initializeCheckIn(){this.putCheckIn();}
public void putCheckIn(){
this.postCheckInUseCase.execute(new CheckInSubscriber());
}
#Override
public void resume() {
}
#Override
public void pause() {
}
#Override
public void destroy() {
}
private final class CheckInSubscriber extends DefaultSubscriber<EventEntity>{
#Override
public void onCompleted() {
Log.d("onCompleted", "OnCompleted");
}
#Override
public void onError(Throwable e) {
Log.d("onError", "OnError: " + e.getMessage());
}
#Override
public void onNext(EventEntity eventEntity) {
Log.d("onNext", "OnNext");
}
}
}
And this is my function from MainActivity that I have to call:
public void getDataForToolbar() {
SharedPreferences sharedPreferences = getSharedPreferences(getResources().getString(R.string.Account_json), Context.MODE_PRIVATE);
final String account = sharedPreferences.getString(getResources().getString(R.string.account_json), null);
if (account != null) {
Gson gson = new Gson();
mAccount = gson.fromJson(account, AccountModel.class);
for (CompanyModel com : mAccount.getCompanies()) {
String name = com.getName();
company_name.setText(name);
logo_url = com.getLogo_url();
}
if (logo_url == null || logo_url.isEmpty()) {
Picasso
.with(this)
.load(R.drawable.default_company)
.resize(70, 58)
.transform(new RoundedTransformation(8, 0))
.into(toolbarImage);
} else {
picassoLoader(this, toolbarImage, logo_url);
}
String username = mAccount.getUsername();
if(mAccount.getStatus()){
aUsername.setText(username + "/" + getResources().getString(R.string.on_duty));
aUsername.setBackgroundColor(ContextCompat.getColor(mContext, R.color.colorGreen));
}else{
aUsername.setText(username + "/" + getResources().getString(R.string.off_duty));
aUsername.setBackgroundColor(ContextCompat.getColor(mContext, R.color.colorWhite));
}
}
}
Could someone helps me how to call this function into my onCompleted method in Presenter? Thanks in advance.
If you want to call some Activity's function from another object, you'll have to pass Activity's reference to that object. This means that you need to add Activity parameter to presenter's constructor.
Side note
I know that what you're implementing is called a "clean architecture" in many places (including the official MVP tutorials by Google), but you might want to read my posts on the subject in order to get an alternative view on what "clean" on Android should look like.
Why activities in Android are not UI elements
MVC and MVP architectural patterns in Android
Create interface IView and make your Activity to implement it.
In IView create method void getDataForToolbar();
I see #Inject annotation in your CheckInPresenter, so if you are using Dagger 2, update you Dagger module's constructor with IView, create a provider method for it and provide it for CheckInPresenter in this module.
Place IView in constructor of CheckInPresenter and update provider method for CheckInPresenter in Dagger module and Dagger component initialization in your Activity.
For example, it might look like this:
public class YourActivity implements IView {
...
}
#Module
public class YourModule {
private IView view;
public YourModule(IView view) {
this.view = view;
}
#Provides
public IView provideView() {
return view;
}
#Provides
#YourScope
public Presenter providePresenter() {
return new YourPresenter(view);
}
}
Just complete your existing Presenter and Module with IView.
After that call in your onCompleted method view.getDataForToolbar().
I have a small problem which I can't figure out to save my life.
Basically I need to register classes anytime dynamically using guice and then loop through them all.
Lets say this is my class to register Strategies but these strategies can be added anytime through the application running.
// Strategy registration may happen anytime, this is just an example
strategyManager.register(ExampleStrategy1.class);
strategyManager.register(ExampleStrategy2.class);
StrategyImpl class
public class StrategyImpl implements Strategy {
#Override
public void register(Class<? extends StrategyDispatcher> strat) {
//Add this class into provider or create an instance for it and add it into guice but how?
}
#Override
public void dispatchStrategy() {
//Find all strategies and execute them
}
}
I've tried using a Provider but have no idea how i'd add the registered class into the provider and retrieve them all?
#Override
protected void configure() {
bind(Strategy.class).toProvider(StrategyProvider.class);
}
My provider class always gets the same instance
public class StrategyProvider implements Provider<StrategyDispatcher> {
public LogManager get() {
return new StrategyDispatcherImpl();
}
}
The strategies that I add extend the StrategyDispatcherImpl class so i could cast them?
I need to add multiple binds to a same instance but it needs to be done dynamically and not using the bind method in configure but another way then be able to find all these strategies and execute them.
If you truly need it to happen at "any time" during the application life cycle then Guice then I think you will need some sort of Guice-aware Factory. I.e.
public class TestStuff {
#Test
public void testDynamicCreation() {
Injector injector = Guice.createInjector();
StrategyManager manager = injector.getInstance(StrategyManager.class);
Hello hello = injector.getInstance(Hello.class);
manager.doStuff();
assertThat(hello.helloCalled, is(false));
manager.register(Hello.class); // DYNAMIC!!
manager.doStuff();
assertThat(hello.helloCalled, is(true));
}
}
interface Strategy {
void doStuff();
}
#Singleton
class Hello implements Strategy {
boolean helloCalled = false;
public void doStuff() {
helloCalled = true;
}
}
class StrategyManager {
private final Collection<Strategy> strategies = new ArrayList<>();
private final StrategyFactory factory;
#Inject
StrategyManager(StrategyFactory factory) {
this.factory = factory;
}
public void register(Class<? extends Strategy> strat) {
strategies.add(factory.create(strat));
}
public void doStuff() {
for (Strategy s : strategies) {
s.doStuff();
}
}
}
class StrategyFactory {
private final Injector injector;
#Inject
StrategyFactory(Injector injector) {
this.injector = injector;
}
public Strategy create(Class<? extends Strategy> clazz) {
return injector.getInstance(clazz);
}
}
If it is not "dynamic" after the initialization phase then you are after the "multibinder" I think.
I'm using Guice 3.0 on a Groovy project and am running into bizarre/unexpected injection behaviors. My main/bootstrapping class:
class WidgetServicePerfTester {
#Inject
WidgetGenerator widgetGenerator
static main(args) {
Injector injector = Guice.createInjector(new WidgetServicePerfTesterModule())
WidgetServicePerfTester perfTester = injector.getInstance(WidgetServicePerfTester)
perfTester.run()
}
void run() {
List<Widget> widgets = widgetGenerator.generateWidgets()
widgets.each {
it.doStuff()
}
}
}
My POJO:
class Widget extends Thingy {
WidgetClient widgetClient
int numFoos
#Override
void doStuff() {
widgetClient.doSomethingOnServer()
}
}
My POJO generators (API + impl):
interface WidgetGenerator {
List<Widget> generateWidgets()
}
class SimpleWidgetGenerator implements WidgetGenerator {
#Inject
WidgetClient widgetClient
int numWidgets
SimpleWidgetGenerator() {
super()
}
SimpleWidgetGenerator(int numWidgets) {
super()
this.numWidgets = numWidgets
}
#Override
List<Widget> generateWidgets() {
List<Widget> widgets = []
Widget firstWidget = new Widget(widgetClient: widgetClient, numFoos: getRandomNumber())
widgets.add(firstWidget)
// Code that populates the rest of 'widgets' with 'numWidgets' number of Widgets.
}
}
My Guice module:
class WidgetServicePerfTesterModule extends AbstractModule {
#Override
protected void configure() {
bind(WidgetClient).to(SimpleWidgetClient)
}
#Provides
public WidgetGenerator provideSimpleWidgetGenerator() {
new SimpleWidgetGenerator(50)
}
}
When I run this code I get null pointer exceptions because the WidgetClient injected into each Widget is NULL. Why and what's the fix?
The whole problem begins here in WidgetServicePerfTesterModule class, in the following block of code:
#Provides
public WidgetGenerator provideSimpleWidgetGenerator() {
new SimpleWidgetGenerator(50)
}
An instance of SimpleWidgetGenerator is created using this constructor:
SimpleWidgetGenerator(int numWidgets) {
super()
this.numWidgets = numWidgets
}
and since this object is created manually injecting WidgetClient into SimpleWidgetGenerator will not work - it's null and it's explainable.
To fix the problem guice-assistedinject should be used and factory for Widgets should be created. Here you can find refactored project using the mentioned extensions. Everything works fine.
Is that clear now?
Could you guys please help me find where I made a mistake ?
I switched from SimpleBeanEditorDriver to RequestFactoryEditorDriver and my code no longer saves full graph even though with() method is called. But it correctly loads full graph in the constructor.
Could it be caused by circular reference between OrganizationProxy and PersonProxy ? I don't know what else to think :( It worked with SimpleBeanEditorDriver though.
Below is my client code. Let me know if you want me to add sources of proxies to this question (or you can see them here).
public class NewOrderView extends Composite
{
interface Binder extends UiBinder<Widget, NewOrderView> {}
private static Binder uiBinder = GWT.create(Binder.class);
interface Driver extends RequestFactoryEditorDriver<OrganizationProxy, OrganizationEditor> {}
Driver driver = GWT.create(Driver.class);
#UiField
Button save;
#UiField
OrganizationEditor orgEditor;
AdminRequestFactory requestFactory;
AdminRequestFactory.OrderRequestContext requestContext;
OrganizationProxy organization;
public NewOrderView()
{
initWidget(uiBinder.createAndBindUi(this));
requestFactory = createFactory();
requestContext = requestFactory.contextOrder();
driver.initialize(requestFactory, orgEditor);
String[] paths = driver.getPaths();
createFactory().contextOrder().findOrganizationById(1).with(paths).fire(new Receiver<OrganizationProxy>()
{
#Override
public void onSuccess(OrganizationProxy response)
{
if (response == null)
{
organization = requestContext.create(OrganizationProxy.class);
organization.setContactPerson(requestContext.create(PersonProxy.class));
} else
organization = requestContext.edit(response);
driver.edit(organization, requestContext);
}
#Override
public void onFailure(ServerFailure error)
{
createConfirmationDialogBox(error.getMessage()).center();
}
});
}
private static AdminRequestFactory createFactory()
{
AdminRequestFactory factory = GWT.create(AdminRequestFactory.class);
factory.initialize(new SimpleEventBus());
return factory;
}
#UiHandler("save")
void buttonClick(ClickEvent e)
{
e.stopPropagation();
save.setEnabled(false);
try
{
AdminRequestFactory.OrderRequestContext ctx = (AdminRequestFactory.OrderRequestContext) driver.flush();
if (!driver.hasErrors())
{
// Link to each other
PersonProxy contactPerson = organization.getContactPerson();
contactPerson.setOrganization(organization);
String[] paths = driver.getPaths();
ctx.saveOrganization(organization).with(paths).fire(new Receiver<Void>()
{
#Override
public void onSuccess(Void arg0)
{
createConfirmationDialogBox("Saved!").center();
}
#Override
public void onFailure(ServerFailure error)
{
createConfirmationDialogBox(error.getMessage()).center();
}
});
}
} finally
{
save.setEnabled(true);
}
}
}
with() is only used for retrieval of information, so your with() use with a void return type is useless (but harmless).
Whether a full graph is persisted is entirely up to your server-side code, which is intimately bound to your persistence API (JPA, JDO, etc.)
First, check that the Organization object you receive in your save() method on the server-side is correctly populated. If it's not the case, check your Locators (and/or static findXxx methods) ; otherwise, check your save() method's code.
Judging from the code above, I can't see a reason why it wouldn't work.
It took me some time to realize that the problem was the composite id of Person entity.
Below is the code snippet of PojoLocator that is used by my proxy entities.
public class PojoLocator extends Locator<DatastoreObject, Long>
{
#Override
public DatastoreObject find(Class<? extends DatastoreObject> clazz, Long id)
{
}
#Override
public Long getId(DatastoreObject domainObject)
{
}
}
In order to fetch child entity from DataStore you need to have id of a parent class. In order to achieve that I switched "ID class" for Locator<> to String which represents textual form of Objectify's Key<> class.
Here is how to looks now:
public class PojoLocator extends Locator<DatastoreObject, String>
{
#Override
public DatastoreObject find(Class<? extends DatastoreObject> clazz, String id)
{
Key<DatastoreObject> key = Key.create(id);
return ofy.load(key);
}
#Override
public String getId(DatastoreObject domainObject)
{
if (domainObject.getId() != null)
{
Key<DatastoreObject> key = ofy.fact().getKey(domainObject);
return key.getString();
} else
return null;
}
}
Please note that your implementation may slightly differ because I'm using Objectify4.