boolean isForComponentId(int componentId) { return mComponent.getId() == componentId; }
@Override public int hashCode() { int result = context.hashCode(); result = 31 * result + root.getId(); result = 31 * result + widthSpec; result = 31 * result + heightSpec; return result; } }
@Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } LayoutStateFuture that = (LayoutStateFuture) o; if (widthSpec != that.widthSpec) { return false; } if (heightSpec != that.heightSpec) { return false; } if (!context.equals(that.context)) { return false; } if (root.getId() != that.root.getId()) { // We only care that the root id is the same since the root is shallow copied before // it's passed to us and will never be the same object. return false; } return true; }
boolean isCompatibleComponentAndSpec( int componentId, int widthSpec, int heightSpec) { return mComponent.getId() == componentId && isCompatibleSpec(widthSpec, heightSpec); }
/** * Compares this component to a different one to check if they are the same * * <p>This is used to be able to skip rendering a component again. We avoid using the {@link * Object#equals(Object)} so we can optimize the code better over time since we don't have to * adhere to the contract required for a equals method. * * @param other the component to compare to * @return true if the components are of the same type and have the same props */ @Override public boolean isEquivalentTo(Component other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } if (getId() == other.getId()) { return true; } return ComponentUtils.hasEquivalentFields(this, other); }
@GuardedBy("this") private boolean isCompatibleComponentAndSpec(LayoutState layoutState) { assertHoldsLock(this); return mRoot != null && isCompatibleComponentAndSpec( layoutState, mRoot.getId(), mWidthSpec, mHeightSpec); }
componentRootId = mRoot.getId();
mComponent.getId(), widthSpec, heightSpec)).isTrue();
@Test public void testSetRootAsyncFollowedByMeasureDoesntComputeSyncLayout() { ComponentTree componentTree = ComponentTree.create(mContext, mComponent).build(); componentTree.setLithoView(new LithoView(mContext)); componentTree.measure(mWidthSpec, mHeightSpec, new int[2], false); componentTree.attach(); Component newComponent = TestDrawableComponent.create(mContext).color(1234).build(); componentTree.setRootAsync(newComponent); assertThat(componentTree.getRoot()).isEqualTo(newComponent); componentTree.measure(mWidthSpec, mHeightSpec, new int[2], false); assertThat(componentTree.getMainThreadLayoutState().isForComponentId(mComponent.getId())) .isTrue(); mLayoutThreadShadowLooper.runToEndOfTasks(); assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId())) .isTrue(); }
toRelease = setBestMainThreadLayoutAndReturnOldLayout(); layoutStateUpdated = (mMainThreadLayoutState != oldMainThreadLayoutState); componentRootId = mRoot.getId();
@Test public void testSetRootAsyncFollowedByNonCompatibleMeasureComputesSyncLayout() { ComponentTree componentTree = ComponentTree.create(mContext, mComponent).build(); componentTree.setLithoView(new LithoView(mContext)); componentTree.measure(mWidthSpec, mHeightSpec, new int[2], false); componentTree.attach(); Component newComponent = TestDrawableComponent.create(mContext).color(1234).build(); componentTree.setRootAsync(newComponent); componentTree.measure(mWidthSpec2, mHeightSpec2, new int[2], false); assertThat(componentTree.getRoot()).isEqualTo(newComponent); assertThat(componentTree.hasCompatibleLayout(mWidthSpec2, mHeightSpec2)).isTrue(); assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId())) .isTrue(); // Clear tasks mLayoutThreadShadowLooper.runToEndOfTasks(); }
mMainThreadLayoutState == null || !isCompatibleSpec(mMainThreadLayoutState, mWidthSpec, mHeightSpec) || (!mMainThreadLayoutState.isForComponentId(mRoot.getId()) && !isPendingLayoutCompatible());
@Test public void testMeasureWithIncompatibleSetRootAsyncThatFinishes() { ComponentTree componentTree = ComponentTree.create(mContext, mComponent).build(); componentTree.setLithoView(new LithoView(mContext)); int widthSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY); int heightSpec1 = SizeSpec.makeSizeSpec(1000, SizeSpec.AT_MOST); int widthSpec2 = SizeSpec.makeSizeSpec(1000, SizeSpec.EXACTLY); int heightSpec2 = SizeSpec.makeSizeSpec(0, SizeSpec.UNSPECIFIED); componentTree.measure(widthSpec2, heightSpec2, new int[2], false); componentTree.attach(); componentTree.measure(widthSpec1, heightSpec1, new int[2], false); Component newComponent = TestDrawableComponent.create(mContext).color(1234).build(); componentTree.setRootAsync(newComponent); runOnBackgroundThreadSync( new Runnable() { @Override public void run() { // "Commit" layout (it will fail since it doesn't have compatible size specs) mLayoutThreadShadowLooper.runToEndOfTasks(); } }); componentTree.measure(widthSpec2, heightSpec2, new int[2], false); assertThat(componentTree.getRoot()).isEqualTo(newComponent); assertThat(componentTree.hasCompatibleLayout(widthSpec2, heightSpec2)).isTrue(); assertThat(componentTree.getMainThreadLayoutState().isForComponentId(newComponent.getId())) .isTrue() .withFailMessage( "The main thread should calculate a new layout synchronously because the async layout will not be used once it completes"); }
.arg("widthSpec", "EXACTLY " + node.getWidth()) .arg("heightSpec", "EXACTLY " + node.getHeight()) .arg("rootComponentId", node.getRootComponent().getId()) .flush();
final Component layoutComponent = createComponentLayout(context); if (layoutComponent == null || layoutComponent.getId() <= 0) { node = null; } else {
.arg("widthSpec", SizeSpec.toString(widthSpec)) .arg("heightSpec", SizeSpec.toString(heightSpec)) .arg("componentId", component.getId()) .flush();