Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Injection Transfuse Activity into Calltrough listener #118

Open
ghost opened this issue Jun 24, 2014 · 10 comments
Open

Injection Transfuse Activity into Calltrough listener #118

ghost opened this issue Jun 24, 2014 · 10 comments
Milestone

Comments

@ghost
Copy link

ghost commented Jun 24, 2014

I'd like to inject the matching transfuse activity in my menuController class like this:

public class MenuController implements ActivityMenuComponent {
    private MenuInflater menuInflater;
    private Context context; // this is of the generated class SearchActivityActivity
    private SearchActivity activity;

    @Inject
    public MenuController(MenuInflater menuInflater, Context context, SearchActivity activity) {
        this.menuInflater = menuInflater;
        this.context = context;
        this.activity = activity;
    }

The reason is that I need to call a function I defined in SearchActivity and that is supposed to stay there.

Currently above code yields the following error:

Exception in thread "pool-316-thread-1" Exception in thread "pool-316-thread-1" org.androidtransfuse.TransfuseAnalysisException: Unable to find a dependency to proxy
    at org.androidtransfuse.analysis.Analyzer.analyze(Analyzer.java:71)
    at org.androidtransfuse.analysis.Analyzer.analyze(Analyzer.java:41)
    at org.androidtransfuse.gen.variableBuilder.VariableInjectionNodeBuilder.buildInjectionNode(VariableInjectionNodeBuilder.java:41)
    at org.androidtransfuse.analysis.InjectionPointFactory.buildInjectionNode(InjectionPointFactory.java:174)
    at org.androidtransfuse.analysis.InjectionPointFactory.buildInjectionNode(InjectionPointFactory.java:153)
    at org.androidtransfuse.analysis.InjectionPointFactory.buildInjectionPoint(InjectionPointFactory.java:84)
    at org.androidtransfuse.analysis.astAnalyzer.InjectionAnalyzer.analyzeType(InjectionAnalyzer.java:55)
    at org.androidtransfuse.analysis.Analyzer.scanClassHierarchy(Analyzer.java:112)
    at org.androidtransfuse.analysis.Analyzer.scanClassHierarchy(Analyzer.java:106)
    at org.androidtransfuse.analysis.Analyzer.analyze(Analyzer.java:85)
    at org.androidtransfuse.analysis.Analyzer.analyze(Analyzer.java:41)
    at org.androidtransfuse.gen.variableBuilder.VariableInjectionNodeBuilder.buildInjectionNode(VariableInjectionNodeBuilder.java:41)
    at org.androidtransfuse.analysis.InjectionPointFactory.buildInjectionNode(InjectionPointFactory.java:174)
    at org.androidtransfuse.analysis.InjectionPointFactory.buildInjectionNode(InjectionPointFactory.java:153)
    at org.androidtransfuse.analysis.InjectionPointFactory.buildInjectionPoint(InjectionPointFactory.java:127)
    at org.androidtransfuse.analysis.astAnalyzer.InjectionAnalyzer.analyzeField(InjectionAnalyzer.java:79)
    at org.androidtransfuse.analysis.Analyzer.scanClassHierarchy(Analyzer.java:121)
    at org.androidtransfuse.analysis.Analyzer.scanClassHierarchy(Analyzer.java:106)
    at org.androidtransfuse.analysis.Analyzer.analyze(Analyzer.java:85)
    at org.androidtransfuse.analysis.Analyzer.analyze(Analyzer.java:41)
    at org.androidtransfuse.gen.variableBuilder.VariableInjectionNodeBuilder.buildInjectionNode(VariableInjectionNodeBuilder.java:41)
    at org.androidtransfuse.analysis.InjectionPointFactory.buildInjectionNode(InjectionPointFactory.java:174)
    at org.androidtransfuse.analysis.InjectionPointFactory.buildInjectionNode(InjectionPointFactory.java:153)
    at org.androidtransfuse.gen.componentBuilder.InjectionNodeFactoryImpl.buildInjectionNode(InjectionNodeFactoryImpl.java:69)
    at org.androidtransfuse.gen.ComponentGenerator.generate(ComponentGenerator.java:78)
    at org.androidtransfuse.gen.AnalysisGeneration.innerRun(AnalysisGeneration.java:47)
    at org.androidtransfuse.gen.AnalysisGeneration.innerRun(AnalysisGeneration.java:29)
    at org.androidtransfuse.transaction.AbstractCompletionTransactionWorker.run(AbstractCompletionTransactionWorker.java:35)
    at org.androidtransfuse.transaction.CodeGenerationScopedTransactionWorker.innerRun(CodeGenerationScopedTransactionWorker.java:47)
    at org.androidtransfuse.transaction.AbstractCompletionTransactionWorker.run(AbstractCompletionTransactionWorker.java:35)
    at org.androidtransfuse.transaction.ScopedTransactionWorker.run(ScopedTransactionWorker.java:55)
    at org.androidtransfuse.transaction.Transaction.run(Transaction.java:77)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:744)
@johncarl81
Copy link
Owner

