前言

首先要做相关优化,就得先要大致清晰影响性能的相关因素,这样可以做针对性调优会比较有条理。

比较常见的性能调优因素有:

  • 内存:Java 一般通过 JVM 对内存进行分配管理,主要是用 JVM 中堆内存来存储 Java 创建的对象。系统堆内存的读写速度非常快,所以基本不存在读写性能瓶颈。但由于内存成本要比磁盘高,相比磁盘,内存的存储空间又非常有限。所以当内存空间被占满,对象无法回收时,就会导致内存溢出、内存泄露等问题。
  • 异常:抛出异常需要构建异常栈,对异常进行捕获和处理,这个过程非常消耗系统性能。
  • 网络:对于传输数据比较大,或者是并发量比较大的系统,网络就很容易成为性能瓶颈。
  • CPU: 复杂的计算,会长时间,频繁地占用cpu执行资源;例如:代码递归调用,JVM频繁GC以及多线程情况下切换资源都会导致CPU资源繁忙。

对以上这些因素可以在代码中做相关优化处理。

延迟加载(懒加载)优化

了解预加载

  • ViewPager控件有预加载机制,即默认情况下当前页面左右相邻页面会被加载,以便用户滑动切换到相邻界面时,更加顺畅的显示出来
  • 通过ViewPager的setOffscreenPageLimit(int limit)可设置预加载页面数量

介绍延迟加载

等页面UI展示给用户时,再加载该页面数据(从网络、数据库等),而不是依靠ViewPager预加载机制提前加载部分,甚至更多页面数据。可提高所属Activity的初始化速度,另一方面也可以为用户节省流量.而这种延迟加载方案已经被诸多APP所采用。

相关概括

  • 没有打开页面,就不预加载数据,当页面可见时,才加载所需数据。
  • 换句话说延迟加载就是可见时才去请求数据。
  • 实际应用开发中有哪些延迟加载案例:
  • ViewPager+Fragment 搭配使用延迟加载
  • H5网页使用延迟加载

ViewPager与Fragment延迟加载的场景

  • ViewPager中setOffscreenPageLimit(int limit)部分源码
//默认的缓存页面数量(常量)
private static final int DEFAULT_OFFSCREEN_PAGES = 1;

//缓存页面数量(变量)
private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;

public void setOffscreenPageLimit(int limit) {
    //当我们手动设置的limit数小于默认值1时,limit值会自动被赋值为默认值1(即DEFAULT_OFFSCREEN_PAGES)
    if (limit < DEFAULT_OFFSCREEN_PAGES) {
        Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "+ DEFAULT_OFFSCREEN_PAGES);
        limit = DEFAULT_OFFSCREEN_PAGES;
    }

    if (limit != mOffscreenPageLimit) {
        //经过前面的拦截判断后,将limit的值设置给mOffscreenPageLimit,用于
        mOffscreenPageLimit = limit;
        populate();
    }
}
  • 思路分析:Fragment中setUserVisibleHint(),此方法会在onCreateView()之前执行,当viewPager中fragment改变可见状态时也会调用,当fragment 从可见到不见,或者从不可见切换到可见,都会调用此方法,使用getUserVisibleHint() 可返回fragment是否可见状态。在onActivityCreated()及setUserVisibleHint()方法中都调一次lazyLoad() 方法。
public abstract class BaseMVPLazyFragment<T extends IBasePresenter> extends BaseMVPFragment<T> {
    /**
    * Fragment的View加载完毕的标记
    */
    protected boolean isViewInitiated;
    /**
    * Fragment对用户可见的标记
    */
    protected boolean isVisibleToUser;
    /**
    * 是否懒加载
    */
    protected boolean isDataInitiated;
    
    ...

    /**
    * 第一步,改变isViewInitiated标记
    * 当onViewCreated()方法执行时,表明View已经加载完毕,此时改变isViewInitiated标记为true,并调用lazyLoad()方法
    */
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        isViewInitiated = true;
        //只有Fragment onCreateView好了,
        //另外这里调用一次lazyLoad()
        prepareFetchData();
        //lazyLoad();
    }

    /**
    * 第二步
    * 此方法会在onCreateView()之前执行
    * 当viewPager中fragment改变可见状态时也会调用
    * 当fragment 从可见到不见,或者从不可见切换到可见,都会调用此方法
    */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        this.isVisibleToUser = isVisibleToUser;
        prepareFetchData();
    }

    /**
    * 第四步:定义抽象方法fetchData(),具体加载数据的工作,交给子类去完成
    */
    public abstract void fetchData();

    /**
    * 第三步:在lazyLoad()方法中进行双重标记判断,通过后即可进行数据加载
    * 第一种方法
    * 调用懒加载,getUserVisibleHint()会返回是否可见状态
    * 这是fragment实现懒加载的关键,只有fragment 可见才会调用onLazyLoad() 加载数据
    */
    private void lazyLoad() {
        if (getUserVisibleHint() && isViewInitiated && !isDataInitiated) {
            fetchData();
            isDataInitiated = true;
        }
    }

    /**
    * 第二种方法
    * 调用懒加载
    */
    public void prepareFetchData() {
        prepareFetchData(false);
    }

    /**
    * 第三步:在lazyLoad()方法中进行双重标记判断,通过后即可进行数据加载
    */
    public void prepareFetchData(boolean forceUpdate) {
        if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {
            fetchData();
            isDataInitiated = true;
        }
    }
}

多线程优化: 建议使用线程池

用线程池的好处

可重用线程池中的线程,避免频繁地创建和销毁线程带来的性能消耗;有效控制线程的最大并发数量,防止线程过大导致抢占资源造成阻塞;可对线程进行有效管理

  • RxJava,RxAndroid,底层对线程池的封装管理非常值得参考。