tobykurien / Xtendroid

Xtendroid is a DSL (domain-specific language) for Android that greatly reduces boilerplate code while providing great tooling support
MIT License
242 stars 23 forks source link

Idea: databinding #89

Open Buggaboo opened 8 years ago

Buggaboo commented 8 years ago

Hi,

I'm gonna work on data-binding.

tobykurien commented 8 years ago

Cool stuff!

Buggaboo commented 8 years ago

The only thing I can think of that is worth the trouble, but without getting in the way of android binding is writing an active annotation for observable models (e.g. BaseObservable, Observable, ObservableParcelable, @Binding, via notifyPropertyChanged).

The view DSL combined with the 'when a model attribute changes, the changes are automagically propagated to the views' is something Xtendroid doesn't have yet. I'm not sure if we should reinvent this mechanism in Xtendroid.

Sources:

  1. overview
  2. two-way binding

Arguments against binding the android way:

tobykurien commented 8 years ago

I agree about an annotation for Observables, although it doesn't have to be specific to this data binding. I have a more sophisticated observable in mind that I used for displaying data from a bluetooth device (where data was streaming in at 1Mbps):

To me, with Xtendroid's easy access to UI widgets, I find it easier to use than the Android data binding library (even for 2-way binding). In both cases, you still have to register your listeners, TextWatchers, etc. I prefer doing this in code than in the XML file.

Buggaboo commented 8 years ago

No changes are required for the gradle files to build this. Just patience.

  1. Transpile xtend without referring to the generated java files. i.e. out comment ...DataBindingLayout...Binding.java in the xtend code, e.g. so xtend won't fail due to the missing yet-to-be-generated java files.
  2. Build with: ./gradlew :Quotes:build
  3. Make the out commented lines active again.
  4. Build again with ./gradlew :Quotes:build

For every build, you need to repeat these four steps then ./gradlew :Quotes:clean. Yes, this is a pain in the ass.

If I attempt to build again, after those steps I mentioned without cleaning (e.g. ./gradlew :Quotes:clean), I get this gradle NPE:


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':Quotes:compileDebugJavaWithJavac'.
> java.lang.NullPointerException (no error message)

* Try:
Run with --info or --debug option to get more log output.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':Quotes:compileDebugJavaWithJavac'.
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:69)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
    at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:64)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:203)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:185)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:62)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:50)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
    at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
Caused by: java.lang.NullPointerException
    at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:210)
    at com.google.common.collect.Iterables.filter(Iterables.java:584)
    at org.eclipse.xtext.xbase.lib.IterableExtensions.filter(IterableExtensions.java:253)
    at org.xtext.gradle.tasks.XtextGenerate$4.apply(XtextGenerate.java:376)
    at org.xtext.gradle.tasks.XtextGenerate$4.apply(XtextGenerate.java:366)
    at org.eclipse.xtext.xbase.lib.ObjectExtensions.operator_doubleArrow(ObjectExtensions.java:139)
    at org.xtext.gradle.tasks.XtextGenerate.installDebugInfo(XtextGenerate.java:409)
    at org.xtext.gradle.android.XtextAndroidBuilderPlugin$6$2.execute(XtextAndroidBuilderPlugin.java:188)
    at org.xtext.gradle.android.XtextAndroidBuilderPlugin$6$2.execute(XtextAndroidBuilderPlugin.java:185)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:585)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:568)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)
    ... 14 more

I think we should move the data binding code generation ahead of the Xtend transpilation process. @tobykurien @oehme any ideas?

I tried to study how Kotlin does it, but I haven't found anything yet.

oehme commented 8 years ago

@Buggaboo Sorry for my ignorance about Android features :) Is databinding something that comes with the android build tools? If so, we should make the xtext-gradle-plugin aware of it.

Buggaboo commented 8 years ago

Yes, com.android.tools.build:gradle supports it from 1.5 onwards.

android {
    dataBinding {
        enabled = true
    }
}

Right now we don't use the _xtext_-gradle-plugin for building. We use the old _xtend_-gradle-plugin because, I can use the multi-project trick, for intellij to import all the projects, please see this build.gradle.

The problem is that xtext-gradle-plugin assumes it has to download these files, from an external source. For some reason the definitions in settings.gradle are ignored.

oehme commented 8 years ago

Sorry I didn’t get why you use the old one, they should be functionally equivalent.

Am 07.01.2016 um 16:37 schrieb Jasm Sison notifications@github.com:

Yes, com.android.tools.build:gradle supports it from 1.5 onwards.

android { dataBinding { enabled = true } } Right now we don't use the xtext-gradle-plugin for building. We use the old one (xtend-gradle-plugin) because, I can use the multi-project trick, see this build.gradle https://github.com/tobykurien/Xtendroid/blob/v0.13_development/build.gradle.

— Reply to this email directly or view it on GitHub https://github.com/tobykurien/Xtendroid/issues/89#issuecomment-169698048.

Buggaboo commented 8 years ago

This is why: xtext/xtend plugin error

Please use this branch to test. I think settings.gradle is getting ignored.

oehme commented 8 years ago

Well first of all if you want to depend on another project, you need to use compile project(':projectName‘). Because what you currently have (compile ':someName‘) is an external dependency. And since your buildscript does not define any repositories, these cannot be resolved. That is unrelated to the Xtend plugin.

Am 07.01.2016 um 17:25 schrieb Jasm Sison notifications@github.com:

This is why: xtext/xtend plugin error https://github.com/Buggaboo/Xtendroid/blob/xtext-gradle-plugin/xtext-gradle-plugin-error.txt Please use this branch https://github.com/Buggaboo/Xtendroid/tree/xtext-gradle-plugin to test.

— Reply to this email directly or view it on GitHub https://github.com/tobykurien/Xtendroid/issues/89#issuecomment-169715340.

Buggaboo commented 8 years ago

Ah. Excellent. Okay, I tried compile project(':Project'). It works. One problem less. Thanks!

I had to run the build >1 times, because some components require >1 passes, but that's not a problem. Unless someone always starts from a clean build, e.g. ./gradlew clean build...

Now, for databinding...

oehme commented 8 years ago

For databinding, please open an issue at the xtext-gradle-plugin on Github =)

Am 07.01.2016 um 17:57 schrieb Jasm Sison notifications@github.com:

Ah. Excellent. Okay, I tried compile project(':Project'). It works. One problem less. Thanks!

Now, for databinding...

— Reply to this email directly or view it on GitHub https://github.com/tobykurien/Xtendroid/issues/89#issuecomment-169727656.

Buggaboo commented 8 years ago

After some manual debugging with gradle, I found out that there is a cyclic dependency between:

So somehow "compile${variantName}JavaWithJavac" must be run twice, before "generate${variantName}Xtext".

Everything is in this branch