美文网首页
基本的优化总结(四)

基本的优化总结(四)

作者: 范锦浩 | 来源:发表于2018-06-22 17:28 被阅读21次

导言

这节主要是讲一下布局方面关于UI的优化手段,属于编码中的一些细节处理

UI流畅性优化

先看Systrace中的某一帧


Systrace中的某一帧

从Alert提示中我们也可以知道反馈的是测量和布局时间过久,所以说后续要做的就是优化测量和布局的时间

ViewStub

先看一下ViewStub里面的一些关键代码

    public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context);

        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.ViewStub, defStyleAttr, defStyleRes);
        mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
        mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
        mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
        a.recycle();
        //可以看到,默认是不显示的
        setVisibility(GONE);
        setWillNotDraw(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //默认的大小也是0,并且不会有任何测量操作
        setMeasuredDimension(0, 0);
    }

    @Override
    public void draw(Canvas canvas) {
        //本身并不会绘制任何东西  
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
      //同样的也不会要求别人绘制任何东西
    }

从上面我们可以看出,实际上ViewStub就是一个占位容器,本身不会做任何操作,并且测量的时候默认为GONE的视图并不会参与测量
再看setVisibility方法

    @Override
    @android.view.RemotableViewMethod(asyncImpl = "setVisibilityAsync")
    public void setVisibility(int visibility) {
        if (mInflatedViewRef != null) {
            //如果内部布局已经inflate过,那么直接显示或者隐藏
            View view = mInflatedViewRef.get();
            if (view != null) {
                view.setVisibility(visibility);
            } else {
                throw new IllegalStateException("setVisibility called on un-referenced view");
            }
        } else {
            //否则显示自己,然后再进行inflate操作
            super.setVisibility(visibility);
            if (visibility == VISIBLE || visibility == INVISIBLE) {
                inflate();
            }
        }
    }

    public View inflate() {
        final ViewParent viewParent = getParent();

        if (viewParent != null && viewParent instanceof ViewGroup) {
            if (mLayoutResource != 0) {
                final ViewGroup parent = (ViewGroup) viewParent;
                final View view = inflateViewNoAdd(parent);
                replaceSelfWithView(view, parent);

                mInflatedViewRef = new WeakReference<>(view);
                if (mInflateListener != null) {
                    mInflateListener.onInflate(this, view);
                }

                return view;
            } else {
                throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
            }
        } else {
            throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
        }
    }

    private View inflateViewNoAdd(ViewGroup parent) {
        final LayoutInflater factory;
        if (mInflater != null) {
            factory = mInflater;
        } else {
            factory = LayoutInflater.from(mContext);
        }
        //这里才进行ViewStub内的布局的inflate操作
        final View view = factory.inflate(mLayoutResource, parent, false);

        if (mInflatedId != NO_ID) {
            view.setId(mInflatedId);
        }
        return view;
    }

    private void replaceSelfWithView(View view, ViewGroup parent) {
        final int index = parent.indexOfChild(this);
        //将当前ViewStub从父布局中移除
        parent.removeViewInLayout(this);
        //然后将ViewStub内的布局直接添加到父布局中
        final ViewGroup.LayoutParams layoutParams = getLayoutParams();
        if (layoutParams != null) {
            parent.addView(view, index, layoutParams);
        } else {
            parent.addView(view, index);
        }
    }

看完之后我们就清楚了,相比于直接把视图放入xml中,ViewStub一开始并不会直接进行inflate操作,而是等到手动调用inflate或者setVisibility(View.VISIBLE),也就是说在ViewStub中的layout的inflate被延迟处理了

结论:ViewStub的使用场景就是一个视图需要满足一定条件的情况下才显示,此时该视图就应该通过ViewStub修饰,通过延迟加载的方式来优化原始原来布局的测量等速度,因为这个布局不一定可见

布局优化

我们知道Android的测量是从顶层布局开始然后一直向下分发,所谓布局优化,实际上就是降低inflate和测量/布局的耗时,比方说对比于RelativeLayout和LinearLayout,先看一个布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text1"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text2"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text3"
            />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text4"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text5"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text6"
            />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text7"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text8"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text9"
            />

    </LinearLayout>

</LinearLayout>
inflate的情况(不包括系统布局) 测量和布局的耗时

然后看一下直接使用RelativeLayout的效果

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="text1"
        />

    <TextView
        android:id="@+id/tv_text2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="text2"
        android:layout_toRightOf="@+id/tv_text1"
        />

    <TextView
        android:id="@+id/tv_text3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="text3"
        android:layout_toRightOf="@+id/tv_text2"
        />

    <TextView
        android:id="@+id/tv_text4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="text4"
        android:layout_below="@+id/tv_text1"
        />

    <TextView
        android:id="@+id/tv_text5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="text5"
        android:layout_toRightOf="@+id/tv_text4"
        android:layout_below="@+id/tv_text1"
        />

    <TextView
        android:id="@+id/tv_text6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="text6"
        android:layout_toRightOf="@+id/tv_text5"
        android:layout_below="@+id/tv_text1"
        />

    <TextView
        android:id="@+id/tv_text7"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="text7"
        android:layout_below="@+id/tv_text4"
        />

    <TextView
        android:id="@+id/tv_text8"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="text8"
        android:layout_toRightOf="@+id/tv_text7"
        android:layout_below="@+id/tv_text4"
        />

    <TextView
        android:id="@+id/tv_text9"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="text9"
        android:layout_toRightOf="@+id/tv_text8"
        android:layout_below="@+id/tv_text4"
        />

</RelativeLayout>
inflate的情况(不包括系统布局)
测量和布局的耗时

