Fresco是二个有力的图纸加载库,想必作者等码农都有使用过和询问,fresco官网。此前app端做过那样八个优化依照用户手提式有线电话机的网络环境加载对应的清晰度的图形,一来是晋升用户体验加载快,二来节省用户的流量毕竟我们的海外用户流量很贵的。然后功用大家落实了,分化互连网环境请求例外的图形对应不一致的U汉兰达L。
–在此基础上领导提议个新必要,当在流量环境下一旦有Wi-Fi高清图片的缓存,就利用高清缓存的图形,不再去恳求低分辨率的图样。fresco的三级缓存策略的前提条件是唯一的U帕杰罗L明确某一张图纸。而官员的供给想当如U帕杰罗L_a的乞求使用ULANDL_b的缓存。
流量优化对于一个app来讲意义拾分首要,能节省用户的流量,节约用户的存款和储蓄空间,而且能有增高互联网请求的回包速度,进步app的快慢。由此流量优化历来都以app的优化重点,而且是一个穿梭优化的点。
好奇心晚报是二个多图片的新闻类应用,采访编辑喜欢上传gif图来升高内容的表现力,那也使得流量消耗不小。粗略估算,用户在浏览完第三页全体音讯(共48篇),会消耗流量达100m,个中98m为图片,那里值得优化的上空充裕大。
本着那种场馆,大家先后采纳的优化包蕴:wifi条件下预载全数小说、图片和js、css数据;重用全部曾经下载的js、css和图表的缓存;后台图片的压缩以及客户端图片的WebP化。
在这之中,后台压缩和WebP化信赖第贰方多媒体处理服务器,已知比较好的境内服务有腾讯优图和七牛。那里我们运用的七牛的劳务,以下居多切实可行的调用都是依据七牛的。
我们的后台通过七牛的图样压缩(包罗质量和分辨率),大家将首页流量由100m减弱到了80m,如故有极大的升级换代空间。因而客户端选拔基于WebP的流量压缩方案,将流量由80m精减到了20m,裁减了四分之三!相对于中期的处理,流量减弱了五分之四!(android半数以上机型支持WebP
animated,压缩能达到百分之八十,但iOS的压缩率取决于首页中gif图的个数和分寸,测试大致优化在60%-8/10之间)上面就介绍下那些效率极好的WebP的流量消除方案。
尝试一:setFirstAvailableImageRequests 方式。
查阅fresco文书档案发现有【多图请求及图片复用】介绍,里面包车型大巴【加载开端可用的图】小段讲的闲事例是图片上传,本地图片和网络下载图片选择该格局,咱们的情状那些如同能够也适用。
Uri uri1, uri2;
ImageRequest request1 = ImageRequest.fromUri(uri1);
ImageRequest request2 = ImageRequest.fromUri(uri2);
ImageRequest[] requests = { request1, request2 };
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setFirstAvailableImageRequests(requests)
.setOldController(mSimpleDraweeView.getController())
.build();
mSimpleDraweeView.setController(controller);
可实际自己断点测试的结果却非文书档案所讲,有小概率会会达到梦想洗过。可基本上时候是先使用request1请求的图形,及时是有request2的缓存图片,request1须求网络请求下载图片的。
一、WebP的介绍
品尝二:代码中判断是还是不是留存Wi-Fi高清图片缓存。
if (NetWorkHelper.netWorkState != NetWorkHelper.kNetWorkWIFI && PreferenceUtil.getBool(App.getInstance(), PreferenceUtil.BOOL_NETWORK_GETING_IMAGE, false)) {
//非Wi-Fi环境下,原则上选择当前网络环境的压缩图片。但当存在Wi-Fi环境下的高清图缓存时候,使用Wi-Fi高清图。无则重新下载
boolean isCacheInDisk = Fresco.getImagePipelineFactory().getMainBufferedDiskCache
().containsSync(new SimpleCacheKey(url));
boolean isCacheInFile = Fresco.getImagePipelineFactory().getMainFileCache()
.hasKey(new SimpleCacheKey(url));
ImageRequest request;
if (isCacheInDisk || isCacheInFile) {
request = ImageRequest.fromUri(uri);
} else {
request = ImageRequest.fromUri(Uri.parse(formatUrl(uri.toString(), getWidth(), getHeight())));
}
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setImageRequest(request)
.setCallerContext(callerContext)
.setOldController(getController())
.build();
setController(controller);
} else {
//Wi-Fi环境下选择高清大图
ImageRequest request2 = ImageRequest.fromUri(uri);
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setImageRequest(request2)
.setCallerContext(callerContext)
.setOldController(getController())
.build();
setController(controller);
}
代码逻辑正是判定缓存中是不是留存,存在即请求Wi-Fi环境下的图形url,不存在就请求流量环境下的图样url.至于判断五次缓存是发源fresco的三级缓存原理,2个是内存中的缓存2个是磁盘缓存。同样那里去央求Wi-Fi环境下的图纸UTucsonL因为事先有缓存的,也只是向来动用缓存并不会进展实际网络下载的。经测试也满意了公司主的渴求,在质量上双眼也未发现肯定的出入。
1、什么是 WebP
WebP (发音 weppy
),是一种同时提供了有损压缩与无损压缩的图纸文件格式,是谷歌新推出的影象技术,它可让网页图档有效开始展览压缩,同时又不影响图片格式包容与事实上清晰度,进而让总体网页下载速度加速。
- WebP 无损压缩的图片能够比同一大小的 PNG 小 26%;
- WebP 有损压缩的图样能够比同等大小的 JPEG 小 25-34%;
- WebP 支持无损的晶莹图层通道,代价只需扩张 22% 的字节存款和储蓄空间;
- WebP 有损透明图像能够比同样大小的 PNG 图像小3倍。
总结
固然尝试2的办法实现了领导的供给,可是有个小危害的询问缓存的代码是在主线程中实施的,当上列表的时候有导致页面卡顿的大概,但本人拿项目测试的时候从不发现那样,也就没在意。搜了下还有个点子查询,但是代码格局麻烦了些。还有方式一不行或者是自个儿姿势不对原因,有趣味的多多交换。
二 、手提式有线电话机端帮助情状
- WebP在四哥大端浏览器的帮忙景况(WebView & UIWebView)

