public abstract class

ViewDataBinding

extends BaseObservable

 java.lang.Object

androidx.databinding.BaseObservable

↳androidx.databinding.ViewDataBinding

Gradle dependencies

compile group: 'androidx.databinding', name: 'library', version: '3.2.0-alpha11'

  • groupId: androidx.databinding
  • artifactId: library
  • version: 3.2.0-alpha11

Artifact androidx.databinding:library:3.2.0-alpha11 it located at Google repository (https://maven.google.com/)

Androidx class mapping:

androidx.databinding.ViewDataBinding android.databinding.ViewDataBinding

Overview

Base class for generated data binding classes. If possible, the generated binding should be instantiated using one of its generated static bind or inflate methods. If the specific binding is unknown, DataBindingUtil.bind(View) or DataBindingUtil.inflate(LayoutInflater, int, ViewGroup, boolean) should be used.

Summary

Fields
public static final java.lang.StringBINDING_TAG_PREFIX

Prefix for android:tag on Views with binding.

protected final DataBindingComponentmBindingComponent

The DataBindingComponent used by this data binding.

Constructors
protectedViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount)

Methods
public voidaddOnRebindCallback(OnRebindCallback listener)

Add a listener to be called when reevaluating dirty fields.

protected static ViewDataBindingbind(DataBindingComponent bindingComponent, View view, int layoutId)

protected voidensureBindingComponentIsNotNull(java.lang.Class<java.lang.Object> oneExample)

protected abstract voidexecuteBindings()

protected static voidexecuteBindingsOn(ViewDataBinding other)

Calls executeBindingsInternal on the other ViewDataBinding

public voidexecutePendingBindings()

Evaluates the pending bindings, updating any Views that have expressions bound to modified variables.

public static intgetBuildSdkInt()

protected static intgetColorFromResource(View view, int resourceId)

protected static ColorStateListgetColorStateListFromResource(View view, int resourceId)

protected static DrawablegetDrawableFromResource(View view, int resourceId)

protected static java.lang.ObjectgetFrom(java.util.Map<java.lang.Object, java.lang.Object> map, java.lang.Object key)

protected static booleangetFromArray(boolean[] arr[], int index)

protected static bytegetFromArray(byte[] arr[], int index)

protected static chargetFromArray(char[] arr[], int index)

protected static doublegetFromArray(double[] arr[], int index)

protected static floatgetFromArray(float[] arr[], int index)

protected static intgetFromArray(int[] arr[], int index)

protected static longgetFromArray(long[] arr[], int index)

protected static shortgetFromArray(short[] arr[], int index)

protected static java.lang.ObjectgetFromArray(java.lang.Object arr[], int index)

protected static java.lang.ObjectgetFromList(<any> list, int index)

protected static java.lang.ObjectgetFromList(<any> list, int index)

protected static java.lang.ObjectgetFromList(java.util.List<java.lang.Object> list, int index)

protected static booleangetFromList(SparseBooleanArray list, int index)

protected static intgetFromList(SparseIntArray list, int index)

protected static longgetFromList(SparseLongArray list, int index)

protected java.lang.ObjectgetObservedField(int localFieldId)

public ViewgetRoot()

Returns the outermost View in the layout file associated with the Binding.

public abstract booleanhasPendingBindings()

Returns whether the UI needs to be refresh to represent the current data.

public abstract voidinvalidateAll()

Invalidates all binding expressions and requests a new rebind to refresh UI.

protected static java.lang.ObjectmapBindings(DataBindingComponent bindingComponent, View roots[], int numBindings, ViewDataBinding.IncludedLayouts includes, SparseIntArray viewsWithIds)

Walks the view hierarchy under roots and pulls out tagged Views, includes, and views with IDs into an Object[] that is returned.

protected static java.lang.ObjectmapBindings(DataBindingComponent bindingComponent, View root, int numBindings, ViewDataBinding.IncludedLayouts includes, SparseIntArray viewsWithIds)

Walks the view hierarchy under root and pulls out tagged Views, includes, and views with IDs into an Object[] that is returned.

protected abstract booleanonFieldChange(int localFieldId, java.lang.Object object, int fieldId)

Called when an observed object changes.

protected static booleanparse(java.lang.String str, boolean fallback)

protected static byteparse(java.lang.String str, byte fallback)

protected static charparse(java.lang.String str, char fallback)

protected static doubleparse(java.lang.String str, double fallback)

protected static floatparse(java.lang.String str, float fallback)

protected static intparse(java.lang.String str, int fallback)

protected static longparse(java.lang.String str, long fallback)

protected static shortparse(java.lang.String str, short fallback)

protected voidregisterTo(int localFieldId, java.lang.Object observable, androidx.databinding.ViewDataBinding.CreateWeakListener listenerCreator)

public voidremoveOnRebindCallback(OnRebindCallback listener)

Removes a listener that was added in ViewDataBinding.addOnRebindCallback(OnRebindCallback).

protected voidrequestRebind()

protected static booleansafeUnbox(java.lang.Boolean boxed)

protected static bytesafeUnbox(java.lang.Byte boxed)

protected static charsafeUnbox(java.lang.Character boxed)

protected static doublesafeUnbox(java.lang.Double boxed)

protected static floatsafeUnbox(java.lang.Float boxed)

protected static intsafeUnbox(java.lang.Integer boxed)

protected static longsafeUnbox(java.lang.Long boxed)

protected static shortsafeUnbox(java.lang.Short boxed)

protected static voidsetBindingInverseListener(ViewDataBinding binder, InverseBindingListener oldListener, ViewDataBinding.PropertyChangedInverseListener listener)

protected voidsetContainedBinding(ViewDataBinding included)

Used internally to set the containing binding for an included binding to this.

public voidsetLifecycleOwner(LifecycleOwner lifecycleOwner)

Sets the LifecycleOwner that should be used for observing changes of LiveData in this binding.

protected voidsetRootTag(View view)

protected voidsetRootTag(View views[])

protected static voidsetTo(<any> list, int index, java.lang.Object value)

protected static voidsetTo(<any> list, int index, java.lang.Object value)

protected static voidsetTo(boolean[] arr[], int index, boolean value)

protected static voidsetTo(byte[] arr[], int index, byte value)

protected static voidsetTo(char[] arr[], int index, char value)

protected static voidsetTo(double[] arr[], int index, double value)

protected static voidsetTo(float[] arr[], int index, float value)

protected static voidsetTo(int[] arr[], int index, int value)

protected static voidsetTo(java.util.List<java.lang.Object> list, int index, java.lang.Object value)

protected static voidsetTo(long[] arr[], int index, long value)

protected static voidsetTo(java.util.Map<java.lang.Object, java.lang.Object> map, java.lang.Object key, java.lang.Object value)

protected static voidsetTo(short[] arr[], int index, short value)

protected static voidsetTo(SparseBooleanArray list, int index, boolean value)

protected static voidsetTo(SparseIntArray list, int index, int value)

protected static voidsetTo(SparseLongArray list, int index, long value)

protected static voidsetTo(java.lang.Object arr[], int index, java.lang.Object value)

public abstract booleansetVariable(int variableId, java.lang.Object value)

Set a value value in the Binding class.

public voidunbind()

Removes binding listeners to expression variables.

protected booleanunregisterFrom(int localFieldId)

protected booleanupdateLiveDataRegistration(int localFieldId, LiveData<java.lang.Object> observable)

protected booleanupdateRegistration(int localFieldId, Observable observable)

protected booleanupdateRegistration(int localFieldId, ObservableList observable)

protected booleanupdateRegistration(int localFieldId, ObservableMap observable)

