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.view.View.MeasureSpec;
     28 import android.widget.FrameLayout;
     29 import android.widget.ImageView;
     30 import android.widget.LinearLayout;
     31 
     32 import androidx.test.InstrumentationRegistry;
     33 import androidx.test.filters.LargeTest;
     34 import androidx.test.rule.ActivityTestRule;
     35 
     36 import org.junit.Rule;
     37 import org.junit.Test;
     38 import org.junit.runner.RunWith;
     39 import org.junit.runners.Parameterized;
     40 
     41 import java.util.ArrayList;
     42 import java.util.List;
     43 
     44 @RunWith(Parameterized.class)
     45 @LargeTest
     46 public class ViewShowHidePerfTest {
     47 
     48     @Rule
     49     public ActivityTestRule mActivityRule = new ActivityTestRule(StubActivity.class);
     50 
     51     @Rule
     52     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
     53 
     54     public Context getContext() {
     55         return InstrumentationRegistry.getInstrumentation().getTargetContext();
     56     }
     57 
     58     static abstract class SubTreeFactory {
     59         String mName;
     60         SubTreeFactory(String name) { mName = name; }
     61 
     62         abstract View create(Context context, int depth);
     63 
     64         @Override
     65         public String toString() {
     66             return mName;
     67         }
     68     }
     69 
     70     private static SubTreeFactory[] sSubTreeFactories = new SubTreeFactory[] {
     71             new SubTreeFactory("NestedLinearLayoutTree") {
     72                 private int mColorToggle = 0;
     73 
     74                 private void createNestedLinearLayoutTree(Context context, LinearLayout parent,
     75                         int remainingDepth) {
     76                     if (remainingDepth <= 0) {
     77                         mColorToggle = (mColorToggle + 1) % 4;
     78                         parent.setBackgroundColor((mColorToggle < 2) ? Color.RED : Color.BLUE);
     79                         return;
     80                     }
     81 
     82                     boolean vertical = remainingDepth % 2 == 0;
     83                     parent.setOrientation(vertical ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
     84 
     85                     for (int i = 0; i < 2; i++) {
     86                         LinearLayout child = new LinearLayout(context);
     87                         // vertical: match parent in x axis, horizontal: y axis.
     88                         parent.addView(child, new LinearLayout.LayoutParams(
     89                                 (vertical ? ViewGroup.LayoutParams.MATCH_PARENT : 0),
     90                                 (vertical ? 0 : ViewGroup.LayoutParams.MATCH_PARENT),
     91                                 1.0f));
     92 
     93                         createNestedLinearLayoutTree(context, child, remainingDepth - 1);
     94                     }
     95                 }
     96 
     97                 @Override
     98                 public View create(Context context, int depth) {
     99                     LinearLayout root = new LinearLayout(context);
    100                     createNestedLinearLayoutTree(context, root, depth - 1);
    101                     return root;
    102                 }
    103             },
    104             new SubTreeFactory("ImageViewList") {
    105                 @Override
    106                 public View create(Context context, int depth) {
    107                     LinearLayout root = new LinearLayout(context);
    108                     root.setOrientation(LinearLayout.HORIZONTAL);
    109                     int childCount = (int) Math.pow(2, depth);
    110                     for (int i = 0; i < childCount; i++) {
    111                         ImageView imageView = new ImageView(context);
    112                         root.addView(imageView, new LinearLayout.LayoutParams(
    113                                 0, ViewGroup.LayoutParams.MATCH_PARENT, 1.0f));
    114                         imageView.setImageDrawable(new ColorDrawable(Color.RED));
    115                     }
    116                     return root;
    117                 }
    118             },
    119     };
    120 
    121 
    122     @Parameterized.Parameters(name = "Factory:{0},depth:{1}")
    123     public static Iterable<Object[]> params() {
    124         List<Object[]> params = new ArrayList<>();
    125         for (int depth : new int[] { 6 }) {
    126             for (SubTreeFactory subTreeFactory : sSubTreeFactories) {
    127                 params.add(new Object[]{ subTreeFactory, depth });
    128             }
    129         }
    130         return params;
    131     }
    132 
    133     private final View mChild;
    134 
    135     public ViewShowHidePerfTest(SubTreeFactory subTreeFactory, int depth) {
    136         mChild = subTreeFactory.create(getContext(), depth);
    137     }
    138 
    139     interface TestCallback {
    140         void run(BenchmarkState state, int width, int height, ViewGroup parent, View child);
    141     }
    142 
    143     private void testParentWithChild(TestCallback callback) throws Throwable {
    144         mActivityRule.runOnUiThread(() -> {
    145             final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
    146 
    147             FrameLayout parent = new FrameLayout(getContext());
    148             mActivityRule.getActivity().setContentView(parent);
    149 
    150             final int width = 1000;
    151             final int height = 1000;
    152             layout(width, height, parent);
    153 
    154             callback.run(state, width, height, parent, mChild);
    155         });
    156     }
    157 
    158     private void updateAndValidateDisplayList(View view) {
    159         boolean hasDisplayList = view.updateDisplayListIfDirty().hasDisplayList();
    160         assertTrue(hasDisplayList);
    161     }
    162 
    163     private void layout(int width, int height, View view) {
    164         view.measure(
    165                 MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
    166                 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
    167         view.layout(0, 0, height, width);
    168     }
    169 
    170     @Test
    171     public void testRemove() throws Throwable {
    172         testParentWithChild((state, width, height, parent, child) -> {
    173             while (state.keepRunning()) {
    174                 state.pauseTiming();
    175                 updateAndValidateDisplayList(parent); // Note, done to be safe, likely not needed
    176                 parent.addView(child);
    177                 layout(width, height, child);
    178                 updateAndValidateDisplayList(parent);
    179                 state.resumeTiming();
    180 
    181                 parent.removeAllViews();
    182             }
    183         });
    184     }
    185 
    186     @Test
    187     public void testAdd() throws Throwable {
    188         testParentWithChild((state, width, height, parent, child) -> {
    189             while (state.keepRunning()) {
    190                 state.pauseTiming();
    191                 layout(width, height, child); // Note, done to be safe, likely not needed
    192                 updateAndValidateDisplayList(parent); // Note, done to be safe, likely not needed
    193                 parent.removeAllViews();
    194                 updateAndValidateDisplayList(parent);
    195                 state.resumeTiming();
    196 
    197                 parent.addView(child);
    198             }
    199         });
    200     }
    201 
    202     @Test
    203     public void testRecordAfterAdd() throws Throwable {
    204         testParentWithChild((state, width, height, parent, child) -> {
    205             while (state.keepRunning()) {
    206                 state.pauseTiming();
    207                 parent.removeAllViews();
    208                 updateAndValidateDisplayList(parent); // Note, done to be safe, likely not needed
    209                 parent.addView(child);
    210                 layout(width, height, child);
    211                 state.resumeTiming();
    212 
    213                 updateAndValidateDisplayList(parent);
    214             }
    215         });
    216     }
    217 
    218     private void testVisibility(int fromVisibility, int toVisibility) throws Throwable {
    219         testParentWithChild((state, width, height, parent, child) -> {
    220             parent.addView(child);
    221 
    222             while (state.keepRunning()) {
    223                 state.pauseTiming();
    224                 layout(width, height, parent);
    225                 updateAndValidateDisplayList(parent);
    226                 child.setVisibility(fromVisibility);
    227                 layout(width, height, parent);
    228                 updateAndValidateDisplayList(parent);
    229                 state.resumeTiming();
    230 
    231                 child.setVisibility(toVisibility);
    232             }
    233         });
    234     }
    235 
    236     @Test
    237     public void testInvisibleToVisible() throws Throwable {
    238         testVisibility(View.INVISIBLE, View.VISIBLE);
    239     }
    240 
    241     @Test
    242     public void testVisibleToInvisible() throws Throwable {
    243         testVisibility(View.VISIBLE, View.INVISIBLE);
    244     }
    245     @Test
    246     public void testGoneToVisible() throws Throwable {
    247         testVisibility(View.GONE, View.VISIBLE);
    248     }
    249 
    250     @Test
    251     public void testVisibleToGone() throws Throwable {
    252         testVisibility(View.VISIBLE, View.GONE);
    253     }
    254 }
    255