1. Hugo Visser
  2. android-apt
  3. Issues

Issues

Issue #38 resolved

android-apt breaks brand new data binding mechanism

Mateusz Grzechociński
created an issue

During Google I/O 2015 Google announced experimental support for Data Binding. Unfortunately, it seems that applying android-apt breaks data binding's code generation and thus compilation errors on missing classes.

Suppose we have a pretty simple project (copied from above data binding docs), with layout and one activity.

build.gradle

apply plugin: 'com.android.application'
apply plugin: 'com.android.databinding'

Layout:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="user" type="example.test.User"/>
    </data>
    <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        <TextView android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="@{user.firstName}"/>
        <TextView android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="@{user.lastName}"/>
    </LinearLayout>
</layout>

Activity:

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SampleBinding binding = DataBindingUtil.setContentView(this, R.layout.sample);
        User user = new User("Test", "User");
        binding.setUser(user);
}

It builds and works pretty well.

Once we try to add

apply plugin: 'com.android.application'
apply plugin: 'com.android.databinding'
apply plugin: 'com.neenbedankt.android-apt'

it starts failing:

:app:compileDebugJavaWithJavac
/example/project/app/src/main/java/example/test/SampleActivity.java:7: error: package example.test.databinding does not exist
import example.test.databinding.SampleBinding;

I tried with changing orders of applying plugins in build.gradle but it doesn't solve the problem. I had no time to do deeper investigation, so for the time being, I'm just only posting this issue here.

I'd appreciate any hint/plans to fix that?