from BaseObservableaddOnPropertyChangedCallback, notifyChange, notifyPropertyChanged, removeOnPropertyChangedCallback
from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final java.lang.String BINDING_TAG_PREFIX

Prefix for android:tag on Views with binding. The root View and include tags will not have android:tag attributes and will use ids instead.

protected final DataBindingComponent mBindingComponent

The DataBindingComponent used by this data binding. This is used for BindingAdapters that are instance methods to retrieve the class instance that implements the adapter.

Constructors

protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount)

Methods

protected void setRootTag(View view)

protected void setRootTag(View views[])

public static int getBuildSdkInt()

Returns:

Returns the current build sdk

protected abstract boolean onFieldChange(int localFieldId, java.lang.Object object, int fieldId)

Called when an observed object changes. Sets the appropriate dirty flag if applicable.

Parameters:

localFieldId: The index into mLocalFieldObservers that this Object resides in.
object: The object that has changed.
fieldId: The BR ID of the field being changed or _all if no specific field is being notified.

Returns:

true if this change should cause a change to the UI.

public abstract boolean setVariable(int variableId, java.lang.Object value)

Set a value value in the Binding class.

Typically, the developer will be able to call the subclass's set method directly. For example, if there is a variable x in the Binding, a setX method will be generated. However, there are times when the specific subclass of ViewDataBinding is unknown, so the generated method cannot be discovered without reflection. The setVariable call allows the values of variables to be set without reflection.

Parameters:

variableId: the BR id of the variable to be set. For example, if the variable is x, then variableId will be BR.x.
value: The new value of the variable to be set.

Returns:

true if the variable is declared or used in the binding or false otherwise.

public void setLifecycleOwner(LifecycleOwner lifecycleOwner)

Sets the LifecycleOwner that should be used for observing changes of LiveData in this binding. If a LiveData is in one of the binding expressions and no LifecycleOwner is set, the LiveData will not be observed and updates to it will not be propagated to the UI.

Parameters:

lifecycleOwner: The LifecycleOwner that should be used for observing changes of LiveData in this binding.

public void addOnRebindCallback(OnRebindCallback listener)

Add a listener to be called when reevaluating dirty fields. This also allows automatic updates to be halted, but does not stop explicit calls to ViewDataBinding.executePendingBindings().

Parameters:

listener: The listener to add.

public void removeOnRebindCallback(OnRebindCallback listener)

Removes a listener that was added in ViewDataBinding.addOnRebindCallback(OnRebindCallback).

Parameters:

listener: The listener to remove.

public void executePendingBindings()

Evaluates the pending bindings, updating any Views that have expressions bound to modified variables. This must be run on the UI thread.

protected static void executeBindingsOn(ViewDataBinding other)

Calls executeBindingsInternal on the other ViewDataBinding

protected abstract void executeBindings()

public abstract void invalidateAll()

Invalidates all binding expressions and requests a new rebind to refresh UI.

public abstract boolean hasPendingBindings()

Returns whether the UI needs to be refresh to represent the current data.

Returns:

true if any field has changed and the binding should be evaluated.

public void unbind()

Removes binding listeners to expression variables.

public View getRoot()

Returns the outermost View in the layout file associated with the Binding. If this binding is for a merge layout file, this will return the first root in the merge tag.

Returns:

the outermost View in the layout file associated with the Binding.

protected boolean unregisterFrom(int localFieldId)

protected void requestRebind()

protected java.lang.Object getObservedField(int localFieldId)

protected boolean updateRegistration(int localFieldId, Observable observable)

protected boolean updateRegistration(int localFieldId, ObservableList observable)

protected boolean updateRegistration(int localFieldId, ObservableMap observable)

protected boolean updateLiveDataRegistration(int localFieldId, LiveData<java.lang.Object> observable)

protected void ensureBindingComponentIsNotNull(java.lang.Class<java.lang.Object> oneExample)

protected void registerTo(int localFieldId, java.lang.Object observable, androidx.databinding.ViewDataBinding.CreateWeakListener listenerCreator)

protected static ViewDataBinding bind(DataBindingComponent bindingComponent, View view, int layoutId)

protected static java.lang.Object mapBindings(DataBindingComponent bindingComponent, View root, int numBindings, ViewDataBinding.IncludedLayouts includes, SparseIntArray viewsWithIds)

Walks the view hierarchy under root and pulls out tagged Views, includes, and views with IDs into an Object[] that is returned. This is used to walk the view hierarchy once to find all bound and ID'd views.

Parameters:

bindingComponent: The binding component to use with this binding.
root: The root of the view hierarchy to walk.
numBindings: The total number of ID'd views, views with expressions, and includes
includes: The include layout information, indexed by their container's index.
viewsWithIds: Indexes of views that don't have tags, but have IDs.

Returns:

An array of size numBindings containing all Views in the hierarchy that have IDs (with elements in viewsWithIds), are tagged containing expressions, or the bindings for included layouts.

protected static boolean parse(java.lang.String str, boolean fallback)

protected static byte parse(java.lang.String str, byte fallback)

protected static short parse(java.lang.String str, short fallback)

protected static int parse(java.lang.String str, int fallback)

protected static long parse(java.lang.String str, long fallback)

protected static float parse(java.lang.String str, float fallback)

protected static double parse(java.lang.String str, double fallback)

protected static char parse(java.lang.String str, char fallback)

protected static int getColorFromResource(View view, int resourceId)

protected static ColorStateList getColorStateListFromResource(View view, int resourceId)

protected static Drawable getDrawableFromResource(View view, int resourceId)

protected static java.lang.Object getFromArray(java.lang.Object arr[], int index)

protected static void setTo(java.lang.Object arr[], int index, java.lang.Object value)

protected static boolean getFromArray(boolean[] arr[], int index)

protected static void setTo(boolean[] arr[], int index, boolean value)

protected static byte getFromArray(byte[] arr[], int index)

protected static void setTo(byte[] arr[], int index, byte value)

protected static short getFromArray(short[] arr[], int index)

protected static void setTo(short[] arr[], int index, short value)

protected static char getFromArray(char[] arr[], int index)

protected static void setTo(char[] arr[], int index, char value)

protected static int getFromArray(int[] arr[], int index)

protected static void setTo(int[] arr[], int index, int value)

protected static long getFromArray(long[] arr[], int index)

protected static void setTo(long[] arr[], int index, long value)

protected static float getFromArray(float[] arr[], int index)

protected static void setTo(float[] arr[], int index, float value)

protected static double getFromArray(double[] arr[], int index)

protected static void setTo(double[] arr[], int index, double value)

protected static java.lang.Object getFromList(java.util.List<java.lang.Object> list, int index)

protected static void setTo(java.util.List<java.lang.Object> list, int index, java.lang.Object value)

protected static java.lang.Object getFromList(<any> list, int index)

protected static void setTo(<any> list, int index, java.lang.Object value)

protected static java.lang.Object getFromList(<any> list, int index)

protected static void setTo(<any> list, int index, java.lang.Object value)

protected static boolean getFromList(SparseBooleanArray list, int index)

protected static void setTo(SparseBooleanArray list, int index, boolean value)

protected static int getFromList(SparseIntArray list, int index)

protected static void setTo(SparseIntArray list, int index, int value)

protected static long getFromList(SparseLongArray list, int index)

protected static void setTo(SparseLongArray list, int index, long value)

protected static java.lang.Object getFrom(java.util.Map<java.lang.Object, java.lang.Object> map, java.lang.Object key)

protected static void setTo(java.util.Map<java.lang.Object, java.lang.Object> map, java.lang.Object key, java.lang.Object value)

