美文网首页我的2019安卓Android架构
新闻类App (MVP + RxJava + Retrofit+

新闻类App (MVP + RxJava + Retrofit+

作者: Peakmain | 来源:发表于2019-04-17 11:20 被阅读305次

    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的使用大家可以看我之前启动优化这篇文章https://www.jianshu.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过度绘制

    相关文章

      网友评论

        本文标题:新闻类App (MVP + RxJava + Retrofit+

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