1468596300.jpg
- Native
Android在4.0之上Image直接辅助对WebP的解码。iOS能够因此google提供的WebP解析库来贯彻UIImageView中展现WebP。
QDaily不支持4.0以入手提式有线电话机(以往估算也没啥支持的了吗。。。),所以下边包车型客车效能并从未测试,无脑搬运:
Android 4.0 以下 WebP
解析库(链接)
iOS WebP
解析库(链接)
③ 、WebP工具在Mac Os下的安装(本地编解码)
- 方式一(推荐):使用Homebrew
1.设置Homebrew。参考网页http://brew.sh/index\_zh-cn.html
2.装置到位后,用如下命令安装libwebp。
brew install webp
- 方式二:使用MacPorts安装
1.到此网站链接下载与系统版本对应的MacPorts,安装MacPorts在此以前必要安装Xcode。
2.根据此文书档案对MacPorts举办安装,链接,作者选用的是下载.pkg文件进行设置的。整个安装进度中国建筑工程总企业议准备梯子举行正确上网。
3.安装到位后更新:
sudo port -v selfupdate
4.然后安装libwebp,
sudo port install webp
- 选用工具举办WebP编解码
1.行使 cwebp 将 JPEG 或 PNG 图像转换来 WebP 格式。
cwebp [options] -q quality input.jpg -o output.webp
2.使用 dwebp 实用程序将 WebP 图像转换回 PNG、PAM、PPM 或 PGM 图像。
dwebp input_file.webp [options] [-o output_file]
② 、QDaily iOS客户端中图纸流量优化方案
1、iOS Native
QDaily的Native图片加载使用的SDWebImage,该零件直接协助WebP的解码。需求在将预编写翻译宏’WebP’置为1,并在pod中引入’iOS-WebP’即可。
小编们图片展现后台是七牛的,暗中同意传给客户端的参数是一张jpg只怕png的图片链接,通过修改url的请求参数完成对WebP图片的拿走。亲测iOS下对WebP
Animated的支撑很差,平日有转码失利景况,所以iOS中从不辅助WebP的动图呈现。
抱有SDWebImage的图片加载都首先通过SDWebImageManager中上边包车型地铁法门:
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
options:(SDWebImageOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageCompletionWithFinishedBlock)completedBlock
小编们透过在该措施最初修改url的参数来兑现对请求url的更迭和当地缓存的读取:
{
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
if (![url isKindOfClass:NSURL.class]) {
url = nil;
}
url = [url qd_replaceToWebPURLWithScreenWidth];
...
...
}
为NSU科雷傲L扩张增加NSU福特ExplorerL+ReplaceWebP
NSURL+ReplaceWebP.h
@interface NSURL (ReplaceWebP)
- (NSURL *)qd_replaceToWebPURLWithScreenWidth;
- (NSString *)qd_defultWebPURLCacheKey;
- (BOOL)qd_isShouldReplaceImageFormat;
@end
NSURL+ReplaceWebP.m
static NSString * const qdailyHost = @"img.qdaily.com";
@implementation NSURL (ReplaceWebP)
- (NSString *)qd_defultWebPURLCacheKey {
if (![self qd_isShouldReplaceImageFormat]) {
return self.absoluteString;
}
NSString *key;
if ([self isWebPURL]) {
key = self.absoluteString;
} else {
key = [self qd_replaceToWebPURLWithScreenWidth].absoluteString;
}
return key.lowercaseString;
}
- (NSURL *)qd_replaceToWebPURLWithImageWidth:(int)width {
if ([self qd_isShouldReplaceImageFormat]) {
NSString *urlStr;
if ([self URLStringcontainFomartString:@"?"]) {
if ([self URLStringcontainFomartString:@"format/jpg"]) {
urlStr = [self.absoluteString stringByReplacingOccurrencesOfString:@"format/jpg" withString:@"format/webp"];
} else {
NSString *suffixStr = @"imageView2/0/format/webp/ignore-error/1";
urlStr = [NSString stringWithFormat:@"%@/%@", self.absoluteString, suffixStr];
}
} else {
NSString *pathExtension = [[self.absoluteString.pathExtension componentsSeparatedByString:@"-"] firstObject];
urlStr = [NSString stringWithFormat:@"%@.%@-WebPiOSW%d",self.absoluteString.stringByDeletingPathExtension, pathExtension, width];
}
return [NSURL URLWithString:urlStr];
}
return self;
}
- (NSURL *)qd_replaceToWebPURLWithScreenWidth {
int width = (int)([UIScreen mainScreen].bounds.size.width * [UIScreen mainScreen].scale);
return [self qd_replaceToWebPURLWithImageWidth:(int)width];
}
- (BOOL)isQdailyHost {
NSString *nsModel = [UIDevice currentDevice].model;
BOOL s_isiPad = [nsModel hasPrefix:@"iPad"];
if (s_isiPad) return NO;
return [self URLStringcontainFomartString:qdailyHost];
}
- (BOOL)qd_isShouldReplaceImageFormat {
if (![self isQdailyHost]) {
return NO;
}
if ([self isWebPURL]) {
return NO;
}
NSArray *extensions = @[@".jpg", @".jpeg", @".png"];
for (NSString *extension in extensions) {
if ([self.absoluteString.lowercaseString rangeOfString:extension options:NSCaseInsensitiveSearch].location != NSNotFound){
return YES;
}
}
return NO;
}
- (BOOL)URLStringcontainFomartString:(NSString *)string {
return ([self.absoluteString.lowercaseString rangeOfString:string options:NSCaseInsensitiveSearch].location != NSNotFound);
}
- (BOOL)isWebPURL {
return [self URLStringcontainFomartString:@"-webp"] || [self URLStringcontainFomartString:@"/webp"];
}
@end
因改动了url值,若在上层通过SDImageCache判断是不是有当地缓存时,也亟需对url先做qd_defultWebPU奥德赛LCacheKey来收获其诚实缓存的key。
2、iOS WebView中
苹果连串具有的webkit内核现在都不援助解析WebP格式的图片,QDaily处理那里关键使用的iOS系统的NSU帕杰罗LProtocol来替换其网络请求,再讲互连网回包数据实行转码成jpg大概png(为了发光度),再回来给webview进行渲染的。
一如既往的,iOS在此间依旧不对gif举办其余处理。
除此以外要小心的是,NSU奥德赛LProtocol会拦截全局的网络流量,为制止误伤,那里须要单独识别是或不是是WebView发起的伸手,能够通过识别request中的ua是或不是含有”AppleWebKit”来兑现。
直接上代码:
@implementation QDWebURLProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
NSString *ua = [request valueForHTTPHeaderField:@"User-Agent"];
if ([request.URL qd_isShouldReplaceImageFormat] && [ua lf_containsSubString:@"AppleWebKit"]) {
return YES;
}
return NO;
}
+(NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request{
return request;
}
- (void)startLoading {
if ([self.request.URL qd_isShouldReplaceImageFormat]) {
[[SDWebImageManager sharedManager] downloadImageWithURL:self.request.URL
options:0
progress:nil
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL)
{
NSData *data;
if ([imageURL.absoluteString.lowercaseString lf_containsSubString:@".png"]) {
data = UIImagePNGRepresentation(image);
} else {
data = UIImageJPEGRepresentation(image, 1);
}
[self.client URLProtocol:self didLoadData:data];
[self.client URLProtocolDidFinishLoading:self];
}];
}
}
- (void)stopLoading {
}
@end
别的,QDaily完毕了中间文章的缓存,包涵js、css以及image等。那里通过NSUWranglerLCache来完毕。相应的,基于WebP的图纸缓存的读取也应当在NSURAV4LCache中拍卖,在先处理完UPRADOL后,用新的Key来展开映射。
此地提出持有基于WebView的流量优化都最好用UA的判断包住,幸免带来难题。因为不论是NSU汉兰达LProtocol还是NSUHighlanderLCache都是全局网络决定。
三 、QDaily Android客户端的图形流量优化方案
WebP正是google出的编码格式,和Android同宗同源,协理自然会好一些。4.0之上的系统,原生暗许帮衬WebP的来得。可是出于国内android手提式无线电话机种种奇葩的体系,有必不可少在WebView中开始展览WebP的分辨支持。
1、Native部分的WebP
QDaily的Android客户端的图样请求使用的glide,所有请求的缓存通过GlideModule的款式开始展览任何的地头接管,没有选用他们暗中同意的cache。
为领悟耦方便和制止侵入性,大家在glide和工作代码之间封装了ImageManager,全体的图片请求都会走这里开始展览发送,因而,WebP的优化和处理重庆大学在此间展开。
重中之重涵盖以下多少个格局:
//主处理方法,根据对url进行处理
public static String getWebpUrl(String url, boolean isWebView);
//判断当前url是否已经是WebP的请求了
public static boolean isWebP(String url);
//url是否支持转成WebP请求
public static boolean supportConvertWebP(String url);
//获取文章的host
public static String getHost(String url);
//域名判断,限定七牛域名才可以进行转换url
public static boolean isQiNiuImageHost(String url);
// url后跟的WebP请求后缀
public static String getWebpExtBaseScreen();
// WebView对WebP静图的支持
public static boolean isWebViewSupportWebp();
// WebView对WebP动图的支持
public static boolean isWebViewSupportWebpAnimation();
// Native对WebP静图的支持
public static boolean isSupportWebp();
// Native对WebP动图的支持
public static boolean isSupportWebpAnimation();
有着Native部分的ImageView在加载图片时候,调用的章程第2步,会先调用getWebpUrl方法对url举行处理,该方法会依照机器状态展开相应的url参数拼凑。具体达成如下:
public static String getWebpUrl(String url, boolean isWebView) {
if (TextUtils.isEmpty(url)) {
QLog.e("", "url 不能为空");
return "";
}
boolean isSupportWebp = isWebView? isWebViewSupportWebp(): isSupportWebp();
boolean isSupportWebpAnimation = isWebView? isWebViewSupportWebpAnimation(): isSupportWebpAnimation();
if (supportConvertWebP(url) && isSupportWebp) {
String ext = MimeTypeMap.getFileExtensionFromUrl(url);
if (TextUtils.isEmpty(ext) || (ext.contains("gif") && !isSupportWebpAnimation)) //扩展名为空就不知道是什么鬼,不转
return url;
if (url.contains("?")) {
if (url.contains("format/jpg")) {
return url.replace("format/jpg", "format/webp");
}
int index = url.indexOf("gif");
if (index != -1) {
return url.substring(0, index) + "gif" + WebpExtGif;
}
return url + WebpExtDefault;
} else {
String query = ext.contains("gif") ? WebpExtGif : getWebpExtBaseScreen();
int index = url.indexOf(ext);
return url.substring(0, index) + ext.split("-")[0] + query;
}
}
return url;
}
public static boolean isWebP(String url) {
return url.contains("/format/webp") || url.contains("-Webp");
}
public static boolean supportConvertWebP(String url) {
return MManagerCenter.getManager(DevConfigManager.class).isUseWebp() && isQiNiuImageHost(url) && !url.contains("/format/webp") && !url.contains("-Webp");
}
//获取文章的host
public static String getHost(String url) {
if (url == null || url.trim().equals("")) {
return "";
}
String host = "";
Pattern p = Pattern.compile("(http://|https://)?([^/]*)", Pattern.CASE_INSENSITIVE);
Matcher matcher = p.matcher(url);
if (matcher.find()) {
host = matcher.group();
}
return host;
}
public static boolean isQiNiuImageHost(String url) {
String host = getHost(url);
return host.equals("http://img.qdaily.com");
}
public static String getWebpExtBaseScreen(){
int width = LocalDisplay.SCREEN_REAL_WIDTH_PIXELS;
if (width >= 1080) {
return WebpExtW3;
}
if (width < 540) {
return WebpExtW1;
}
return WebpExtW2;
}
public static boolean isWebViewSupportWebp(){
return MManagerCenter.getManager(QDConfigManager.class).isWebViewSupportWebp();
}
public static boolean isWebViewSupportWebpAnimation(){
return MManagerCenter.getManager(QDConfigManager.class).isWebViewSupportWebpAnimation();
}
// Native默认支持WebP静图
public static boolean isSupportWebp(){
return true;
}
// TODO: 16/7/15 所有列表页,最终会用ImageView渲染的,暂时不支持webp animated
public static boolean isSupportWebpAnimation(){
return false;
}
2、WebView部分的WebP
基于预加载和缓存的内需,QDaily的稿子选拔的办法是通过接口下载html文件,在地面加载进webview的主意来达成的,具体方法如下:
loadDataWithBaseURL(curUrl, "html string", "text/html", "UTF-8", curUrl);
因为android的WebView能够直接解码WebP格式,所以那里一向开始展览了html中的url替换,即正则取出全体的image请求url,用地方的getWebpUrl的方法进行更换,再交替原来的url即可。具体贯彻方式如下:
public static String converHtmlToWebPHtml(String html){
Map<String, String> map = getSupportWebPImgArray(html);
if (map != null && map.size() > 0) {
for (Map.Entry<String,String> entry: map.entrySet()) {
html = html.replaceAll(entry.getKey(), entry.getValue());
}
}
return html;
}
private static Map<String, String> getSupportWebPImgArray(String html) {
if (!(isWebViewSupportWebp() || isWebViewSupportWebpAnimation()) && TextUtils.isEmpty(html))
return null;
Map<String, String> result = new HashMap<>();
Pattern p = Pattern.compile("<img[^>]*data-src=\"([^\"]*)\"[^>]*>");
Matcher m = p.matcher(html);
while (m.find()) {
String url = m.group(1);
String value = getWebpUrl(url, true);
if (!url.equals(value)) result.put(url, getWebpUrl(url, true));
}
p = Pattern.compile("<img[^>]*src=\"([^\"]*)\"[^>]*>");
m = p.matcher(html);
while (m.find()) {
String url = m.group(1);
String value = getWebpUrl(url, true);
if (!url.equals(value)) result.put(url, getWebpUrl(url, true));
}
return result;
}
3、判断Android端WebView对WebP Animated的支持
法定称4.3上述系统直接支持WebP
Animated,但亲测部分荣耀手机(例如HTC!)在4.4的EMUI中WebView无法健康解码WebP
Animated,消除方案是在App第3回运维时实行WebView的WebP援救情状检查,并将值保存在sp中。
检查办法为在WebView中分别加载1像素的WebP静图和动图,并用JS检查和测试是或不是成功体现,成功展现即为帮助。
直白上代码:
<html>
<head>
<meta charset="UTF-8" />
<title>WEBP TEST</title>
<script type="text/javascript">
;(function() {
var image = new Image();
image.onerror = function() {
console.log('QDaily:supportWebp=false')
};
image.onload = function() {
if (image.width == 1) {
console.log('QDaily:supportWebp=true')
} else {
console.log('QDaily:supportWebp=false')
}
}
image.src = 'data:image/webp;base64,UklGRiwAAABXRUJQVlA4ICAAAAAUAgCdASoBAAEAL/3+/3+CAB/AAAFzrNsAAP5QAAAAAA==';
})();
;(function() {
var image = new Image();
image.onerror = function() {
console.log('QDaily:supportWebpAnimation=false')
};
image.onload = function() {
if (image.width == 1) {
console.log('QDaily:supportWebpAnimation=true')
} else {
console.log('QDaily:supportWebpAnimation=false')
}
}
image.src = 'animation.webp';
})();
</script>
</head>
<body>
</body>
</html>
中间,第①个js方法用来检测是还是不是匡助静态webp,src对应一段webp图片的base64编码,显示出来的大幅度为1px。第二个方法src对应二个动态webp,由于animated
webp的base64比较长,所以一贯将三个1k的1px宽的webp
animation和那段html一起放在了assets中,用于调用检查和测试。
地点说将html文件以及所用到的图样都置身 assets
目录下。然后在页面上通过WebView来显示。上面上webview的检测代码:
//以下代码放在第一个启动的Activity中
private WebView webView;//必须成员变量,临时变量无效
private void checkWebpSupport(){
if (isInitWebViewCheckSupportWebp)
return;
webView = new WebView(this);
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onConsoleMessage(ConsoleMessage cm) {
String log = cm.message();
if (!TextUtils.isEmpty(log) && log.contains("QDaily:")) {
if (log.equals("QDaily:supportWebp=true")) {
setWebViewSupportWebp(true);
} else if (log.equals("QDaily:supportWebp=false")) {
setWebViewSupportWebp(false);
} else if (log.equals("QDaily:supportWebpAnimation=true")) {
setWebViewSupportWebpAnimation(true);
} else if (log.equals("QDaily:supportWebpAnimation=false")) {
setWebViewSupportWebpAnimation(false);
}
isInitWebViewCheckSupportWebp = true;
}
return true;
}
});
//注意下面这行一定要加,否则js不能执行!
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl("file:///android_asset/webp.html");
}
三 、其余能源的缓存优化
QDaily在互联网请求笔者的优化主要有对http请求的gzip压缩,和对图片大小和格式的定制化(依照显示屏尺寸请求大小,依照客户端情形采取是或不是WebP)。
其余一些在流量的优化都在缓存部分,为的是相同财富不再请求第③遍。下边将不难介绍那里,一些切实可行的部分会在其后文章再做展开。
集合图片的当地缓存处理
一个app中,WebView和ImageView都会呈请图片,Android端和iOS端都定制了友好统一的内部存款和储蓄器LRU
Cache和Disk LRU Cache举行处理,幸免再次请求。
css和js的缓存
QDaily里作品用了联合的css和js文件。客户端那边首要举办缓存和复用,以开始展览重复使用
http请求的缓存
此间根本是将volley的缓存管理起来,通过etag进行更新判断
上边介绍的可比范,前面会有比较实际的介绍。
参考链接
http://chiemy.com/android/android-webp
http://hahack.com/wiki/sundries-webp.html
http://blog.teamtreehouse.com/getting-started-webp-image-format
谷歌(谷歌(Google))官方文书档案帮衬
https://developers.google.com/speed/webp/
http://www.smarting.me/glide-images-loading-library-introduction-and-usage.html