Comments (49)

  1. Anonymous

    You don't need to apply this plug-in if you are using the new data-binding mechanism as that plug-in already activate the APT. If you are using Dagger or other APT dependent library, simply add it to your dependencies and instead of "apt 'somelibrary'" use "provided 'somelibrary'"

  2. Hugo Visser repo owner

    Nice how you state that the plugin is breaking something that didn't exists a couple of days before and not the other way round :) Anyway, I'll investigate what's going on.

    Can one of you supply a sample project showing the issue? Bitbucket, github project or attached to this issue is fine.

    @rjarana provided has always been around, but is different from what android-apt provides.

  3. Hugo Visser repo owner

    Haha, no problem and no offence taken, just funny :) You are correct that provided does not expose the sources. It will also put the provided dependencies (the annotation processor) on the class path of your IDE which might be an issue.

  4. Mateusz Grzechociński reporter

    As you requested, here's damn simple project which applies databinding plugin and builds well. Once you try to uncomment apply plugin: 'com.neenbedankt.android-apt' you should see:

    /bindings-example/app/src/main/java/net/grzechocinski/android/bindings/BindingDemoActivity.java:6: error: package net.grzechocinski.android.bindings.databinding does not exist
    import net.grzechocinski.android.bindings.databinding.SampleBinding;
                                                         ^
    /bindings-example/app/src/main/java/net/grzechocinski/android/bindings/BindingDemoActivity.java:10: error: cannot find symbol
        private SampleBinding binding;
                ^
      symbol:   class SampleBinding
      location: class BindingDemoActivity
    2 errors
    
  5. Rick Lei

    I'm using Dagger 2.0 and ran into the same issue. The workaround suggested by @rjarana (comment "apply plugin: ...android-apt" out and replace "apt ...:dagger-compiler:..." with "provided ...:dagger-compiler:...") seems to be working for me.

  6. Hugo Visser repo owner

    To clarify: you are not using data binding in that project? Sounds like some paths have been changed in AS 1.3, which should be easy to fix.

    Can anybody confirm that the project builds from the gradle command line with android-apt applied?

  7. Mateusz Grzechociński reporter

    Look at the project I pasted. I use new data binding and once I try to apply apt plugin it starts to fail. From gradle command line it doesn't work as well.

    Enable android-apt:

    ↪  git diff
    diff --git a/app/build.gradle b/app/build.gradle
    index d5956e9..81386a3 100644
    --- a/app/build.gradle
    +++ b/app/build.gradle
    @@ -3,7 +3,7 @@ apply plugin: 'com.android.application'
     apply plugin: 'com.android.databinding'
    
     //Uncommenting line below causes SampleBinding sample class not generated anymore
    -//apply plugin: 'com.neenbedankt.android-apt'
    +apply plugin: 'com.neenbedankt.android-apt'
    

    ./gradlew asembleDebug

    :app:compileDebugJavaWithJavac
    /Users/mateuszgrzechocinski/dev/RD/bindings-example/app/src/main/java/net/grzechocinski/android/bindings/BindingDemoActivity.java:6: error: package net.grzechocinski.android.bindings.databinding does not exist
    import net.grzechocinski.android.bindings.databinding.SampleBinding;
                                                         ^
    /Users/mateuszgrzechocinski/dev/RD/bindings-example/app/src/main/java/net/grzechocinski/android/bindings/BindingDemoActivity.java:10: error: cannot find symbol
        private SampleBinding binding;
                ^
      symbol:   class SampleBinding
      location: class BindingDemoActivity
    2 errors
    

    Seems like for some reason this class is not even generated (find . -name "SampleBinding.*" returns nothing), whereas when I disable apt plugin and run again:

    ↪  find . -name "SampleBinding.*"
    ./app/build/intermediates/classes/debug/net/grzechocinski/android/bindings/databinding/SampleBinding.class
    ./app/build/intermediates/classes/debug/net/grzechocinski/android/bindings/databinding/SampleBinding.java
    
  8. Hugo Visser repo owner

    OK. The issue is that dependencies in provided scope are currently not considered as annotation processors by android-apt. The work around is to add the dependency yourself in the apt scope: apt 'com.android.databinding:compiler:1.0-rc0

  9. Mateusz Grzechociński reporter

    Confirmed. Workaround works. I'm curious why is that: dependencies in provided scope are currently not considered as annotation processors by android-apt? I guess that's the reason we have to specify dependnecy to dagger2-compiler as apt rather than provided.

  10. Hugo Visser repo owner

    It's not because it normally doesn't make sense if you apply android-apt. The whole reason to use this plugin is to specify annotation processors and to make sure that AS sees the generated sources. provided will work without android-apt because javac will discover the annotation processor. However, the provided dependencies are on the IDE class path which means that you can accidentally import the classes from the processors or it's dependencies in your app, resulting in failures at runtime. When you apply android-apt to the project, only the processors that are in apt scope will be ran. apt also extends compile so effectively every dependency in compile plus what's in apt will be considered as a processor.

    So again, if you use android-apt don't put your processors on provided; apt is the better option in that case.

    What is happening now for the databinding is that the databinding plugin is adding the processor to provided (because it doesn't know about apt), that's why you need to put it in apt yourself. I'll probably fix this by also adding provided jars to apt.

  11. yigit boyar

    (disclaimer: I'm one of the authors of data binding)

    Actually for data binding, it would be great if these generated classes are not visible to the IDE unless user is in active debugging.

    We try to provide a better experience for data binding by creating light classes in the IDE which are updated as you edit the XML file (w/o compilation). This also allows better navigation. For example, if you have a view with id "username" and if you cmd + click on the binding.username in your java code; we take user to the XML tag rather then the generated code. This is probably not something you can easily change but if possible, it would be good to have an option to exclude generated data binding classes (even if it means excluding completely).

  12. Hugo Visser repo owner

    Thanks yigit boyar for chiming in. With "visible" I mean that classes can be resolved by the IDE, for example for debugging. For some annotation processors this is needed also for compile time, like for Dagger 2. I don't think there will be an issue supporting the databinding features you mention in the IDE in combination of android-apt as long as the IDE doesn't make assumptions on where the generated classes live. AS can still do whatever is right when inspecting the XML that contains the binding.

    android-apt only sets some javac command line options so that the generated sources end up in a different directory and makes AS aware of that through the API for that in the build tools. Since this is a global javac option, it would not be possible to do this on a per plugin basis; javac just runs all annotation processors and the plugin tells it where to put the sources.

  13. yigit boyar

    Let me clarify further but I guess you are right that nothing can be done in android-apt side.

    The data binding integration in AS works by creating virtual PSI classes. (PSI class is the internal representation of classes in IntelliJ). There is actually no actual code to back those classes.

    We provide these classes to the IDE via their class resolution interfaces. When android apt adds the generated code to the source folder list of the variant, for some reason, AS resolves to those classes instead of the virtual ones data binding provides. Right now, the elements we generate (e.g. PSIField) has the reference to the XML tag for navigation so when class is not resolved to the one we provide, the navigation is lost.

    Seems like we need to do something on the AS side to resolve this conflict because as far as I can see from your source code, all annotation processed code is exported to another folder and you provide it to the variant.

    def aptOutputDir = project.file(new File(project.buildDir, "generated/source/apt"))
    def aptOutput = new File(aptOutputDir, variant.dirName)
    javaCompile.options.compilerArgs += [
                    '-s', aptOutput
            ]
    

    I guess it won't be possible to provide a separate output for data binding's processor. I'll discuss w/ Studio team to see if we can figure this out, thanks!

  14. Hugo Visser repo owner

    Well yeah, it resolves that now because without any configuration AS does not know about the generated classes, and with android-apt applied it does. It's actually not so much the line that you quote, but variant.addJavaSourceFoldersToModel(aptOutput) that's responsible for that. It sounds like you are relying for AS not to find these classes, which is wrong IMHO. For any other annotation processor that does not need specific tooling in AS, you'd always want to have those classes resolved and this is why the plugin exists in the first place ;) I agree that a fix would need to be in the AS space.

    I would think that the PSI classes would somehow need to override or have preference over the actual code that is generated through the Gradle build. Or maybe there's a way to just annotate the generated code in a way that AS could interpret, without the need for PSI classes. Just thinking out loud here :)

  15. yigit boyar

    We don't want to rely on annotations from generated code because it requires compiling the application to update them. Creating these classes virtually makes it possible to keep them up to date as XML changes w/o compiling the whole application. Of course it is a lot of effort to duplicate class interface generation but this provides a better UX.

    Unless user is in a debugging session, they should not see these generated classes. I don't know how to do this but I hope Studio team knows :). This plugin is awesome but hopefully in the future AS can read these generated classes w/o needing a plugin.

  16. Hugo Visser repo owner

    In 1.5 annotation processors that are in provided scope will also be invoked. This is mainly for plugins that contribute annotation processors in the provided scope. Normally the apt scope should be used.

  17. Basilis Charalampakis

    Hi,

    i am using Android Studio 1.3.1

    my dependencies in build.gradle(for project) are :

    classpath 'com.android.tools.build:gradle:1.3.0'
    classpath 'com.android.databinding:dataBinder:1.0-rc1'
    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.6'
    

    and my build.gradle(for app) is

    apply plugin: 'com.android.application'
    apply plugin: 'com.android.databinding'
    apply plugin: 'com.neenbedankt.android-apt'
    
    android {
        compileSdkVersion 22
        buildToolsVersion "22.0.1"
    
        ...
    
        dependencies {
    
            ...
    
            apt 'com.google.dagger:dagger-compiler:2.0'
            compile 'com.google.dagger:dagger:2.0'
    
            apt 'com.android.databinding:compiler:1.0-rc1'
        }
    }
    

    i changed apt with provided but still doesn't work. What should i do?

  18. Hugo Visser repo owner

    As of 1.6 you don't need to add the dependency for the compiler anymore, it should be picked up automatically since the databinding plugin adds it to the provided scope. It's not clear what's not working for you. In any case you should first try to compile on the command line, then try in AS.

  19. Basilis Charalampakis

    I removed the apt 'com.android.databinding:compiler:1.0-rc1' line but still doesn't work..

    I get that error message

    Error:(17, 32) error: package **.**.databinding does not exist
    Error:(53, 13) error: cannot find symbol class FragmentTimelineBinding
    Error:(31, 8) error: dagger.internal.codegen.ComponentProcessor was unable to process this class because not all of its dependencies could be resolved. Check for compilation errors or a circular dependency with generated code.
    
  20. Csaba Kozák

    I found a problem when using Android Studio. If you cmd + click one of the bindings (MyLayoutBinding), it will not open the XML layout file, but the generated source file in the apt folder. What is even worse, that generated file will have an error, saying "duplicate file found in the file: .../mylayout.xml".

    Is there a way to resolve this problem? (Apart from avoid opening the MyLayoutBinding class. :) )

  21. Hugo Visser repo owner

    No that has to be fixed in AS. Databinding is the exception to the rule here, for all other things (Dagger2, AndroidAnnotations, etc) you'd actually want to go to the generated source class. The databinding plugin is also generating stuff in the "wrong" locations, triggering a warning in AS, but that's another story :)

  22. Csaba Kozák

    Yeah, i totally agree. I am even OK with going into the generated file instead of the xml. I guess the duplicate file problem is because the class is actually resolved to the xml file, maybe there is an exception when you open the class in its known location. But when you use android-apt, the generated file is not in the know location, but in the apt folder.

    yigit boyar are you planning to do something about this?

  23. yigit boyar

    This is in the list but not trivial. Basically, for best UX, on cmd+click, we want to send you to the generated class if you are in a debugging session and otherwise take you to the XML. There is no reason to show you the generated code unless you are debugging. Also, being able to click on binding.username and go to the TextView with id=username is very useful for navigation. The main reason why we are doing the virtual class generation (instead of relying on apt) is to provide a better developer experience since we can update these virtual classes instantly w/o code being recompiled. Unfortunately, this virtual class generation support is limited yet so I cannot even tell people not to use apt for data binding.

    I'm honestly not sure if it will be possible to keep debug + xml support together. It might be and will probably require deeper integration w/ apt.

    Sorry, i don't have a satisfactory answer for this yet but we are aware of the issue:/.

  24. Daksh Jotwani

    I'm using com.android.tools.build:gradle:2.1.2 on my Android Studio, and getting the duplicate file issues for my data-binding layouts.

    I've added Dagger 2 code in my build.gradle which is from where the issue started. Having read above, I understand it is an apt already existing situation. Just want someone to help me out on the following:

    Added to Top-level build.gradle:

    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

    Added to App-level build.gradle:

    apply plugin: 'com.neenbedankt.android-apt'

    dependencies {
    ...
    ...
    compile 'com.google.dagger:dagger:2.4'
        apt 'com.google.dagger:dagger-compiler:2.4'
        provided 'javax.annotation:jsr250-api:1.0'
    }
    

    Please tell me what modifications to make so as to use Dagger 2 and Data Binding in the same project. Thanks!

  25. Alex Trotsenko

    Hugo Visser I have exactly the same issue as Daksh Jotwani. I have Android Studio 2.1.2. I am using new-style data binding:

    ...
    android {
        ...
        dataBinding {
            enabled = true
        }
    }
    

    As soon as I add apt "com.google.dagger:dagger-compiler:2.2" I have error like:

    BookDetailsActivity.java:9: error: package com.alextrotsenko.booksearch.databinding does not exist
    import com.alextrotsenko.booksearch.databinding.ActivityBookDetailsBinding;
    

    The strange thing here, that other apt dependencies works fine. E.g. apt 'com.jakewharton:butterknife-compiler:8.1.0'.

    Example of my project could be found here: https://github.com/AlexTrotsenko/google-books-search/blob/73a66aeae038aed64f3b966cfdb090218c1597bb/app/build.gradle#L46

    P.S. I have this error happening from both command line gradlew as well as AS. Also changing apt for provided for dagger-compiler does nothing.

    Any ideas of why is this happening and how can I fix/work-around this issue?

    Thank you in advance.

  26. Hugo Visser repo owner

    Thanks for supplying a sample project Alex Trotsenko. Would you mind opening a new issue for this? Can you also try the project without android-apt from the command line to verify that the compilation works otherwise?

  27. Daksh Jotwani

    Alex Trotsenko My problem was resolved. It resulted in being a wrong setup on my part for Dagger 2.

    Go through the logs in detail and find out Dagger 2 related logs.

    It will surely be a problem in either setting up Component(s) or Module(s) !

    UPDATE: I read that you just added the apt and issues started appearing. Here is my working build.gradle config:

    Project's build.gradle:

    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
    

    App's build.gradle:

    apply plugin: 'com.neenbedankt.android-apt'
    
    dependencies {
        //Dagger 2
        apt 'com.google.guava:guava:19.0'
        compile 'com.google.dagger:dagger:2.4'
        apt 'com.google.dagger:dagger-compiler:2.4'
        provided 'javax.annotation:jsr250-api:1.0'
    }
    
  28. Alex Trotsenko

    Hugo Visser thanks for quick reply! I have created new issue: https://bitbucket.org/hvisser/android-apt/issues/65/android-apt-breaks-new-style-data-binding. Also concerning you question: removing android-apt makes the compilation works.

    Daksh Jotwani thank you for your advise and explaining the issue, which you used to have. Unfortunalty, your set-up looks exactly like mine. There is only one last thing to check: how do you set-up data-binding with apt 'com.android.databinding:compiler:1.0-rc0 or dataBinding.enabled = true ?

  29. Log in to comment