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;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -21,10 +19,8 @@ import java.util.ArrayDeque;
|
|||
|
||||
private final FlatUIViewOperationQueue mOperationsQueue;
|
||||
|
||||
// DrawCommands
|
||||
private final ArrayDeque<DrawCommand> mDrawCommands = new ArrayDeque<>();
|
||||
private DrawCommand[] mPreviousDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||
private int mPreviousDrawCommandsIndex;
|
||||
private final ElementsList<DrawCommand> mDrawCommands =
|
||||
new ElementsList(DrawCommand.EMPTY_ARRAY);
|
||||
|
||||
/* package */ StateBuilder(FlatUIViewOperationQueue operationsQueue) {
|
||||
mOperationsQueue = operationsQueue;
|
||||
|
@ -42,14 +38,7 @@ import java.util.ArrayDeque;
|
|||
* Adds a DrawCommand for current mountable node.
|
||||
*/
|
||||
/* package */ void addDrawCommand(AbstractDrawCommand drawCommand) {
|
||||
if (mPreviousDrawCommandsIndex < mPreviousDrawCommands.length &&
|
||||
mPreviousDrawCommands[mPreviousDrawCommandsIndex] == drawCommand) {
|
||||
++mPreviousDrawCommandsIndex;
|
||||
} else {
|
||||
mPreviousDrawCommandsIndex = mPreviousDrawCommands.length + 1;
|
||||
}
|
||||
|
||||
mDrawCommands.addLast(drawCommand);
|
||||
mDrawCommands.add(drawCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,47 +75,17 @@ import java.util.ArrayDeque;
|
|||
int tag,
|
||||
float width,
|
||||
float height) {
|
||||
// save
|
||||
int d = mDrawCommands.size();
|
||||
DrawCommand[] previousDrawCommands = mPreviousDrawCommands;
|
||||
int previousDrawCommandsIndex = mPreviousDrawCommandsIndex;
|
||||
|
||||
// reset
|
||||
mPreviousDrawCommands = node.getDrawCommands();
|
||||
mPreviousDrawCommandsIndex = 0;
|
||||
mDrawCommands.start(node.getDrawCommands());
|
||||
|
||||
collectStateRecursively(node, 0, 0, width, height);
|
||||
|
||||
if (mPreviousDrawCommandsIndex != mPreviousDrawCommands.length) {
|
||||
// DrawCommands changes, need to re-mount them and re-draw the View.
|
||||
DrawCommand[] drawCommands = extractDrawCommands(d);
|
||||
final DrawCommand[] drawCommands = mDrawCommands.finish();
|
||||
if (drawCommands != null) {
|
||||
// DrawCommands changed, need to re-mount them and re-draw the View.
|
||||
node.setDrawCommands(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