Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.view;
     18 
     19 import static org.junit.Assert.assertTrue;
     20 
     21 import android.content.Context;
     22 import android.graphics.Color;
     23 import android.graphics.drawable.ColorDrawable;
     24 import android.perftests.utils.BenchmarkState;
     25 import android.perftests.utils.PerfStatusReporter;
     26 import android.perftests.utils.StubActivity;
     27 import android.support.test.InstrumentationRegistry;
     28 import android.support.test.filters.LargeTest;
     29 import android.support.test.rule.ActivityTestRule;
     30 import android.view.View.MeasureSpec;
     31 import android.widget.FrameLayout;
     32 import android.widget.ImageView;
     33 import android.widget.LinearLayout;
     34 
     35 import org.junit.Rule;
     36 import org.junit.Test;
     37 import org.junit.runner.RunWith;
     38 import org.junit.runners.Parameterized;
     39 
     40 import java.util.ArrayList;
     41 import java.util.List;
     42 
     43 @RunWith(Parameterized.class)
     44 @LargeTest
     45 public class ViewShowHidePerfTest {
     46 
     47     @Rule
     48     public ActivityTestRule mActivityRule = new ActivityTestRule(StubActivity.class);
     49 
     50     @Rule
     51     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
     52 
     53     public Context getContext() {
     54         return InstrumentationRegistry.getInstrumentation().getTargetContext();
     55     }
     56 
     57     static abstract class SubTreeFactory {
     58         String mName;
     59         SubTreeFactory(String name) { mName = name; }
     60 
     61         abstract View create(Context context, int depth);
     62 
     63         @Override
     64         public String toString() {
     65             return mName;
     66         }
     67     }
     68 
     69     private static SubTreeFactory[] sSubTreeFactories = new SubTreeFactory[] {
     70             new SubTreeFactory("NestedLinearLayoutTree") {
     71                 private int mColorToggle = 0;
     72 
     73                 private void createNestedLinearLayoutTree(Context context, LinearLayout parent,
     74                         int remainingDepth) {
     75                     if (remainingDepth <= 0) {
     76                         mColorToggle = (mColorToggle + 1) % 4;
     77                         parent.setBackgroundColor((mColorToggle < 2) ? Color.RED : Color.BLUE);
     78                         return;
     79                     }
     80 
     81                     boolean vertical = remainingDepth % 2 == 0;
     82                     parent.setOrientation(vertical ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
     83 
     84                     for (int i = 0; i < 2; i++) {
     85                         LinearLayout child = new LinearLayout(context);
     86                         // vertical: match parent in x axis, horizontal: y axis.
     87                         parent.addView(child, new LinearLayout.LayoutParams(
     88                                 (vertical ? ViewGroup.LayoutParams.MATCH_PARENT : 0),
     89                                 (vertical ? 0 : ViewGroup.LayoutParams.MATCH_PARENT),
     90                                 1.0f));
     91 
     92                         createNestedLinearLayoutTree(context, child, remainingDepth - 1);
     93                     }
     94                 }
     95 
     96                 @Override
     97                 public View create(Context context, int depth) {
     98                     LinearLayout root = new LinearLayout(context);
     99                     createNestedLinearLayoutTree(context, root, depth - 1);
    100                     return root;
    101                 }
    102             },
    103             new SubTreeFactory("ImageViewList") {
    104                 @Override
    105                 public View create(Context context, int depth) {
    106                     LinearLayout root = new LinearLayout(context);
    107                     root.setOrientation(LinearLayout.HORIZONTAL);
    108                     int childCount = (int) Math.pow(2, depth);
    109                     for (int i = 0; i < childCount; i++) {
    110                         ImageView imageView = new ImageView(context);
    111                         root.addView(imageView, new LinearLayout.LayoutParams(
    112                                 0, ViewGroup.LayoutParams.MATCH_PARENT, 1.0f));
    113                         imageView.setImageDrawable(new ColorDrawable(Color.RED));
    114                     }
    115                     return root;
    116                 }
    117             },
    118     };
    119 
    120 
    121     @Parameterized.Parameters(name = "Factory:{0},depth:{1}")
    122     public static Iterable<Object[]> params() {
    123         List<Object[]> params = new ArrayList<>();
    124         for (int depth : new int[] { 6 }) {
    125             for (SubTreeFactory subTreeFactory : sSubTreeFactories) {
    126                 params.add(new Object[]{ subTreeFactory, depth });
    127             }
    128         }
    129         return params;
    130     }
    131 
    132     private final View mChild;
    133 
    134     public ViewShowHidePerfTest(SubTreeFactory subTreeFactory, int depth) {
    135         mChild = subTreeFactory.create(getContext(), depth);
    136     }
    137 
    138     interface TestCallback {
    139         void run(BenchmarkState state, int width, int height, ViewGroup parent, View child);
    140     }
    141 
    142     private void testParentWithChild(TestCallback callback) throws Throwable {
    143         mActivityRule.runOnUiThread(() -> {
    144             final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
    145 
    146             FrameLayout parent = new FrameLayout(getContext());
    147             mActivityRule.getActivity().setContentView(parent);
    148 
    149             final int width = 1000;
    150             final int height = 1000;
    151             layout(width, height, parent);
    152 
    153             callback.run(state, width, height, parent, mChild);
    154         });
    155     }
    156 
    157     private void updateAndValidateDisplayList(View view) {
    158         boolean hasDisplayList = view.updateDisplayListIfDirty().isValid();
    159         assertTrue(hasDisplayList);
    160     }
    161 
    162     private void layout(int width, int height, View view) {
    163         view.measure(
    164                 MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
    165                 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
    166         view.layout(0, 0, height, width);
    167     }
    168 
    169     @Test
    170     public void testRemove() throws Throwable {
    171         testParentWithChild((state, width, height, parent, child) -> {
    172             while (state.keepRunning()) {
    173                 state.pauseTiming();
    174                 updateAndValidateDisplayList(parent); // Note, done to be safe, likely not needed
    175                 parent.addView(child);
    176                 layout(width, height, child);
    177                 updateAndValidateDisplayList(parent);
    178                 state.resumeTiming();
    179 
    180                 parent.removeAllViews();
    181             }
    182         });
    183     }
    184 
    185     @Test
    186     public void testAdd() throws Throwable {
    187         testParentWithChild((state, width, height, parent, child) -> {
    188             while (state.keepRunning()) {
    189                 state.pauseTiming();
    190                 layout(width, height, child); // Note, done to be safe, likely not needed
    191                 updateAndValidateDisplayList(parent); // Note, done to be safe, likely not needed
    192                 parent.removeAllViews();
    193                 updateAndValidateDisplayList(parent);
    194                 state.resumeTiming();
    195 
    196                 parent.addView(child);
    197             }
    198         });
    199     }
    200 
    201     @Test
    202     public void testRecordAfterAdd() throws Throwable {
    203         testParentWithChild((state, width, height, parent, child) -> {
    204             while (state.keepRunning()) {
    205                 state.pauseTiming();
    206                 parent.removeAllViews();
    207                 updateAndValidateDisplayList(parent); // Note, done to be safe, likely not needed
    208                 parent.addView(child);
    209                 layout(width, height, child);
    210                 state.resumeTiming();
    211 
    212                 updateAndValidateDisplayList(parent);
    213             }
    214         });
    215     }
    216 
    217     private void testVisibility(int fromVisibility, int toVisibility) throws Throwable {
    218         testParentWithChild((state, width, height, parent, child) -> {
    219             parent.addView(child);
    220 
    221             while (state.keepRunning()) {
    222                 state.pauseTiming();
    223                 layout(width, height, parent);
    224                 updateAndValidateDisplayList(parent);
    225                 child.setVisibility(fromVisibility);
    226                 layout(width, height, parent);
    227                 updateAndValidateDisplayList(parent);
    228                 state.resumeTiming();
    229 
    230                 child.setVisibility(toVisibility);
    231             }
    232         });
    233     }
    234 
    235     @Test
    236     public void testInvisibleToVisible() throws Throwable {
    237         testVisibility(View.INVISIBLE, View.VISIBLE);
    238     }
    239 
    240     @Test
    241     public void testVisibleToInvisible() throws Throwable {
    242         testVisibility(View.VISIBLE, View.INVISIBLE);
    243     }
    244     @Test
    245     public void testGoneToVisible() throws Throwable {
    246         testVisibility(View.GONE, View.VISIBLE);
    247     }
    248 
    249     @Test
    250     public void testVisibleToGone() throws Throwable {
    251         testVisibility(View.VISIBLE, View.GONE);
    252     }
    253 }
    254