新闻类App (MVP + RxJava + Retrofit+Dagger+ARouter)性能优化之App布局优化

Github地址:新闻类App (MVP + RxJava + Retrofit+Dagger+ARouter)

绘制原理

  • CPU负责计算显示的内容
  • GPU负责栅格化(UI元素绘制到屏幕上)
  • 16ms发出VSync信号触发UI渲染
  • 大多数Android设备屏幕刷新频率:60Hz

优化工具

Systrance

  • 关注Framas
  • 正常:绿色圆点,丢帧:黄色或者红色
  • Alerts栏

Layout Inspector 查看视图层次结构

image.png

我的页面结果
image.png

Choreographer
获取fps,线上使用,具备实时性

  • API16之后
  • Choreographer.getInstance().postFrameCallback
  • 代码
    private int mFrameCount = 0;
    private static final long MONITOR_INTERVAL = 160L; //单次计算FPS使用160毫秒
    private static final long MONITOR_INTERVAL_NANOS = MONITOR_INTERVAL * 1000L * 1000L;
    private static final long MAX_INTERVAL = 1000L; //设置计算fps的单位时间间隔1000ms,即fps/s;
    private long mStartFrameTime=0;

    private void getFPS() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
            return;
        }
        Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                if (mStartFrameTime == 0) {
                    mStartFrameTime = frameTimeNanos;
                }
                long interval = frameTimeNanos - mStartFrameTime;
                if (interval > MONITOR_INTERVAL_NANOS) {
                    double fps = (((double) (mFrameCount * 1000L * 1000L)) / interval) * MAX_INTERVAL;
                    LogUtils.e(fps);
                    mFrameCount = 0;
                    mStartFrameTime = 0;
                } else {
                    ++mFrameCount;
                }

                Choreographer.getInstance().postFrameCallback(this);
            }
        });
    }

获取布局耗时

  • 背景:获取每个界面加载耗时
  • 实现:覆写方法,手动埋点
  • AOP实现,关于AOP的使用大家可以看我之前启动优化这篇文章//www.greatytc.com/p/6d5cddd56d94
 @Around("execution (* android.app.Activity.setContentView**(..))")
    public void getSetContentViewTime(ProceedingJoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();

        long time = System.currentTimeMillis();
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        Log.e("SectionAspect", signature.getName() + "  cost Time:" + (System.currentTimeMillis() - time));
    }

获取某个布局每个控件耗时

  @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        LayoutInflaterCompat.setFactory2(getLayoutInflater(), new LayoutInflater.Factory2() {
            @Override
            public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {

                long time = System.currentTimeMillis();
                View view = getDelegate().createView(parent, name, context, attrs);
                LogUtils.e(name + " cost " + (System.currentTimeMillis() - time));
                return view;
            }

            @Override
            public View onCreateView(String name, Context context, AttributeSet attrs) {
                return null;
            }
        });
        super.onCreate(savedInstanceState);
    }

异步Inflate

  • 背景介绍
    布局文件读取慢,创建View慢(反射:比new慢3倍)
  • AsyncLayoutInflater实践,只是缓解并不能根本去解决
    WorkThread加载布局->回掉主线程 ->节约主线程时间

添加依赖

   implementation 'com.android.support:asynclayoutinflater:28.0.0-alpha1'

代码

 new AsyncLayoutInflater(this).inflate(R.layout.activity_main, null, new AsyncLayoutInflater.OnInflateFinishedListener() {
           @Override
           public void onInflateFinished(@NonNull View view, int i, @Nullable ViewGroup viewGroup) {
               setContentView(view);
               //初始化一些参数比如findViewById等
           }
       });
  • 缺陷:不能设置layoutInflater.Factory,view中不能有依赖主线程的操作

布局加载优化

  • java代码写布局
    本质上解决了问题,但是不便于开发,可维护性差

  • X2C框架
    保留XML优点,解决其性能问题

  • 开发人员写xml,加载Java代码

  • 原理:APT编译期间翻译XML为Java代码

  • 缺点:部分属性Java不支持,失去了系统的兼容,不能用于线上

依赖

    annotationProcessor 'com.zhangyue.we:x2c-apt:1.1.2'
    implementation 'com.zhangyue.we:x2c-lib:1.0.6'

代码

@Xml(layouts = "activity_main")
public class MainActivity extends BaseActivity

原本的setContentView->  X2C.setContentView(MainActivity.this,R.layout.activity_main);

视图绘制优化

  • 减少View树层级
  • 布局宽而浅,避免窄而深

布局的选择

  • ConstraintLayout
    实现几乎完全扁平化布局
    构建复杂布局性能更高
    具有RelativeLayout和LinearLayout特性
  • FrameLayout能实现的优先使用FrameLayout
  • 优先选择RelativeLayout
  • 当在不嵌套的情况下,RelativeLayout和LinearLayout同时能满足需求时,优先选择LinearLayout。因为RelativeLayout功能复杂且会出现重复绘制
  • 不嵌套使用RelativeLayout
  • 不在嵌套linearLayout中使用weight
  • 使用include
    目的是提高代码的复用性,减少代码,将布局中公共部分抽取供其他layout使用
  • 使用merge
    解决布局层次级的优化,减少布局嵌套的层次,提高布局加载的效率
  • 使用viewStub
    ViewStub只有加载该布局的时候才占用资源,INVISIBLE状态是不会绘制出来的。如:加载网络错误的时候显示的布
  • onDraw中避免:创建大对象,耗时操作
  • TextView优化

过度绘制

  • 一个像素最好只被绘制一次
  • 调试GPU过度绘制
  • 蓝色可以接受

避免过度绘制的方法

  • 去掉多余背景色,减少复杂shape使用
  • 避免层级叠加
  • 自定义view使用clipRect 屏蔽被覆盖View绘制

打开自己的GPU过度绘制


image.png

我的app优化后的效果


image.png

蓝色1x过度绘制
绿色2x过度绘制
淡红色3x过度绘制
红色超过4x过度绘制

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容

  • 设计师,开发人员,需求研究和测试都会影响到一个app最后的UI展示,所有人都很乐于去建议app应该怎么去展示UI。...
    瑜小贤阅读 1,135评论 0 3
  • 注意事项: 布局优化;尽量使用include、merge、ViewStub标签,尽量不存在冗余嵌套及过于复杂布局(...
    HarryXR阅读 5,166评论 1 19
  • 相关文章 Android绘制优化(一)绘制性能分析 前言 我们知道一个界面的测量和绘制是通过递归来完成的,减少布局...
    刘望舒阅读 1,291评论 0 11
  • 我们通过对View的源码分析,其实发现View的测量和绘制都是递归实现的,布局是一个多叉树的结构,多叉树遍历所需的...
    dev_journey阅读 970评论 0 1
  • 1.孩子对性别有了概念 孩子慢慢长大后,对男孩和女孩有了一些概念,明白了生理上的一些差别。这时父母可以给孩子普及一...
    诚思心梦阅读 236评论 4 6