protected static void setBindingInverseListener(ViewDataBinding binder, InverseBindingListener oldListener, ViewDataBinding.PropertyChangedInverseListener listener)

protected static int safeUnbox(java.lang.Integer boxed)

protected static long safeUnbox(java.lang.Long boxed)

protected static short safeUnbox(java.lang.Short boxed)

protected static byte safeUnbox(java.lang.Byte boxed)

protected static char safeUnbox(java.lang.Character boxed)

protected static double safeUnbox(java.lang.Double boxed)

protected static float safeUnbox(java.lang.Float boxed)

protected static boolean safeUnbox(java.lang.Boolean boxed)

protected void setContainedBinding(ViewDataBinding included)

Used internally to set the containing binding for an included binding to this.

protected static java.lang.Object mapBindings(DataBindingComponent bindingComponent, View roots[], int numBindings, ViewDataBinding.IncludedLayouts includes, SparseIntArray viewsWithIds)

Walks the view hierarchy under roots and pulls out tagged Views, includes, and views with IDs into an Object[] that is returned. This is used to walk the view hierarchy once to find all bound and ID'd views.

Parameters:

bindingComponent: The binding component to use with this binding.
roots: The root Views of the view hierarchy to walk. This is used with merge tags.
numBindings: The total number of ID'd views, views with expressions, and includes
includes: The include layout information, indexed by their container's index.
viewsWithIds: Indexes of views that don't have tags, but have IDs.

Returns:

An array of size numBindings containing all Views in the hierarchy that have IDs (with elements in viewsWithIds), are tagged containing expressions, or the bindings for included layouts.

Source

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.databinding;

import android.annotation.TargetApi;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.OnLifecycleEvent;
import android.content.res.ColorStateList;
import androidx.databinding.CallbackRegistry.NotifierCallback;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import android.util.LongSparseArray;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.view.Choreographer;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;

import androidx.databinding.library.R;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Map;

/**
 * Base class for generated data binding classes. If possible, the generated binding should
 * be instantiated using one of its generated static bind or inflate methods. If the specific
 * binding is unknown, {@link DataBindingUtil#bind(View)} or
 * {@link DataBindingUtil#inflate(LayoutInflater, int, ViewGroup, boolean)} should be used.
 */
public abstract class ViewDataBinding extends BaseObservable {

    /**
     * Instead of directly accessing Build.VERSION.SDK_INT, generated code uses this value so that
     * we can test API dependent behavior.
     */
    static int SDK_INT = VERSION.SDK_INT;

    private static final int REBIND = 1;
    private static final int HALTED = 2;
    private static final int REBOUND = 3;

    /**
     * Prefix for android:tag on Views with binding. The root View and include tags will not have
     * android:tag attributes and will use ids instead.
     *
     * @hide
     */
    public static final String BINDING_TAG_PREFIX = "binding_";

    // The length of BINDING_TAG_PREFIX prevents calling length repeatedly.
    private static final int BINDING_NUMBER_START = BINDING_TAG_PREFIX.length();

    private static final boolean USE_CHOREOGRAPHER = SDK_INT >= 16;

