Merge "Add benchmarks for different ways to show/hide view subtrees"
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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 android.view;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
@LargeTest
|
||||
public class ViewShowHidePerfTest {
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule mActivityRule = new ActivityTestRule(StubActivity.class);
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
public Context getContext() {
|
||||
return InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
}
|
||||
|
||||
static abstract class SubTreeFactory {
|
||||
String mName;
|
||||
SubTreeFactory(String name) { mName = name; }
|
||||
|
||||
abstract View create(Context context, int depth);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mName;
|
||||
}
|
||||
}
|
||||
|
||||
private static SubTreeFactory[] sSubTreeFactories = new SubTreeFactory[] {
|
||||
new SubTreeFactory("NestedLinearLayoutTree") {
|
||||
private int mColorToggle = 0;
|
||||
|
||||
private void createNestedLinearLayoutTree(Context context, LinearLayout parent,
|
||||
int remainingDepth) {
|
||||
if (remainingDepth <= 0) {
|
||||
mColorToggle = (mColorToggle + 1) % 4;
|
||||
parent.setBackgroundColor((mColorToggle < 2) ? Color.RED : Color.BLUE);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean vertical = remainingDepth % 2 == 0;
|
||||
parent.setOrientation(vertical ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
LinearLayout child = new LinearLayout(context);
|
||||
// vertical: match parent in x axis, horizontal: y axis.
|
||||
parent.addView(child, new LinearLayout.LayoutParams(
|
||||
(vertical ? ViewGroup.LayoutParams.MATCH_PARENT : 0),
|
||||
(vertical ? 0 : ViewGroup.LayoutParams.MATCH_PARENT),
|
||||
1.0f));
|
||||
|
||||
createNestedLinearLayoutTree(context, child, remainingDepth - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View create(Context context, int depth) {
|
||||
LinearLayout root = new LinearLayout(context);
|
||||
createNestedLinearLayoutTree(context, root, depth - 1);
|
||||
return root;
|
||||
}
|
||||
},
|
||||
new SubTreeFactory("ImageViewList") {
|
||||
@Override
|
||||
public View create(Context context, int depth) {
|
||||
LinearLayout root = new LinearLayout(context);
|
||||
root.setOrientation(LinearLayout.HORIZONTAL);
|
||||
int childCount = (int) Math.pow(2, depth);
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
ImageView imageView = new ImageView(context);
|
||||
root.addView(imageView, new LinearLayout.LayoutParams(
|
||||
0, ViewGroup.LayoutParams.MATCH_PARENT, 1.0f));
|
||||
imageView.setImageDrawable(new ColorDrawable(Color.RED));
|
||||
}
|
||||
return root;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@Parameterized.Parameters(name = "Factory:{0},depth:{1}")
|
||||
public static Iterable<Object[]> params() {
|
||||
List<Object[]> params = new ArrayList<>();
|
||||
for (int depth : new int[] {1, 6, 10}) {
|
||||
for (SubTreeFactory subTreeFactory : sSubTreeFactories) {
|
||||
params.add(new Object[]{ subTreeFactory, depth });
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
private final View mChild;
|
||||
|
||||
public ViewShowHidePerfTest(SubTreeFactory subTreeFactory, int depth) {
|
||||
mChild = subTreeFactory.create(getContext(), depth);
|
||||
}
|
||||
|
||||
interface TestCallback {
|
||||
void run(BenchmarkState state, int width, int height, ViewGroup parent, View child);
|
||||
}
|
||||
|
||||
private void testParentWithChild(TestCallback callback) throws Throwable {
|
||||
mActivityRule.runOnUiThread(() -> {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
FrameLayout parent = new FrameLayout(getContext());
|
||||
mActivityRule.getActivity().setContentView(parent);
|
||||
|
||||
final int width = 1000;
|
||||
final int height = 1000;
|
||||
layout(width, height, parent);
|
||||
|
||||
callback.run(state, width, height, parent, mChild);
|
||||
});
|
||||
}
|
||||
|
||||
private void updateAndValidateDisplayList(View view) {
|
||||
boolean hasDisplayList = view.updateDisplayListIfDirty().isValid();
|
||||
assertTrue(hasDisplayList);
|
||||
}
|
||||
|
||||
private void layout(int width, int height, View view) {
|
||||
view.measure(
|
||||
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
|
||||
view.layout(0, 0, height, width);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove() throws Throwable {
|
||||
testParentWithChild((state, width, height, parent, child) -> {
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
updateAndValidateDisplayList(parent); // Note, done to be safe, likely not needed
|
||||
parent.addView(child);
|
||||
layout(width, height, child);
|
||||
updateAndValidateDisplayList(parent);
|
||||
state.resumeTiming();
|
||||
|
||||
parent.removeAllViews();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd() throws Throwable {
|
||||
testParentWithChild((state, width, height, parent, child) -> {
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
layout(width, height, child); // Note, done to be safe, likely not needed
|
||||
updateAndValidateDisplayList(parent); // Note, done to be safe, likely not needed
|
||||
parent.removeAllViews();
|
||||
updateAndValidateDisplayList(parent);
|
||||
state.resumeTiming();
|
||||
|
||||
parent.addView(child);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void testVisibility(int fromVisibility, int toVisibility) throws Throwable {
|
||||
testParentWithChild((state, width, height, parent, child) -> {
|
||||
parent.addView(child);
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
layout(width, height, parent);
|
||||
updateAndValidateDisplayList(parent);
|
||||
child.setVisibility(fromVisibility);
|
||||
layout(width, height, parent);
|
||||
updateAndValidateDisplayList(parent);
|
||||
state.resumeTiming();
|
||||
|
||||
child.setVisibility(toVisibility);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvisibleToVisible() throws Throwable {
|
||||
testVisibility(View.INVISIBLE, View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVisibleToInvisible() throws Throwable {
|
||||
testVisibility(View.VISIBLE, View.INVISIBLE);
|
||||
}
|
||||
@Test
|
||||
public void testGoneToVisible() throws Throwable {
|
||||
testVisibility(View.GONE, View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVisibleToGone() throws Throwable {
|
||||
testVisibility(View.VISIBLE, View.GONE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user