之前的文章从理论上介绍了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。
XUtils的TraceView消耗389.371毫秒,占CPU时间25.6%
主要的性能消耗是线程池的创建和一次网络请求
占用内存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耗时
采用retrofit2+okhttp3 进行第一次网络调用,启动到请求完成共耗时574毫秒,接口返回速度上没有太大差别,冷启动时间表现比xutils好,
内存占用小于xutils,使用Xutils的内存比retrofit2+okhttp3 要大将近4MB,这个还是蛮大的,对于移动设备来说。xutils其他部分在本例中并没有使用,可以忽略。
采用retrofit2+okhttp3的框架的TraceView,我们看到线程池占用的cpu时间是比较小额,下面我们从线程池的角度分析比较两种框架
@Override
public <T> Callback.Cancelable post(RequestParams entity, Callback.CommonCallback<T> callback) {
return request(HttpMethod.POST, entity, callback);
}
@Override
public <T> Callback.Cancelable request(HttpMethod method, RequestParams entity, Callback.CommonCallback<T> callback) {
entity.setMethod(method);
Callback.Cancelable cancelable = null;
if (callback instanceof Callback.Cancelable) {
cancelable = (Callback.Cancelable) callback;
}
HttpTask<T> task = new HttpTask<T>(entity, cancelable, callback);
return x.task().start(task);
}
public HttpTask(RequestParams params, Callback.Cancelable cancelHandler,
Callback.CommonCallback<ResultType> 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<Runnable>(), 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,替换它能够优化网络调用的性能,并且会有较低的内存。