public final class Binders
- Object
- Binders
Public entry point for the build-time component binding framework.
@Bindable classes get a generated binder at build time. Each binder’s
static initializer self-registers with this registry. The registry stays
empty until something triggers each generated class’s <clinit>:
- iOS / Android – the build server probes the project zip for
cn1app.BinderBootstrapand splices anew cn1app.BinderBootstrap();into the per-build application stub beforeDisplay.init. - JavaSE simulator + desktop –
JavaSEPort#postInitcallsClass.forName("cn1app.BinderBootstrap")so the registry is populated on the same boundary. - Unit tests / manual init – application code can call
Binders.register(...)directly.
Two-way bindings and the change-notification contract
A binding declared twoWay = true flows in both directions:
- Component -> model. The generated binder installs a listener on
the editable component (
TextField,CheckBox, etc.). When the user mutates the component, the binder calls the model’s setter (or writes the public field directly), and the setter’s instrumented exit callsnotifyChanged(this). - Model -> component. Application code that mutates the model
through a setter (instrumented by the build-time processor) causes
notifyChanged(this)to fire, which walks every live binding for that model and pushes the new value into the matching component.
The two paths together would loop forever if left to themselves – the
model setter fires a change event, which refreshes the component, which
fires its own change event, which calls the model setter again. To
break the loop, every framework-initiated mutation runs inside an
“update region” guarded by a thread-local flag. While the flag is set,
notifyChanged is a no-op, and component listeners short-circuit.
Concretely:
Binders.bind(model, container)enters an update region for the initial model -> component push.Binding#refresh()enters an update region for every subsequent model -> component push.Binding#commit()enters an update region for the component -> model pull.- The generated component listener enters an update region before calling the setter.
Limitation: when a setter synchronously mutates a second bound
field (e.g. setFirstName also calls setFullName), the second field’s
notification is also suppressed – the user must call
binding.refresh() explicitly if they want the second component to
catch up. See Annotation-Component-Binding.asciidoc for details.
Methods
public static <T> void register(Binder<T> binder) | Installs binder under binder.type().getName(). |
public static <T> Binder<T> get(Class<T> type) | Looks up the binder for type (by type.getName()) or null when none is registered. |
public static <T> Binding bind(T model, Container container) | Pushes the model values into the matching components in container, wiring up the two-way listeners declared on its @Bind fields. |
public static void notifyChanged(Object model) | Called from the build-time-instrumented setter at every return point. |
public static void registerBinding(NotifiableBinding binding) | Generated binders call this to enroll a new live binding in the per-class registry. |
public static void unregisterBinding(NotifiableBinding binding) | Inverse of registerBinding. |
public static void enterUpdate() | Enter an update region. |
public static void exitUpdate() | Exit an update region. |
public static boolean isInUpdate() | True while the calling thread is inside a binding update region. |
Inherited methods
Method details
register
public static <T> void register(Binder<T> binder)binder under binder.type().getName(). The generated
per-class binder’s static initializer calls this; hand-written
binders for classes outside the build’s annotation scan call it
explicitly.get
public static <T> Binder<T> get(Class<T> type)type (by type.getName()) or null when
none is registered.bind
public static <T> Binding bind(T model, Container container)container, wiring up the two-way listeners declared on its
@Bind fields. Throws IllegalStateException when no binder is
registered for model.getClass().notifyChanged
public static void notifyChanged(Object model)Called from the build-time-instrumented setter at every return
point. Walks the live bindings for model.getClass().getName()
and refreshes those whose source is model. Short-circuits when
the calling thread is already inside an update region, breaking
the model -> component -> model loop.
Application code rarely calls this directly; the build-time instrumentation wires it into generated setters automatically.
registerBinding
public static void registerBinding(NotifiableBinding binding)unregisterBinding from disconnect to
remove it.unregisterBinding
public static void unregisterBinding(NotifiableBinding binding)registerBinding. Called from
Binding#disconnect.enterUpdate
public static void enterUpdate()exitUpdate
public static void exitUpdate()enterUpdate.isInUpdate
public static boolean isInUpdate()