Extract DrawCommands tracking in StateBuilder into a helper class.
Summary: @public This is a pure refactoring diff makes `StateBuilder` code a little bit easier to read. This gets increasingly important as new features with similar logic are added to `StateBuilder`. Reviewed By: sriramramani Differential Revision: D2564342
This commit is contained in:
parent
fbe1b61fe1
commit
5b182fa0ca
|
@ -0,0 +1,175 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.facebook.react.flat;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class that supports 3 main operations: start(), add() an element and finish().
|
||||||
|
*
|
||||||
|
* When started, it takes a baseline array to compare to. When adding a new element, it checks
|
||||||
|
* whether a corresponding element in baseline array is the same. On finish(), it will return null
|
||||||
|
* if baseline array contains exactly the same elements that were added with a sequence of add()
|
||||||
|
* calls, or a new array the recorded elements:
|
||||||
|
*
|
||||||
|
* Example 1:
|
||||||
|
* -----
|
||||||
|
* start([A])
|
||||||
|
* add(A)
|
||||||
|
* finish() -> null (because [A] == [A])
|
||||||
|
*
|
||||||
|
* Example 2:
|
||||||
|
* ----
|
||||||
|
* start([A])
|
||||||
|
* add(B)
|
||||||
|
* finish() -> [B] (because [A] != [B])
|
||||||
|
*
|
||||||
|
* It is important that start/finish can be nested:
|
||||||
|
* ----
|
||||||
|
* start([A])
|
||||||
|
* add(A)
|
||||||
|
* start([B])
|
||||||
|
* add(B)
|
||||||
|
* finish() -> null
|
||||||
|
* add(C)
|
||||||
|
* finish() -> [A, C]
|
||||||
|
*
|
||||||
|
* StateBuilder is using this class to check if e.g. a DrawCommand list for a given View needs to be
|
||||||
|
* updated.
|
||||||
|
*/
|
||||||
|
/* package */ final class ElementsList<E> {
|
||||||
|
|
||||||
|
private static final class Scope {
|
||||||
|
Object[] elements;
|
||||||
|
int index;
|
||||||
|
int size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ArrayList<Scope> mScopesStack = new ArrayList<>();
|
||||||
|
private final ArrayDeque<E> mElements = new ArrayDeque<>();
|
||||||
|
private final E[] mEmptyArray;
|
||||||
|
private Scope mCurrentScope = null;
|
||||||
|
private int mScopeIndex = 0;
|
||||||
|
|
||||||
|
public ElementsList(E[] emptyArray) {
|
||||||
|
mEmptyArray = emptyArray;
|
||||||
|
mScopesStack.add(mCurrentScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a new scope.
|
||||||
|
*/
|
||||||
|
public void start(Object[] elements) {
|
||||||
|
pushScope();
|
||||||
|
|
||||||
|
Scope scope = getCurrentScope();
|
||||||
|
scope.elements = elements;
|
||||||
|
scope.index = 0;
|
||||||
|
scope.size = mElements.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finished current scope, and returns null if there were no changes recorded, or a new array
|
||||||
|
* containing all the recorded elements otherwise.
|
||||||
|
*/
|
||||||
|
public E[] finish() {
|
||||||
|
Scope scope = getCurrentScope();
|
||||||
|
popScope();
|
||||||
|
|
||||||
|
E[] result = null;
|
||||||
|
int size = mElements.size() - scope.size;
|
||||||
|
if (scope.index != scope.elements.length) {
|
||||||
|
result = extractElements(size);
|
||||||
|
} else {
|
||||||
|
// downsize
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
mElements.pollLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// to prevent leaks
|
||||||
|
scope.elements = null;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new element to the list. This method can be optimized to avoid inserts on same elements.
|
||||||
|
*/
|
||||||
|
public void add(E element) {
|
||||||
|
Scope scope = getCurrentScope();
|
||||||
|
|
||||||
|
if (scope.index < scope.elements.length &&
|
||||||
|
scope.elements[scope.index] == element) {
|
||||||
|
++scope.index;
|
||||||
|
} else {
|
||||||
|
scope.index = Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mElements.add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets all references to the elements to null to avoid memory leaks.
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
if (getCurrentScope() != null) {
|
||||||
|
throw new RuntimeException("Must call finish() for every start() call being made.");
|
||||||
|
}
|
||||||
|
mElements.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts last size elements into an array.
|
||||||
|
*/
|
||||||
|
private E[] extractElements(int size) {
|
||||||
|
if (size == 0) {
|
||||||
|
// avoid allocating empty array
|
||||||
|
return mEmptyArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
E[] elements = (E[]) Array.newInstance(mEmptyArray.getClass().getComponentType(), size);
|
||||||
|
for (int i = size - 1; i >= 0; --i) {
|
||||||
|
elements[i] = mElements.pollLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves current scope in a stack.
|
||||||
|
*/
|
||||||
|
private void pushScope() {
|
||||||
|
++mScopeIndex;
|
||||||
|
if (mScopeIndex == mScopesStack.size()) {
|
||||||
|
mCurrentScope = new Scope();
|
||||||
|
mScopesStack.add(mCurrentScope);
|
||||||
|
} else {
|
||||||
|
mCurrentScope = mScopesStack.get(mScopeIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores last save current scope.
|
||||||
|
*/
|
||||||
|
private void popScope() {
|
||||||
|
--mScopeIndex;
|
||||||
|
mCurrentScope = mScopesStack.get(mScopeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current scope.
|
||||||
|
*/
|
||||||
|
private Scope getCurrentScope() {
|
||||||
|
return mCurrentScope;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,8 +9,6 @@
|
||||||
|
|
||||||
package com.facebook.react.flat;
|
package com.facebook.react.flat;
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shadow node hierarchy by itself cannot display UI, it is only a representation of what UI should
|
* Shadow node hierarchy by itself cannot display UI, it is only a representation of what UI should
|
||||||
* be from JavaScript perspective. StateBuilder is a helper class that can walk the shadow node tree
|
* be from JavaScript perspective. StateBuilder is a helper class that can walk the shadow node tree
|
||||||
|
@ -21,10 +19,8 @@ import java.util.ArrayDeque;
|
||||||
|
|
||||||
private final FlatUIViewOperationQueue mOperationsQueue;
|
private final FlatUIViewOperationQueue mOperationsQueue;
|
||||||
|
|
||||||
// DrawCommands
|
private final ElementsList<DrawCommand> mDrawCommands =
|
||||||
private final ArrayDeque<DrawCommand> mDrawCommands = new ArrayDeque<>();
|
new ElementsList(DrawCommand.EMPTY_ARRAY);
|
||||||
private DrawCommand[] mPreviousDrawCommands = DrawCommand.EMPTY_ARRAY;
|
|
||||||
private int mPreviousDrawCommandsIndex;
|
|
||||||
|
|
||||||
/* package */ StateBuilder(FlatUIViewOperationQueue operationsQueue) {
|
/* package */ StateBuilder(FlatUIViewOperationQueue operationsQueue) {
|
||||||
mOperationsQueue = operationsQueue;
|
mOperationsQueue = operationsQueue;
|
||||||
|
@ -42,14 +38,7 @@ import java.util.ArrayDeque;
|
||||||
* Adds a DrawCommand for current mountable node.
|
* Adds a DrawCommand for current mountable node.
|
||||||
*/
|
*/
|
||||||
/* package */ void addDrawCommand(AbstractDrawCommand drawCommand) {
|
/* package */ void addDrawCommand(AbstractDrawCommand drawCommand) {
|
||||||
if (mPreviousDrawCommandsIndex < mPreviousDrawCommands.length &&
|
mDrawCommands.add(drawCommand);
|
||||||
mPreviousDrawCommands[mPreviousDrawCommandsIndex] == drawCommand) {
|
|
||||||
++mPreviousDrawCommandsIndex;
|
|
||||||
} else {
|
|
||||||
mPreviousDrawCommandsIndex = mPreviousDrawCommands.length + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
mDrawCommands.addLast(drawCommand);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,47 +75,17 @@ import java.util.ArrayDeque;
|
||||||
int tag,
|
int tag,
|
||||||
float width,
|
float width,
|
||||||
float height) {
|
float height) {
|
||||||
// save
|
mDrawCommands.start(node.getDrawCommands());
|
||||||
int d = mDrawCommands.size();
|
|
||||||
DrawCommand[] previousDrawCommands = mPreviousDrawCommands;
|
|
||||||
int previousDrawCommandsIndex = mPreviousDrawCommandsIndex;
|
|
||||||
|
|
||||||
// reset
|
|
||||||
mPreviousDrawCommands = node.getDrawCommands();
|
|
||||||
mPreviousDrawCommandsIndex = 0;
|
|
||||||
|
|
||||||
collectStateRecursively(node, 0, 0, width, height);
|
collectStateRecursively(node, 0, 0, width, height);
|
||||||
|
|
||||||
if (mPreviousDrawCommandsIndex != mPreviousDrawCommands.length) {
|
final DrawCommand[] drawCommands = mDrawCommands.finish();
|
||||||
// DrawCommands changes, need to re-mount them and re-draw the View.
|
if (drawCommands != null) {
|
||||||
DrawCommand[] drawCommands = extractDrawCommands(d);
|
// DrawCommands changed, need to re-mount them and re-draw the View.
|
||||||
node.setDrawCommands(drawCommands);
|
node.setDrawCommands(drawCommands);
|
||||||
|
|
||||||
mOperationsQueue.enqueueUpdateMountState(tag, drawCommands);
|
mOperationsQueue.enqueueUpdateMountState(tag, drawCommands);
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore
|
|
||||||
mPreviousDrawCommandsIndex = previousDrawCommandsIndex;
|
|
||||||
mPreviousDrawCommands = previousDrawCommands;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all DrawCommands collectes so far starting from a given index.
|
|
||||||
*/
|
|
||||||
private DrawCommand[] extractDrawCommands(int lowerBound) {
|
|
||||||
int upperBound = mDrawCommands.size();
|
|
||||||
int size = upperBound - lowerBound;
|
|
||||||
if (size == 0) {
|
|
||||||
// avoid allocating empty array
|
|
||||||
return DrawCommand.EMPTY_ARRAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawCommand[] drawCommands = new DrawCommand[size];
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
drawCommands[i] = mDrawCommands.pollFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
return drawCommands;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue