Android高性能编码实战:网络框架优化

之前的文章从理论上介绍了Android高性能编码的几个优化的方向,下面我们从实战的角度讲述如何优化

Android高性能编码实战:App启动优化

Android高性能编码实战:网络框架优化

Android高性能编码实战:修复内存泄漏

之前的App启动优化最后提到了网络框架的优化问题,本篇将针对这个问题对APP进一步优化

04-26 18:14:59.504 11295-11295/com.js.test E/xutils: app start at 1493201699503
04-26 18:14:59.845 11295-11295/com.js.test E/xutils: Launch app cost time 342
04-26 18:14:59.845 11295-11295/com.js.test E/xutils: Enter appFragment cost time 342
04-26 18:15:00.088 11295-11295/com.js.test E/xutils: Call first api cost time 585

之前优化过后第一次网络调用的结果

I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +618ms (total +1s296ms)
I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +403ms (total +1s18ms)
I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +411ms (total +1s38ms)
I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +415ms (total +1s31ms)
I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +417ms (total +1s47ms)
I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +415ms (total +1s45ms)

冷启动APP到完全展示的耗时

Xutls,作为一个快速开发的框架,Xutils集成了网络调用、数据库、图片处理和View注入等常用的模块,解决了快速开发的难点,基本上开发人员使用封装过的xutil之后,就只剩下界面和业务逻辑了,非常方便。好了,言归正传,我们先对比一下使用Xutils3 和Retrofit + OkHttp3的网络框架处理网络请求的TraceView。

Android高性能编码实战:网络框架优化_第1张图片

XUtils的TraceView消耗389.371毫秒,占CPU时间25.6%

主要的性能消耗是线程池的创建和一次网络请求

Android高性能编码实战:网络框架优化_第2张图片

占用内存38.47MB

04-26 18:18:16.197 17131-17131/com.js.test E/retrofit: app start at 1493201896196
04-26 18:18:16.535 17131-17131/com.js.test E/retrofit: Launch app cost time 339
04-26 18:18:16.535 17131-17131/com.js.test E/retrofit: Enter appFragment cost time 339
04-26 18:18:16.770 17131-17131/com.js.test E/retrofit: Call first api cost time 574

采用retrofit2+okhttp3 启动APP到第一次网络调用耗时(两种方式均采用fastJson进行json解析)

I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +469ms (total +1s165ms)
I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +389ms (total +1s12ms)
I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +379ms (total +1s6ms)
I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +381ms (total +1s10ms)
I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +370ms (total +992ms)
I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +372ms (total +996ms)
I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +377ms (total +1s2ms)
I/ActivityManager: Displayed com.js.test/.ui.activity.MainActivity: +391ms (total +1s12ms)

冷启动APP耗时

Android高性能编码实战:网络框架优化_第3张图片

采用retrofit2+okhttp3 进行第一次网络调用,启动到请求完成共耗时574毫秒,接口返回速度上没有太大差别,冷启动时间表现比xutils好,

内存占用小于xutils,使用Xutils的内存比retrofit2+okhttp3 要大将近4MB,这个还是蛮大的,对于移动设备来说。xutils其他部分在本例中并没有使用,可以忽略。

Android高性能编码实战:网络框架优化_第4张图片

采用retrofit2+okhttp3的框架的TraceView,我们看到线程池占用的cpu时间是比较小额,下面我们从线程池的角度分析比较两种框架

    @Override
    public  Callback.Cancelable post(RequestParams entity, Callback.CommonCallback callback) {
        return request(HttpMethod.POST, entity, callback);
    }

    @Override
    public  Callback.Cancelable request(HttpMethod method, RequestParams entity, Callback.CommonCallback callback) {
        entity.setMethod(method);
        Callback.Cancelable cancelable = null;
        if (callback instanceof Callback.Cancelable) {
            cancelable = (Callback.Cancelable) callback;
        }
        HttpTask task = new HttpTask(entity, cancelable, callback);
        return x.task().start(task);
    }

    public HttpTask(RequestParams params, Callback.Cancelable cancelHandler,
                    Callback.CommonCallback callback) {
        super(cancelHandler);
......

        // init executor
        if (params.getExecutor() != null) {
            this.executor = params.getExecutor();
        } else {
            if (cacheCallback != null) {
                this.executor = CACHE_EXECUTOR;
            } else {
                this.executor = HTTP_EXECUTOR;
            }
        }
    }

进行第一次请求时,创建HttpTask,引用线程池

    private static final PriorityExecutor HTTP_EXECUTOR = new PriorityExecutor(5, true);
    private static final PriorityExecutor CACHE_EXECUTOR = new PriorityExecutor(5, true);

Xutils的线程池是HttpTask静态成员变量,类加载时自动创建,线程池corePoolSize是5,居然创建了两个线程池。

  /** Executes calls. Created lazily. */
  private ExecutorService executorService;

okhttp3 在Dispatcher里面的线程池是使用时才创建的

  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

并且,线程池创建时corePoolSize的大小为0

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

线程池 execute,先判断线程池的corePoolSize是否足够,不够double check之后,创建新的worker add进来。

xutils由于创建了多余的线程池,而且线程数量多,异步占用的cpu时间是比较高的,这个增加冷启动的时间,也消耗了很多内存。

进过分析,xutils框架在网络调用方面表现不如retrofit2+okhttp3,替换它能够优化网络调用的性能,并且会有较低的内存。



你可能感兴趣的:(Android高性能编码,Android,总结)