    /**
     * Method object extracted out to attach a listener to a bound Observable object.
     */
    private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
            return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
        }
    };

    /**
     * Method object extracted out to attach a listener to a bound ObservableList object.
     */
    private static final CreateWeakListener CREATE_LIST_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
            return new WeakListListener(viewDataBinding, localFieldId).getListener();
        }
    };

    /**
     * Method object extracted out to attach a listener to a bound ObservableMap object.
     */
    private static final CreateWeakListener CREATE_MAP_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
            return new WeakMapListener(viewDataBinding, localFieldId).getListener();
        }
    };

    /**
     * Method object extracted out to attach a listener to a bound LiveData object.
     */
    private static final CreateWeakListener CREATE_LIVE_DATA_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
            return new LiveDataListener(viewDataBinding, localFieldId).getListener();
        }
    };

    private static final CallbackRegistry.NotifierCallback<OnRebindCallback, ViewDataBinding, Void>
        REBIND_NOTIFIER = new NotifierCallback<OnRebindCallback, ViewDataBinding, Void>() {
        @Override
        public void onNotifyCallback(OnRebindCallback callback, ViewDataBinding sender, int mode,
                Void arg2) {
            switch (mode) {
                case REBIND:
                    if (!callback.onPreBind(sender)) {
                        sender.mRebindHalted = true;
                    }
                    break;
                case HALTED:
                    callback.onCanceled(sender);
                    break;
                case REBOUND:
                    callback.onBound(sender);
                    break;
            }
        }
    };

    private static final ReferenceQueue<ViewDataBinding> sReferenceQueue = new ReferenceQueue<>();

    private static final OnAttachStateChangeListener ROOT_REATTACHED_LISTENER;

    static {
        if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
            ROOT_REATTACHED_LISTENER = null;
        } else {
            ROOT_REATTACHED_LISTENER = new OnAttachStateChangeListener() {
                @TargetApi(VERSION_CODES.KITKAT)
                @Override
                public void onViewAttachedToWindow(View v) {
                    // execute the pending bindings.
                    final ViewDataBinding binding = getBinding(v);
                    binding.mRebindRunnable.run();
                    v.removeOnAttachStateChangeListener(this);
                }

                @Override
                public void onViewDetachedFromWindow(View v) {
                }
            };
        }
    }

    /**
     * Runnable executed on animation heartbeat to rebind the dirty Views.
     */
    private final Runnable mRebindRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (this) {
                mPendingRebind = false;
            }
            processReferenceQueue();

            if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
                // Nested so that we don't get a lint warning in IntelliJ
                if (!mRoot.isAttachedToWindow()) {
                    // Don't execute the pending bindings until the View
                    // is attached again.
                    mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
                    mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
                    return;
                }
            }
            executePendingBindings();
        }
    };

    /**
     * Flag indicates that there are pending bindings that need to be reevaluated.
     */
    private boolean mPendingRebind = false;

    /**
     * Indicates that a onPreBind has stopped the executePendingBindings call.
     */
    private boolean mRebindHalted = false;

    /**
     * The observed expressions.
     */
    private WeakListener[] mLocalFieldObservers;

    /**
     * The root View that this Binding is associated with.
     */
    private final View mRoot;

    /**
     * The collection of OnRebindCallbacks.
     */
    private CallbackRegistry<OnRebindCallback, ViewDataBinding, Void> mRebindCallbacks;

    /**
     * Flag to prevent reentrant executePendingBinding calls.
     */
    private boolean mIsExecutingPendingBindings;

    // null api < 16
    private Choreographer mChoreographer;

    private final Choreographer.FrameCallback mFrameCallback;

    // null api >= 16
    private Handler mUIThreadHandler;

    /**
     * The DataBindingComponent used by this data binding. This is used for BindingAdapters
     * that are instance methods to retrieve the class instance that implements the
     * adapter.
     *
     * @hide
     */
    protected final DataBindingComponent mBindingComponent;

    /**
     * If this ViewDataBinding is an include in another ViewDataBinding, this is the one
     * that contains this. mContainingBinding is used to order executeBindings -- containing
     * bindings should execute prior to included bindings.
     */
    private ViewDataBinding mContainingBinding;

    /**
     * Track the LifecycleOwner set in {@link #setLifecycleOwner(LifecycleOwner)}. Set as
     * Object so that the class can be compiled without requiring the LifecycleOwner dependency.
     */
    private LifecycleOwner mLifecycleOwner;

    /**
     * Listener when mLifecycleOwner is non-null to allow delaying rebind calls while the
     * status is less than started.
     */
    private OnStartListener mOnStartListener;

    /**
     * When LiveData first registers for a change, it notifies immediately that there was a
     * change. This flag identifies that we've just started observing LiveData and we should ignore
     * the change notification.
     */
    private boolean mInLiveDataRegisterObserver;

    /**
     * @hide
     */
    protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {
        mBindingComponent = bindingComponent;
        mLocalFieldObservers = new WeakListener[localFieldCount];
        this.mRoot = root;
        if (Looper.myLooper() == null) {
            throw new IllegalStateException("DataBinding must be created in view's UI Thread");
        }
        if (USE_CHOREOGRAPHER) {
            mChoreographer = Choreographer.getInstance();
            mFrameCallback = new Choreographer.FrameCallback() {
                @Override
                public void doFrame(long frameTimeNanos) {
                    mRebindRunnable.run();
                }
            };
        } else {
            mFrameCallback = null;
            mUIThreadHandler = new Handler(Looper.myLooper());
        }
    }

    /**
     * @hide
     */
    protected void setRootTag(View view) {
        view.setTag(R.id.dataBinding, this);
    }

    /**
     * @hide
     */
    protected void setRootTag(View[] views) {
        for (View view : views) {
            view.setTag(R.id.dataBinding, this);
        }
    }

    /**
     * @hide
     *
     * @return Returns the current build sdk
     */
    public static int getBuildSdkInt() {
        return SDK_INT;
    }

    /**
     * Called when an observed object changes. Sets the appropriate dirty flag if applicable.
     * @param localFieldId The index into mLocalFieldObservers that this Object resides in.
     * @param object The object that has changed.
     * @param fieldId The BR ID of the field being changed or _all if
     *                no specific field is being notified.
     * @return true if this change should cause a change to the UI.
     * @hide
     */
    protected abstract boolean onFieldChange(int localFieldId, Object object, int fieldId);

    /**
     * Set a value value in the Binding class.
     * <p>
     * Typically, the developer will be able to call the subclass's set method directly. For
     * example, if there is a variable <code>x</code> in the Binding, a <code>setX</code> method
     * will be generated. However, there are times when the specific subclass of ViewDataBinding
     * is unknown, so the generated method cannot be discovered without reflection. The
     * setVariable call allows the values of variables to be set without reflection.
     *
     * @param variableId the BR id of the variable to be set. For example, if the variable is
     *                   <code>x</code>, then variableId will be <code>BR.x</code>.
     * @param value The new value of the variable to be set.
     * @return <code>true</code> if the variable is declared or used in the binding or
     * <code>false</code> otherwise.
     */
    public abstract boolean setVariable(int variableId, @Nullable Object value);

    /**
     * Sets the {@link LifecycleOwner} that should be used for observing changes of
     * LiveData in this binding. If a {@link LiveData} is in one of the binding expressions
     * and no LifecycleOwner is set, the LiveData will not be observed and updates to it
     * will not be propagated to the UI.
     *
     * @param lifecycleOwner The LifecycleOwner that should be used for observing changes of
     *                       LiveData in this binding.
     */
    @MainThread
    public void setLifecycleOwner(@Nullable LifecycleOwner lifecycleOwner) {
        if (mLifecycleOwner == lifecycleOwner) {
            return;
        }
        if (mLifecycleOwner != null) {
            mLifecycleOwner.getLifecycle().removeObserver(mOnStartListener);
        }
        mLifecycleOwner = lifecycleOwner;
        if (lifecycleOwner != null) {
            if (mOnStartListener == null) {
                mOnStartListener = new OnStartListener();
            }
            lifecycleOwner.getLifecycle().addObserver(mOnStartListener);
        }
        for (WeakListener<?> weakListener : mLocalFieldObservers) {
            if (weakListener != null) {
                weakListener.setLifecycleOwner(lifecycleOwner);
            }
        }
    }

    /**
     * Add a listener to be called when reevaluating dirty fields. This also allows automatic
     * updates to be halted, but does not stop explicit calls to {@link #executePendingBindings()}.
     *
     * @param listener The listener to add.
     */
    public void addOnRebindCallback(@NonNull OnRebindCallback listener) {
        if (mRebindCallbacks == null) {
            mRebindCallbacks = new CallbackRegistry<OnRebindCallback, ViewDataBinding, Void>(REBIND_NOTIFIER);
        }
        mRebindCallbacks.add(listener);
    }

    /**
     * Removes a listener that was added in {@link #addOnRebindCallback(OnRebindCallback)}.
     *
     * @param listener The listener to remove.
     */
    public void removeOnRebindCallback(@NonNull OnRebindCallback listener) {
        if (mRebindCallbacks != null) {
            mRebindCallbacks.remove(listener);
        }
    }

    /**
     * Evaluates the pending bindings, updating any Views that have expressions bound to
     * modified variables. This <b>must</b> be run on the UI thread.
     */
    public void executePendingBindings() {
        if (mContainingBinding == null) {
            executeBindingsInternal();
        } else {
            mContainingBinding.executePendingBindings();
        }
    }

    /**
     * Evaluates the pending bindings without executing the parent bindings.
     */
    private void executeBindingsInternal() {
        if (mIsExecutingPendingBindings) {
            requestRebind();
            return;
        }
        if (!hasPendingBindings()) {
            return;
        }
        mIsExecutingPendingBindings = true;
        mRebindHalted = false;
        if (mRebindCallbacks != null) {
            mRebindCallbacks.notifyCallbacks(this, REBIND, null);

            // The onRebindListeners will change mPendingHalted
            if (mRebindHalted) {
                mRebindCallbacks.notifyCallbacks(this, HALTED, null);
            }
        }
        if (!mRebindHalted) {
            executeBindings();
            if (mRebindCallbacks != null) {
                mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
            }
        }
        mIsExecutingPendingBindings = false;
    }

    /**
     * Calls executeBindingsInternal on the other ViewDataBinding
     *
     * @hide
     */
    protected static void executeBindingsOn(ViewDataBinding other) {
        other.executeBindingsInternal();
    }


    void forceExecuteBindings() {
        executeBindings();
    }

    /**
     * @hide
     */
    protected abstract void executeBindings();

    /**
     * Invalidates all binding expressions and requests a new rebind to refresh UI.
     */
    public abstract void invalidateAll();

    /**
     * Returns whether the UI needs to be refresh to represent the current data.
     *
     * @return true if any field has changed and the binding should be evaluated.
     */
    public abstract boolean hasPendingBindings();

    /**
     * Removes binding listeners to expression variables.
     */
    public void unbind() {
        for (WeakListener weakListener : mLocalFieldObservers) {
            if (weakListener != null) {
                weakListener.unregister();
            }
        }
    }

    static ViewDataBinding getBinding(View v) {
        if (v != null) {
            return (ViewDataBinding) v.getTag(R.id.dataBinding);
        }
        return null;
    }

    /**
     * Returns the outermost View in the layout file associated with the Binding. If this
     * binding is for a merge layout file, this will return the first root in the merge tag.
     *
     * @return the outermost View in the layout file associated with the Binding.
     */
    @NonNull
    public View getRoot() {
        return mRoot;
    }

    private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
        if (mInLiveDataRegisterObserver) {
            // We're in LiveData registration, which always results in a field change
            // that we can ignore. The value will be read immediately after anyway, so
            // there is no need to be dirty.
            return;
        }
        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
        if (result) {
            requestRebind();
        }
    }

    /**
     * @hide
     */
    protected boolean unregisterFrom(int localFieldId) {
        WeakListener listener = mLocalFieldObservers[localFieldId];
        if (listener != null) {
            return listener.unregister();
        }
        return false;
    }

    /**
     * @hide
     */
    protected void requestRebind() {
        if (mContainingBinding != null) {
            mContainingBinding.requestRebind();
        } else {
            synchronized (this) {
                if (mPendingRebind) {
                    return;
                }
                mPendingRebind = true;
            }
            if (mLifecycleOwner != null) {
                Lifecycle.State state = mLifecycleOwner.getLifecycle().getCurrentState();
                if (!state.isAtLeast(Lifecycle.State.STARTED)) {
                    return; // wait until lifecycle owner is started
                }
            }
            if (USE_CHOREOGRAPHER) {
                mChoreographer.postFrameCallback(mFrameCallback);
            } else {
                mUIThreadHandler.post(mRebindRunnable);
            }
        }
    }

    /**
     * @hide
     */
    protected Object getObservedField(int localFieldId) {
        WeakListener listener = mLocalFieldObservers[localFieldId];
        if (listener == null) {
            return null;
        }
        return listener.getTarget();
    }

    private boolean updateRegistration(int localFieldId, Object observable,
            CreateWeakListener listenerCreator) {
        if (observable == null) {
            return unregisterFrom(localFieldId);
        }
        WeakListener listener = mLocalFieldObservers[localFieldId];
        if (listener == null) {
            registerTo(localFieldId, observable, listenerCreator);
            return true;
        }
        if (listener.getTarget() == observable) {
            return false;//nothing to do, same object
        }
        unregisterFrom(localFieldId);
        registerTo(localFieldId, observable, listenerCreator);
        return true;
    }

    /**
     * @hide
     */
    protected boolean updateRegistration(int localFieldId, Observable observable) {
        return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
    }

    /**
     * @hide
     */
    protected boolean updateRegistration(int localFieldId, ObservableList observable) {
        return updateRegistration(localFieldId, observable, CREATE_LIST_LISTENER);
    }

    /**
     * @hide
     */
    protected boolean updateRegistration(int localFieldId, ObservableMap observable) {
        return updateRegistration(localFieldId, observable, CREATE_MAP_LISTENER);
    }

    /**
     * @hide
     */
    protected boolean updateLiveDataRegistration(int localFieldId, LiveData<?> observable) {
        mInLiveDataRegisterObserver = true;
        try {
            return updateRegistration(localFieldId, observable, CREATE_LIVE_DATA_LISTENER);
        } finally {
            mInLiveDataRegisterObserver = false;
        }
    }

    /**
     * @hide
     */
    protected void ensureBindingComponentIsNotNull(Class<?> oneExample) {
        if (mBindingComponent == null) {
            String errorMessage = "Required DataBindingComponent is null in class " +
                    getClass().getSimpleName() + ". A BindingAdapter in " +
                    oneExample.getCanonicalName() +
                    " is not static and requires an object to use, retrieved from the " +
                    "DataBindingComponent. If you don't use an inflation method taking a " +
                    "DataBindingComponent, use DataBindingUtil.setDefaultComponent or " +
                    "make all BindingAdapter methods static.";
            throw new IllegalStateException(errorMessage);
        }
    }

    /**
     * @hide
     */
    protected void registerTo(int localFieldId, Object observable,
            CreateWeakListener listenerCreator) {
        if (observable == null) {
            return;
        }
        WeakListener listener = mLocalFieldObservers[localFieldId];
        if (listener == null) {
            listener = listenerCreator.create(this, localFieldId);
            mLocalFieldObservers[localFieldId] = listener;
            if (mLifecycleOwner != null) {
                listener.setLifecycleOwner(mLifecycleOwner);
            }
        }
        listener.setTarget(observable);
    }

    /**
     * @hide
     */
    protected static ViewDataBinding bind(DataBindingComponent bindingComponent, View view,
            int layoutId) {
        return DataBindingUtil.bind(bindingComponent, view, layoutId);
    }

    /**
     * Walks the view hierarchy under root and pulls out tagged Views, includes, and views with
     * IDs into an Object[] that is returned. This is used to walk the view hierarchy once to find
     * all bound and ID'd views.
     *
     * @param bindingComponent The binding component to use with this binding.
     * @param root The root of the view hierarchy to walk.
     * @param numBindings The total number of ID'd views, views with expressions, and includes
     * @param includes The include layout information, indexed by their container's index.
     * @param viewsWithIds Indexes of views that don't have tags, but have IDs.
     * @return An array of size numBindings containing all Views in the hierarchy that have IDs
     * (with elements in viewsWithIds), are tagged containing expressions, or the bindings for
     * included layouts.
     * @hide
     */
    protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
            int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
        Object[] bindings = new Object[numBindings];
        mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
        return bindings;
    }

    /** @hide */
    protected static boolean parse(String str, boolean fallback) {
        if (str == null) {
            return fallback;
        }
        return Boolean.parseBoolean(str);
    }

    /** @hide */
    protected static byte parse(String str, byte fallback) {
        try {
            return Byte.parseByte(str);
        } catch (NumberFormatException e) {
            return fallback;
        }
    }

    /** @hide */
    protected static short parse(String str, short fallback) {
        try {
            return Short.parseShort(str);
        } catch (NumberFormatException e) {
            return fallback;
        }
    }

    /** @hide */
    protected static int parse(String str, int fallback) {
        try {
            return Integer.parseInt(str);
        } catch (NumberFormatException e) {
            return fallback;
        }
    }

    /** @hide */
    protected static long parse(String str, long fallback) {
        try {
            return Long.parseLong(str);
        } catch (NumberFormatException e) {
            return fallback;
        }
    }

    /** @hide */
    protected static float parse(String str, float fallback) {
        try {
            return Float.parseFloat(str);
        } catch (NumberFormatException e) {
            return fallback;
        }
    }

    /** @hide */
    protected static double parse(String str, double fallback) {
        try {
            return Double.parseDouble(str);
        } catch (NumberFormatException e) {
            return fallback;
        }
    }

    /** @hide */
    protected static char parse(String str, char fallback) {
        if (str == null || str.isEmpty()) {
            return fallback;
        }
        return str.charAt(0);
    }

    /** @hide */
    protected static int getColorFromResource(View view, int resourceId) {
        if (VERSION.SDK_INT >= VERSION_CODES.M) {
            return view.getContext().getColor(resourceId);
        } else {
            return view.getResources().getColor(resourceId);
        }
    }

    /** @hide */
    protected static ColorStateList getColorStateListFromResource(View view, int resourceId) {
        if (VERSION.SDK_INT >= VERSION_CODES.M) {
            return view.getContext().getColorStateList(resourceId);
        } else {
            return view.getResources().getColorStateList(resourceId);
        }
    }

    /** @hide */
    protected static Drawable getDrawableFromResource(View view, int resourceId) {
        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
            return view.getContext().getDrawable(resourceId);
        } else {
            return view.getResources().getDrawable(resourceId);
        }
    }

    /** @hide */
    protected static <T> T getFromArray(T[] arr, int index) {
        if (arr == null || index < 0 || index >= arr.length) {
            return null;
        }
        return arr[index];
    }

    /** @hide */
    protected static <T> void setTo(T[] arr, int index, T value) {
        if (arr == null || index < 0 || index >= arr.length) {
            return;
        }
        arr[index] = value;
    }

    /** @hide */
    protected static boolean getFromArray(boolean[] arr, int index) {
        if (arr == null || index < 0 || index >= arr.length) {
            return false;
        }
        return arr[index];
    }

    /** @hide */
    protected static void setTo(boolean[] arr, int index, boolean value) {
        if (arr == null || index < 0 || index >= arr.length) {
            return;
        }
        arr[index] = value;
    }

    /** @hide */
    protected static byte getFromArray(byte[] arr, int index) {
        if (arr == null || index < 0 || index >= arr.length) {
            return 0;
        }
        return arr[index];
    }

    /** @hide */
    protected static void setTo(byte[] arr, int index, byte value) {
        if (arr == null || index < 0 || index >= arr.length) {
            return;
        }
        arr[index] = value;
    }

    /** @hide */
    protected static short getFromArray(short[] arr, int index) {
        if (arr == null || index < 0 || index >= arr.length) {
            return 0;
        }
        return arr[index];
    }

    /** @hide */
    protected static void setTo(short[] arr, int index, short value) {
        if (arr == null || index < 0 || index >= arr.length) {
            return;
        }
        arr[index] = value;
    }

    /** @hide */
    protected static char getFromArray(char[] arr, int index) {
        if (arr == null || index < 0 || index >= arr.length) {
            return 0;
        }
        return arr[index];
    }

    /** @hide */
    protected static void setTo(char[] arr, int index, char value) {
        if (arr == null || index < 0 || index >= arr.length) {
            return;
        }
        arr[index] = value;
    }

    /** @hide */
    protected static int getFromArray(int[] arr, int index) {
        if (arr == null || index < 0 || index >= arr.length) {
            return 0;
        }
        return arr[index];
    }

    /** @hide */
    protected static void setTo(int[] arr, int index, int value) {
        if (arr == null || index < 0 || index >= arr.length) {
            return;
        }
        arr[index] = value;
    }

    /** @hide */
    protected static long getFromArray(long[] arr, int index) {
        if (arr == null || index < 0 || index >= arr.length) {
            return 0;
        }
        return arr[index];
    }

    /** @hide */
    protected static void setTo(long[] arr, int index, long value) {
        if (arr == null || index < 0 || index >= arr.length) {
            return;
        }
        arr[index] = value;
    }

    /** @hide */
    protected static float getFromArray(float[] arr, int index) {
        if (arr == null || index < 0 || index >= arr.length) {
            return 0;
        }
        return arr[index];
    }

    /** @hide */
    protected static void setTo(float[] arr, int index, float value) {
        if (arr == null || index < 0 || index >= arr.length) {
            return;
        }
        arr[index] = value;
    }

    /** @hide */
    protected static double getFromArray(double[] arr, int index) {
        if (arr == null || index < 0 || index >= arr.length) {
            return 0;
        }
        return arr[index];
    }

    /** @hide */
    protected static void setTo(double[] arr, int index, double value) {
        if (arr == null || index < 0 || index >= arr.length) {
            return;
        }
        arr[index] = value;
    }

    /** @hide */
    protected static <T> T getFromList(List<T> list, int index) {
        if (list == null || index < 0 || index >= list.size()) {
            return null;
        }
        return list.get(index);
    }

    /** @hide */
    protected static <T> void setTo(List<T> list, int index, T value) {
        if (list == null || index < 0 || index >= list.size()) {
            return;
        }
        list.set(index, value);
    }

    /** @hide */
    protected static <T> T getFromList(SparseArray<T> list, int index) {
        if (list == null || index < 0) {
            return null;
        }
        return list.get(index);
    }

    /** @hide */
    protected static <T> void setTo(SparseArray<T> list, int index, T value) {
        if (list == null || index < 0 || index >= list.size()) {
            return;
        }
        list.put(index, value);
    }

    /** @hide */
    @TargetApi(VERSION_CODES.JELLY_BEAN)
    protected static <T> T getFromList(LongSparseArray<T> list, int index) {
        if (list == null || index < 0) {
            return null;
        }
        return list.get(index);
    }

    /** @hide */
    @TargetApi(VERSION_CODES.JELLY_BEAN)
    protected static <T> void setTo(LongSparseArray<T> list, int index, T value) {
        if (list == null || index < 0 || index >= list.size()) {
            return;
        }
        list.put(index, value);
    }

    /** @hide */
    protected static <T> T getFromList(androidx.collection.LongSparseArray<T> list, int index) {
        if (list == null || index < 0) {
            return null;
        }
        return list.get(index);
    }

    /** @hide */
    protected static <T> void setTo(androidx.collection.LongSparseArray<T> list, int index,
            T value) {
        if (list == null || index < 0 || index >= list.size()) {
            return;
        }
        list.put(index, value);
    }

    /** @hide */
    protected static boolean getFromList(SparseBooleanArray list, int index) {
        if (list == null || index < 0) {
            return false;
        }
        return list.get(index);
    }

    /** @hide */
    protected static void setTo(SparseBooleanArray list, int index, boolean value) {
        if (list == null || index < 0 || index >= list.size()) {
            return;
        }
        list.put(index, value);
    }

    /** @hide */
    protected static int getFromList(SparseIntArray list, int index) {
        if (list == null || index < 0) {
            return 0;
        }
        return list.get(index);
    }

    /** @hide */
    protected static void setTo(SparseIntArray list, int index, int value) {
        if (list == null || index < 0 || index >= list.size()) {
            return;
        }
        list.put(index, value);
    }

    /** @hide */
    @TargetApi(VERSION_CODES.JELLY_BEAN_MR2)
    protected static long getFromList(SparseLongArray list, int index) {
        if (list == null || index < 0) {
            return 0;
        }
        return list.get(index);
    }

    /** @hide */
    @TargetApi(VERSION_CODES.JELLY_BEAN_MR2)
    protected static void setTo(SparseLongArray list, int index, long value) {
        if (list == null || index < 0 || index >= list.size()) {
            return;
        }
        list.put(index, value);
    }

    /** @hide */
    protected static <K, T> T getFrom(Map<K, T> map, K key) {
        if (map == null) {
            return null;
        }
        return map.get(key);
    }

    /** @hide */
    protected static <K, T> void setTo(Map<K, T> map, K key, T value) {
        if (map == null) {
            return;
        }
        map.put(key, value);
    }

    /** @hide */
    protected static void setBindingInverseListener(ViewDataBinding binder,
            InverseBindingListener oldListener, PropertyChangedInverseListener listener) {
        if (oldListener != listener) {
            if (oldListener != null) {
                binder.removeOnPropertyChangedCallback(
                        (PropertyChangedInverseListener) oldListener);
            }
            if (listener != null) {
                binder.addOnPropertyChangedCallback(listener);
            }
        }
    }

    /** @hide */
    protected static int safeUnbox(java.lang.Integer boxed) {
        return boxed == null ? 0 : (int)boxed;
    }

    /** @hide */
    protected static long safeUnbox(java.lang.Long boxed) {
        return boxed == null ? 0L : (long)boxed;
    }

    /** @hide */
    protected static short safeUnbox(java.lang.Short boxed) {
        return boxed == null ? 0 : (short)boxed;
    }

    /** @hide */
    protected static byte safeUnbox(java.lang.Byte boxed) {
        return boxed == null ? 0 : (byte)boxed;
    }

    /** @hide */
    protected static char safeUnbox(java.lang.Character boxed) {
        return boxed == null ? '\u0000' : (char)boxed;
    }

    /** @hide */
    protected static double safeUnbox(java.lang.Double boxed) {
        return boxed == null ? 0.0 : (double)boxed;
    }

    /** @hide */
    protected static float safeUnbox(java.lang.Float boxed) {
        return boxed == null ? 0f : (float)boxed;
    }

    /** @hide */
    protected static boolean safeUnbox(java.lang.Boolean boxed) {
        return boxed == null ? false : (boolean)boxed;
    }

    /**
     * Used internally to set the containing binding for an included binding to this.
     *
     * @hide
     */
    protected void setContainedBinding(ViewDataBinding included) {
        if (included != null) {
            included.mContainingBinding = this;
        }
    }

    /**
     * Walks the view hierarchy under roots and pulls out tagged Views, includes, and views with
     * IDs into an Object[] that is returned. This is used to walk the view hierarchy once to find
     * all bound and ID'd views.
     *
     * @param bindingComponent The binding component to use with this binding.
     * @param roots The root Views of the view hierarchy to walk. This is used with merge tags.
     * @param numBindings The total number of ID'd views, views with expressions, and includes
     * @param includes The include layout information, indexed by their container's index.
     * @param viewsWithIds Indexes of views that don't have tags, but have IDs.
     * @return An array of size numBindings containing all Views in the hierarchy that have IDs
     * (with elements in viewsWithIds), are tagged containing expressions, or the bindings for
     * included layouts.
     * @hide
     */
    protected static Object[] mapBindings(DataBindingComponent bindingComponent, View[] roots,
            int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
        Object[] bindings = new Object[numBindings];
        for (int i = 0; i < roots.length; i++) {
            mapBindings(bindingComponent, roots[i], bindings, includes, viewsWithIds, true);
        }
        return bindings;
    }

    private static void mapBindings(DataBindingComponent bindingComponent, View view,
            Object[] bindings, IncludedLayouts includes, SparseIntArray viewsWithIds,
            boolean isRoot) {
        final int indexInIncludes;
        final ViewDataBinding existingBinding = getBinding(view);
        if (existingBinding != null) {
            return;
        }
        Object objTag = view.getTag();
        final String tag = (objTag instanceof String) ? (String) objTag : null;
        boolean isBound = false;
        if (isRoot && tag != null && tag.startsWith("layout")) {
            final int underscoreIndex = tag.lastIndexOf('_');
            if (underscoreIndex > 0 && isNumeric(tag, underscoreIndex + 1)) {
                final int index = parseTagInt(tag, underscoreIndex + 1);
                if (bindings[index] == null) {
                    bindings[index] = view;
                }
                indexInIncludes = includes == null ? -1 : index;
                isBound = true;
            } else {
                indexInIncludes = -1;
            }
        } else if (tag != null && tag.startsWith(BINDING_TAG_PREFIX)) {
            int tagIndex = parseTagInt(tag, BINDING_NUMBER_START);
            if (bindings[tagIndex] == null) {
                bindings[tagIndex] = view;
            }
            isBound = true;
            indexInIncludes = includes == null ? -1 : tagIndex;
        } else {
            // Not a bound view
            indexInIncludes = -1;
        }
        if (!isBound) {
            final int id = view.getId();
            if (id > 0) {
                int index;
                if (viewsWithIds != null && (index = viewsWithIds.get(id, -1)) >= 0 &&
                        bindings[index] == null) {
                    bindings[index] = view;
                }
            }
        }

        if (view instanceof  ViewGroup) {
            final ViewGroup viewGroup = (ViewGroup) view;
            final int count = viewGroup.getChildCount();
            int minInclude = 0;
            for (int i = 0; i < count; i++) {
                final View child = viewGroup.getChildAt(i);
                boolean isInclude = false;
                if (indexInIncludes >= 0 && child.getTag() instanceof String) {
                    String childTag = (String) child.getTag();
                    if (childTag.endsWith("_0") &&
                            childTag.startsWith("layout") && childTag.indexOf('/') > 0) {
                        // This *could* be an include. Test against the expected includes.
                        int includeIndex = findIncludeIndex(childTag, minInclude,
                                includes, indexInIncludes);
                        if (includeIndex >= 0) {
                            isInclude = true;
                            minInclude = includeIndex + 1;
                            final int index = includes.indexes[indexInIncludes][includeIndex];
                            final int layoutId = includes.layoutIds[indexInIncludes][includeIndex];
                            int lastMatchingIndex = findLastMatching(viewGroup, i);
                            if (lastMatchingIndex == i) {
                                bindings[index] = DataBindingUtil.bind(bindingComponent, child,
                                        layoutId);
                            } else {
                                final int includeCount =  lastMatchingIndex - i + 1;
                                final View[] included = new View[includeCount];
                                for (int j = 0; j < includeCount; j++) {
                                    included[j] = viewGroup.getChildAt(i + j);
                                }
                                bindings[index] = DataBindingUtil.bind(bindingComponent, included,
                                        layoutId);
                                i += includeCount - 1;
                            }
                        }
                    }
                }
                if (!isInclude) {
                    mapBindings(bindingComponent, child, bindings, includes, viewsWithIds, false);
                }
            }
        }
    }

    private static int findIncludeIndex(String tag, int minInclude,
            IncludedLayouts included, int includedIndex) {
        final int slashIndex = tag.indexOf('/');
        final CharSequence layoutName = tag.subSequence(slashIndex + 1, tag.length() - 2);

        final String[] layouts = included.layouts[includedIndex];
        final int length = layouts.length;
        for (int i = minInclude; i < length; i++) {
            final String layout = layouts[i];
            if (TextUtils.equals(layoutName, layout)) {
                return i;
            }
        }
        return -1;
    }

    private static int findLastMatching(ViewGroup viewGroup, int firstIncludedIndex) {
        final View firstView = viewGroup.getChildAt(firstIncludedIndex);
        final String firstViewTag = (String) firstView.getTag();
        final String tagBase = firstViewTag.substring(0, firstViewTag.length() - 1); // don't include the "0"
        final int tagSequenceIndex = tagBase.length();

        final int count = viewGroup.getChildCount();
        int max = firstIncludedIndex;
        for (int i = firstIncludedIndex + 1; i < count; i++) {
            final View view = viewGroup.getChildAt(i);
            final Object objTag = view.getTag();
            final String tag = objTag instanceof String ? (String) view.getTag() : null;
            if (tag != null && tag.startsWith(tagBase)) {
                if (tag.length() == firstViewTag.length() && tag.charAt(tag.length() - 1) == '0') {
                    return max; // Found another instance of the include
                }
                if (isNumeric(tag, tagSequenceIndex)) {
                    max = i;
                }
            }
        }
        return max;
    }

    private static boolean isNumeric(String tag, int startIndex) {
        int length = tag.length();
        if (length == startIndex) {
            return false; // no numerals
        }
        for (int i = startIndex; i < length; i++) {
            if (!Character.isDigit(tag.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    /**
     * Parse the tag without creating a new String object. This is fast and assumes the
     * tag is in the correct format.
     * @param str The tag string.
     * @return The binding tag number parsed from the tag string.
     */
    private static int parseTagInt(String str, int startIndex) {
        final int end = str.length();
        int val = 0;
        for (int i = startIndex; i < end; i++) {
            val *= 10;
            char c = str.charAt(i);
            val += (c - '0');
        }
        return val;
    }

    /**
     * Polls sReferenceQueue to remove listeners on ViewDataBindings that have been collected.
     */
    private static void processReferenceQueue() {
        Reference<? extends ViewDataBinding> ref;
        while ((ref = sReferenceQueue.poll()) != null) {
            if (ref instanceof WeakListener) {
                WeakListener listener = (WeakListener) ref;
                listener.unregister();
            }
        }
    }

    private interface ObservableReference<T> {
        WeakListener<T> getListener();
        void addListener(T target);
        void removeListener(T target);
        void setLifecycleOwner(LifecycleOwner lifecycleOwner);
    }

    private static class WeakListener<T> extends WeakReference<ViewDataBinding> {
        private final ObservableReference<T> mObservable;
        protected final int mLocalFieldId;
        private T mTarget;

        public WeakListener(ViewDataBinding binder, int localFieldId,
                ObservableReference<T> observable) {
            super(binder, sReferenceQueue);
            mLocalFieldId = localFieldId;
            mObservable = observable;
        }

        public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
            mObservable.setLifecycleOwner(lifecycleOwner);
        }

        public void setTarget(T object) {
            unregister();
            mTarget = object;
            if (mTarget != null) {
                mObservable.addListener(mTarget);
            }
        }

        public boolean unregister() {
            boolean unregistered = false;
            if (mTarget != null) {
                mObservable.removeListener(mTarget);
                unregistered = true;
            }
            mTarget = null;
            return unregistered;
        }

        public T getTarget() {
            return mTarget;
        }

        protected ViewDataBinding getBinder() {
            ViewDataBinding binder = get();
            if (binder == null) {
                unregister(); // The binder is dead
            }
            return binder;
        }
    }

    private static class WeakPropertyListener extends Observable.OnPropertyChangedCallback
            implements ObservableReference<Observable> {
        final WeakListener<Observable> mListener;

        public WeakPropertyListener(ViewDataBinding binder, int localFieldId) {
            mListener = new WeakListener<Observable>(binder, localFieldId, this);
        }

        @Override
        public WeakListener<Observable> getListener() {
            return mListener;
        }

        @Override
        public void addListener(Observable target) {
            target.addOnPropertyChangedCallback(this);
        }

        @Override
        public void removeListener(Observable target) {
            target.removeOnPropertyChangedCallback(this);
        }

        @Override
        public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
        }

        @Override
        public void onPropertyChanged(Observable sender, int propertyId) {
            ViewDataBinding binder = mListener.getBinder();
            if (binder == null) {
                return;
            }
            Observable obj = mListener.getTarget();
            if (obj != sender) {
                return; // notification from the wrong object?
            }
            binder.handleFieldChange(mListener.mLocalFieldId, sender, propertyId);
        }
    }

    private static class WeakListListener extends ObservableList.OnListChangedCallback
            implements ObservableReference<ObservableList> {
        final WeakListener<ObservableList> mListener;

        public WeakListListener(ViewDataBinding binder, int localFieldId) {
            mListener = new WeakListener<ObservableList>(binder, localFieldId, this);
        }

        @Override
        public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
        }

        @Override
        public WeakListener<ObservableList> getListener() {
            return mListener;
        }

        @Override
        public void addListener(ObservableList target) {
            target.addOnListChangedCallback(this);
        }

        @Override
        public void removeListener(ObservableList target) {
            target.removeOnListChangedCallback(this);
        }

        @Override
        public void onChanged(ObservableList sender) {
            ViewDataBinding binder = mListener.getBinder();
            if (binder == null) {
                return;
            }
            ObservableList target = mListener.getTarget();
            if (target != sender) {
                return; // We expect notifications only from sender
            }
            binder.handleFieldChange(mListener.mLocalFieldId, target, 0);
        }

        @Override
        public void onItemRangeChanged(ObservableList sender, int positionStart, int itemCount) {
            onChanged(sender);
        }

        @Override
        public void onItemRangeInserted(ObservableList sender, int positionStart, int itemCount) {
            onChanged(sender);
        }

        @Override
        public void onItemRangeMoved(ObservableList sender, int fromPosition, int toPosition,
                int itemCount) {
            onChanged(sender);
        }

        @Override
        public void onItemRangeRemoved(ObservableList sender, int positionStart, int itemCount) {
            onChanged(sender);
        }
    }

    private static class WeakMapListener extends ObservableMap.OnMapChangedCallback
            implements ObservableReference<ObservableMap> {
        final WeakListener<ObservableMap> mListener;

        public WeakMapListener(ViewDataBinding binder, int localFieldId) {
            mListener = new WeakListener<ObservableMap>(binder, localFieldId, this);
        }

        @Override
        public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
        }

        @Override
        public WeakListener<ObservableMap> getListener() {
            return mListener;
        }

        @Override
        public void addListener(ObservableMap target) {
            target.addOnMapChangedCallback(this);
        }

        @Override
        public void removeListener(ObservableMap target) {
            target.removeOnMapChangedCallback(this);
        }

        @Override
        public void onMapChanged(ObservableMap sender, Object key) {
            ViewDataBinding binder = mListener.getBinder();
            if (binder == null || sender != mListener.getTarget()) {
                return;
            }
            binder.handleFieldChange(mListener.mLocalFieldId, sender, 0);
        }
    }

    private static class LiveDataListener implements Observer,
            ObservableReference<LiveData<?>> {
        final WeakListener<LiveData<?>> mListener;
        LifecycleOwner mLifecycleOwner;

        public LiveDataListener(ViewDataBinding binder, int localFieldId) {
            mListener = new WeakListener(binder, localFieldId, this);
        }

        @Override
        public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
            LifecycleOwner owner = (LifecycleOwner) lifecycleOwner;
            LiveData<?> liveData = mListener.getTarget();
            if (liveData != null) {
                if (mLifecycleOwner != null) {
                    liveData.removeObserver(this);
                }
                if (lifecycleOwner != null) {
                    liveData.observe(owner, this);
                }
            }
            mLifecycleOwner = owner;
        }

        @Override
        public WeakListener<LiveData<?>> getListener() {
            return mListener;
        }

        @Override
        public void addListener(LiveData<?> target) {
            if (mLifecycleOwner != null) {
                target.observe(mLifecycleOwner, this);
            }
        }

        @Override
        public void removeListener(LiveData<?> target) {
            target.removeObserver(this);
        }

        @Override
        public void onChanged(@Nullable Object o) {
            ViewDataBinding binder = mListener.getBinder();
            binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0);
        }
    }

    private interface CreateWeakListener {
        WeakListener create(ViewDataBinding viewDataBinding, int localFieldId);
    }

    /**
     * This class is used by generated subclasses of {@link ViewDataBinding} to track the
     * included layouts contained in the bound layout. This class is an implementation
     * detail of how binding expressions are mapped to Views after inflation.
     * @hide
     */
    protected static class IncludedLayouts {
        public final String[][] layouts;
        public final int[][] indexes;
        public final int[][] layoutIds;

        public IncludedLayouts(int bindingCount) {
            layouts = new String[bindingCount][];
            indexes = new int[bindingCount][];
            layoutIds = new int[bindingCount][];
        }

        public void setIncludes(int index, String[] layouts, int[] indexes, int[] layoutIds) {
            this.layouts[index] = layouts;
            this.indexes[index] = indexes;
            this.layoutIds[index] = layoutIds;
        }
    }

    /**
     * This class is used by generated subclasses of {@link ViewDataBinding} to listen for
     * changes on variables of Bindings. This is important for two-way data binding on variables
     * in included Bindings.
     * @hide
     */
    protected static abstract class PropertyChangedInverseListener
            extends Observable.OnPropertyChangedCallback implements InverseBindingListener {
        final int mPropertyId;

        public PropertyChangedInverseListener(int propertyId) {
            mPropertyId = propertyId;
        }

        @Override
        public void onPropertyChanged(Observable sender, int propertyId) {
            if (propertyId == mPropertyId || propertyId == 0) {
                onChange();
            }
        }
    }

    /**
     * This class is used internally to handle Lifecycle events in ViewDataBinding. A
     * LifecycleObserver class MUST be public.
     *
     * @hide
     */
    public class OnStartListener implements LifecycleObserver {
        private OnStartListener() {
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        public void onStart() {
            executePendingBindings();
        }
    }
}