大致分析一下差别,其实可以看出,如果在同样的实现效果下,使用RelativeLayout可以减少视图的个数,从而优化inflate的效率,相对于测量和布局来说,这个比较明显
当然例子中的布局层级比较简单,所以说没有看出measure/layout上面的差别,当布局特别复杂的时候,这个也是要考虑的部分

结论:
优先应该考虑降低布局的层级,实际上也就是降低视图的数量,这样可以相对有效的降低inflate的耗时

merge

上面提到了要降低布局层级,实际开发中会出现一种情况,比方说setContentView,实际上父布局就是一个FrameLayout,如果此时我们的xml中父布局是FrameLayout

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/iv_image2"
        android:layout_width="36dp"
        android:layout_height="36dp"
        android:src="@mipmap/ic_launcher"
        android:layout_gravity="center"
        />

    <ImageView
        android:id="@+id/iv_image3"
        android:layout_width="36dp"
        android:layout_height="36dp"
        android:src="@mipmap/ic_launcher"
        android:layout_gravity="right"
        />

</FrameLayout>

此时想到于FrameLayout中嵌套FrameLayout,很明显这个是多余,所幸的是Android提供了方式来处理,可以通过merge标签来合并

<?xml version="1.0" encoding="utf-8"?>
<merge
    xmlns:android="http://schemas.android.com/apk/res/android">

    <ImageView
        android:id="@+id/iv_image2"
        android:layout_width="36dp"
        android:layout_height="36dp"
        android:src="@mipmap/ic_launcher"
        android:layout_gravity="center"
        />

    <ImageView
        android:id="@+id/iv_image3"
        android:layout_width="36dp"
        android:layout_height="36dp"
        android:src="@mipmap/ic_launcher"
        android:layout_gravity="right"
        />

</merge>

看一下LayoutInflate的具体处理

    ...
    if (TAG_MERGE.equals(name)) {
        if (root == null || !attachToRoot) {
            throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
            }

            rInflate(parser, root, inflaterContext, attrs, false);
            } else {
    ...

可以看到这里直接进入rInflate,实际上就是继续解析一下标签,也就是说inflate遇到merge之后就直接pass,然后把内部的视图添加到merge外部的视图当中

测量优化

我们知道视图在使用的时候必须要进行测量,具体的意义就是要确定子视图的大小,那么这一块的耗时也会影响到一帧的流畅性,也应该是优化考虑的一部分
比方说LinearLayout和RelativeLayout,最明显的区别就是LinearLayout对内部的子视图只测量一次,而RelativeLayout要对内部的子视图测量两次,所以说测量的速度上面来说LinearLayout会优于RelativeLayout,布局的话因为RelativeLayout在测量的时候就计算好了子视图的位置,所以相对LinearLayout来说可能会快一点。

结论:
从使用的角度来说,如果可以单一的使用LinearLayout或者RelativeLayout的话确实是比较好的选择,否则可以考虑通过自定义视图的方式来实现测量和布局,自己实现可以避免了大量的无意义的计算操作,效率会更好

总结

UI这块的优化相对比较复杂,具体的情况还是要做深入的分析,个人认为原则上就是尽量降低视图层级,能自己写一个ViewGroup直接摆好的那就最好,如果时间充裕的话,也可以考虑通过Systrace来进行对比,选择合理方案,这一节讲了视图层级优化,下一节看看具体的工具

文章系列:
基本的优化总结(一)
基本的优化总结(二)
基本的优化总结(三)
基本的优化总结(四)
基本的优化总结(五)
基本的优化总结(六)
基本的优化总结(七)

相关文章

  • 基本的优化总结(四)

    导言 这节主要是讲一下布局方面关于UI的优化手段,属于编码中的一些细节处理 UI流畅性优化 先看Systrace中...

  • IOS的性能优化包括哪几点

    iOS性能优化总结 iOS性能优化总结。关于 iOS 性能优化梳理: 基本工具、业务优化、内存优化、卡顿优化、布局...

  • iOS必读 - 收藏集 - 掘金

    iOS 性能优化总结 - iOS - 掘金关于iOS 性能优化梳理: 基本工具、业务优化、内存优化、卡顿优化、布局...

  • 基本的优化总结(七)

    导言 上一节主要讲了分析内存问题的一些工具,这一节主要是总结一些常见的场景 内存泄漏 说了那么久的内存泄漏,实际上...

  • 基本的优化总结(六)

    导言 这节关注一下MEMORY分析的相关工具 Android Profiler中的MEMORY篇 首先看一些基本的...

  • 基本的优化总结(五)

    导言 这一节主要介绍测量/布局/绘制的检查工具Hierarchy,上节提到了一些优化手段,不过这也需要工具来帮助我...

  • 基本的优化总结(八)

    导言 这一节目标着眼于常见的网络请求方面的优化 基础 首先分析一个网络请求的基本过程1.创建一个请求,然后在工作线...

  • 基本的优化总结(二)

    导言 上一篇描述了通过Systrace分析绘制的问题,里面也有提到过,某一帧绘制过久,那么这可能是代码等地方有问题...

  • 基本的优化总结(一)

    导言 首先说一下个人的思考,为什么要做优化?1.差异性:同样的功能,你的更加流畅、易于使用,那么就会有一定的用户粘...

  • 基本的优化总结(三)

    导言 这节主要讲的是UI优化方面关于GPU相关的知识,比方说过渡绘制和硬件加速 过渡绘制 在Google的教程中我...

网友评论

      本文标题:基本的优化总结(四)

      本文链接:https://www.haomeiwen.com/subject/jiekyftx.html