Are you also injecting the MenuController into your SearchActivity? It looks like you've encountered a dependency loop. Transfuse handles this case, but you have to break it somewhere by either injecting via an interface or using a Provider:

Try this:

public class MenuController implements ActivityMenuComponent {
    private MenuInflater menuInflater;
    private Context context; // this is of the generated class SearchActivityActivity
    private Provider<SearchActivity> activityProvider;

    @Inject
    public MenuController(MenuInflater menuInflater, Context context, Provider<SearchActivity> activityProvider) {
        this.menuInflater = menuInflater;
        this.context = context;
        this.activityProvider = activityProvider;
    }

@ghost
Copy link
Author

ghost commented Jun 24, 2014

Ah, so that's what causes the problem.
Using the provider doesn't seem to work.
Basically it creates a SearchActivity$$Provider$$0 class, whose get()-method basically repeats the content of the SearchActivity instead of referencing it.

@johncarl81
Copy link
Owner

Shoot. You'll have to break the dependency loop by either injecting MenuController under an interface or just making your SearchActivity the ActivityMenuComponent.

Here's how to inject via the interface:

@Activity
public class SearchActivity{
    @Inject MenuController controller;
}

public interface MenuController {}
public class MenuControllerImpl implements ActivityMenuComponent, MenuController {}

@TransfuseModule
@Bind(type=MenuController.class, to=MenuControllerImpl.class)
public class Module{}

@ghost
Copy link
Author

ghost commented Jun 25, 2014

I tried the bind based injection. No errors on compile, but the callthrough listeners aren't registered anymore.
Could this be a bug with bind based injection?

@johncarl81
Copy link
Owner

Shoot, you may have to move the ActivityMenuComponent interface to as superclass of MenuController:

public interface MenuController extends ActivityMenuComponent{}
public class MenuControllerImpl implements MenuController {}

Are you using @RegisterListener on the MenuControllerImpl class or the controller injection?

@ghost
Copy link
Author

ghost commented Jun 26, 2014

RegisterListener is in SearchActivity:

    @Inject
    @RegisterListener
    MenuController menu;

If I extend ActivityMenuComponent, I get the following error:

Error:(9, 8) error: MenuControllerImpl$$VProxy$$0 is not abstract and does not override abstract method onOptionsMenuClosed(Menu) in ActivityMenuComponent

The generated code looks like this

@Generated(value = "org.androidtransfuse.TransfuseAnnotationProcessor", date = "2014-06-26T10:47+0200")
public class MenuControllerImpl$$VProxy$$0
    implements MenuController, DelayedLoad<MenuControllerImpl>
{

    private MenuControllerImpl delegate = null;

    public void load(MenuControllerImpl delegateInput) {
        delegate = delegateInput;
    }

    private void checkDelegate() {
        if (delegate == null) {
            throw new VirtualProxyException("Trying to use a proxied instance before initialization");
        }
    }

    public boolean equals(Object object$$0) {
        checkDelegate();
        return delegate.equals(object$$0);
    }

    public int hashCode() {
        checkDelegate();
        return delegate.hashCode();
    }

    public String toString() {
        checkDelegate();
        return delegate.toString();
    }

}

@johncarl81
Copy link
Owner

I went ahead and duplicated this problem. I was able to make it work by changing the activityProvider back to just an activity injection and duplicating the callthrough method interface on MenuController. Here's what it looks like:

public interface MenuController extends ActivityMenuComponent {
    boolean onCreateOptionsMenu(Menu menu);
    boolean onPrepareOptionsMenu(Menu menu);
    boolean onOptionsItemSelected(MenuItem menuItem);
    boolean onMenuOpened(int featureId, Menu menu);
    void onOptionsMenuClosed(Menu menu);
}
public class MenuControllerImpl implements MenuController {

    private MenuInflater menuInflater;
    private Context context; // this is of the generated class SearchActivityActivity
    private SearchActivity activity;

    @Inject
    public MenuControllerImpl(MenuInflater menuInflater, Context context, SearchActivity activity) {
        this.menuInflater = menuInflater;
        this.context = context;
        this.activity = activity;
    }
//...

Let me know if this gets you farther.

Although this works, this is obviously not as nice as it could be... lets keep this open as a bug. What I think is not happening here is twofold, the virtual proxy analysis/generator is not climbing the inheritance tree to find the callthrough interface and the @RegisterListener is not registering based on the bound type (rather on the type directly).

@ghost
Copy link
Author

ghost commented Jun 26, 2014

That worked well, thanks looking into this :)

@knutsoned
Copy link

I just encountered the same issue, and it was difficult to track down the problem since one of the errors points to a generated file for the application class, instead of either the activity or the ActivityMenuCompat-implementing class. I would like to suggest linking to this issue in the Call-Through Events section here to save time for developers in the future: http://androidtransfuse.org/documentation.html#activity

@johncarl81
Copy link
Owner

👍 @knutsoned, we should add this to the docs (or at least some wording to relfect this). And, one of the changes for the 0.3.0 needs to be better error reporting.

@johncarl81 johncarl81 added this to the 0.3.0 Release milestone Aug 24, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants