android webview webp,iOS WebView中使用webp格式图片的方法

webp格式图片

webp格式图片是google推出的,相比jpg png有着巨大的优势,同样质量的图片webp格式的图片占用空间更小,在像电商这样图片比较多的App中,使用webp格式图片会很有优势。

引言

很早之前,我们的项目中就已经采用了webp格式,但是由于webView本身并不能解析webp格式,所以我们基于webView的文章详情页就无法使用到这项优化。

那么有没有什么办法能实现呢?当然是有的。

在开始技术讲解之前需要先说明,本文的技术方案,是基于本项目的情况:文章的正文大部分通过接口直接获取到,通过在客户端本地进行html正文组装,最后通过webView的loadHTMLString方法进行加载显示。普通的图片可以通过转换链接得到webp服务器获取到相应的webp版的图片。

本项目中,图片缓存使用了SDWebImage,并且开启了webp支持功能,那么我们对详情页webView的处理也会基于此来实现。

通过思考,方案其实还是比较明确的,就是替换html中图片链接,通过客户端下载webp图片,然后在通过js刷新出页面上的下完的图片,但实际开发中也遇到了一些坑,比如:

HTML解析库的setAttributeNamed不能增加属性

webp服务器图片下载后的默认缓存时gif不能正常存储

下载完的图片不能实时通过js更改src为本地文件地址加载出来

最终的技术实现:

1.对下载回来的html内容进行处理,获取所有图片链接,并进行webp链接处理转换

对html内容的解析处理我使用的是Objective-C-HMTL-Parser,但是该库已经多年不维护,这里有我fork后进行部分优化调整的版本:https://github.com/YueRuo/Objective-C-HMTL-Parser (本地下载)

处理html图片核心处理逻辑代码:

@try {

HTMLParser *parser = [[HTMLParser alloc] initWithString:htmlContent error:&error];

HTMLNode *bodyNode = [parser body];

if (error) {

return;

}

//得到所有的img标签

NSArray *inputNodes = [bodyNode findChildTags:@"img"];

for (HTMLNode *inputNode in inputNodes) {

NSString *imageSrc = [inputNode getAttributeNamed:@"src"];

if (!imageSrc) {

continue;

}

NSString *newSrc = [[GlobalVariable shareInstance] resizeWebpImageWithUrl:imageSrc size:CGSizeMake((SCREEN_WIDTH - 20) * 2, 0)];//根据原图片,得到webp服务器使用的图片链接,需要有webp处理服务器

//检查本地图片缓存

NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:[NSURL URLWithString:newSrc]];

NSString *localPath = [[SDImageCache sharedImageCache] defaultCachePathForKey:key];

NSString *webpImage = newSrc;

BOOL localExsit = [[NSFileManager defaultManager] fileExistsAtPath:localPath];

if (localExsit) {

newSrc = [NSString stringWithFormat:@"file://%@", localPath];

}

//存储疑似webp图片和原图片,如果newSrc和webp相同则说明本地没有缓存图片

[_webpImageUrlDic setObject:webpImage forKey:newSrc];

if(localExsit){

setAttributeNamed(inputNode->_node, "src", [newSrc cStringUsingEncoding:NSUTF8StringEncoding]);

}else{

setAttributeNamed(inputNode->_node, "src", "详情页占位图@2x.png");

}

//给img标签中增加一个叫osrc的属性,便于后续处理

setAttributeNamed(inputNode->_node, "osrc", [newSrc cStringUsingEncoding:NSUTF8StringEncoding]);

}

htmlContent = [NSMutableString stringWithString:parser.doc.rawContents];

}

@catch (NSException *exception) {

}

@finally {

[webView loadHTMLString:htmlContent baseURL:baseUrl];

}

2.用原生方法下载webp图片,缓存到本地

下载之后会存储为jpg或png格式,这样就可以被webView进行本地加载,但是需要注意gif的存储特殊处理。

另外通过实验,直接通过js无法实时更新下载到本地的图片,只好通过图片的base64encode数据加载方式实现。

具体代码如下:

- (void)webViewDidFinishLoad:(UIWebView *)web {

//处理webp格式加载

[_webpImageUrlDic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {

if([obj isEqualToString:key]){//说明这图没有缓存,还需要下载

[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:[NSURL URLWithString:obj] options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {

if (image&&finished) {

NSString *js;

NSRange range = [[obj lowercaseString] rangeOfString:@".gif"];//检查是否是gif

BOOL isGif = (range.location != NSNotFound);

if (!isGif) {

[[SDImageCache sharedImageCache] storeImage:image forKey:obj];

NSString *base64 = [UIImageJPEGRepresentation(image,1) base64EncodedStringWithOptions:0];

js = [NSString stringWithFormat:@"replaceWebPImg('%@','data:image/jpeg;base64,%@')",key,base64];

}else{//gif的图片如果直接存储,会变成jpg从而失去动画,因此要特殊处理

[[SDImageCache sharedImageCache] storeImage:image recalculateFromImage:false imageData:data forKey:key toDisk:true];

NSString *base64 = [data base64EncodedStringWithOptions:0];

js = [NSString stringWithFormat:@"replaceWebPImg('%@','data:image/gif;base64,%@')",key,base64];

}

[NSThread excuteInMainThread:^{

[webView stringByEvaluatingJavaScriptFromString:js];

} async:false];

}

}];

} else {//缓存中存在,那么直接加载吧

NSString *js;

NSRange range = [[obj lowercaseString] rangeOfString:@".gif"];//检查是否是gif

NSData* data = [NSData dataWithContentsOfFile:[key stringByReplacingOccurrencesOfString:@"file://" withString:@""]];

NSString *base64 = [data base64EncodedStringWithOptions:0];

BOOL isGif = (range.location != NSNotFound);

if (!isGif) {

js = [NSString stringWithFormat:@"replaceWebPImg('%@','data:image/jpeg;base64,%@')",obj,base64];

}else{

js = [NSString stringWithFormat:@"replaceWebPImg('%@','data:image/gif;base64,%@')",obj,base64];

}

[NSThread excuteInMainThread:^{

[webView stringByEvaluatingJavaScriptFromString:js];

} async:false];

}

}];

}

3.回调webView页面,用本地链接替换原有的图片

加载已下载好的图片,这里主要通过js来实现,即第2步中的replaceWebPImg方法,该js方法可通过提前置于html的模板中,或者webViewDidFinishLoad后采用js注入进去

replaceWebPImg = function(src, localPath) {

var imgs = document.querySelectorAll('img[osrc="'+src+'"]'),len = imgs.length;;

for (var i = 0; i < len; i++) {

var img = imgs[i];

img.src = localPath;

}

}

好再次总结一下整个流程:

对服务器返回的htmlContent数据进行相应处理,检查图片是否存在缓存,存在则使用本地地址为src,不存在则把图片的src替换成占位图。记录下图片地址,并增加属性做好标记。

图片的地址进行webp转换,通过客户端进行下载

下载后的图片,通过js方法进行src更改,并且赋值的base64的图片编码数据,因为给本地地址无法实时展示出来

这篇写的比较简单,更详细的步骤请查阅上面的代码,里面我加上还算详细的注释,希望对想要在webView中使用webp图片的大家有所帮助。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对编程圈的支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/353986.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

呵呵!Function构造函数

今天准备吐槽一下Function构造函数。 我们知道&#xff0c;不管是函数声明还是函数表达式都是基于词法作用域的&#xff0c;明白这点在闭包中十分重要&#xff0c;譬如&#xff1a; var aglobal; function foo(){var alocal;return function(){console.log(a);} } foo()(); …

android关机位置定位,Android5.0关机充电动画位置

充电动画位置bootable\bootloader\lk\dev\logo充电动画对应的分辨率&#xff1a;\bootable\bootloader\lk\project\XX.mkBOOT_LOGO : wxga#LOCAL_DIR : $(GET_LOCAL_DIR)TARGET : teft6752_lwt_lMODULES app/mt_boot \dev/lcmMTK_EMMC_SUPPORT yesDEFINES MTK_NEW_COMBO_EMM…

nosql的数据服务_使用NoSQL实现实体服务–第3部分:CouchDB

nosql的数据服务在本系列的第2部分中 &#xff0c;我使用SOA的“合同优先”技术创建和部署了产品实体服务&#xff0c;现在&#xff0c;我将致力于服务实现的NoSQL数据库方面。 正如我在第1部分中已经提到的那样&#xff0c;我已经选择CouchDB作为我的NoSQL数据库&#xff0c;选…

缓存穿透、缓存击穿与失效时的雪崩效应

问题&#xff0c;原理&#xff0c;解决方案转载于:https://www.cnblogs.com/tuhooo/p/8573614.html

android电视root权限获取,电视盒子/ 智能电视如何通过ADB获取ROOT权限?

如何通过adb获取root权限(安卓电视盒和智能电视通用)?Android 系统rom里面最主要的就3个文件&#xff1a;boot.img、system.img、userdata.img其中boot.img 存放着内核以及Android系统的配置信息&#xff0c;比如android系统各文件夹的读写权限&#xff0c;adb 的权限。所以如…

确定活动的热点垃圾收集器

StackOverflow问题查找正在运行哪种类型的垃圾收集 器&#xff0c;jvm的默认垃圾收集器 &#xff0c; 如何通过查看gc日志来查看正在运行的垃圾收集器&#xff1f; &#xff0c;以及如何知道HotSpot jvm的当前GC策略&#xff1f; 和博客文章如何以编程方式获取GC信息表明了人们…

spring3: Aspectj后置返回通知

Aspectj后置返回通知 接口&#xff1a; package chapter1.server;public interface IHelloService2 {public int sayAfterReturning(String param); }接口实现 package chapter1.service.impl;import chapter1.server.IHelloService2;public class HelloService2 implements IH…

Android加密通信防抓包,[原创]基于Taintdroid思想的android ssl\tsl保密通信抓包研究(未成功,分享一下思路)...

[旧帖][原创]基于Taintdroid思想的android ssl\tsl保密通信抓包研究(未成功&#xff0c;分享一下思路)0.00元2014-5-12 22:071565[旧帖][原创]基于Taintdroid思想的android ssl\tsl保密通信抓包研究(未成功&#xff0c;分享一下思路)0.00元2014-5-12 22:071565这篇文章我先在CS…

app store 关键词

如何选取关键字&#xff0c;让你的应用关键词越来越多&#xff1f; 很多朋友在做应用商店优化的时候&#xff0c;都会遇到一个让人很头疼的问题&#xff1a;如何选取关键词&#xff1f;关键词的质量直接关系到App的自然下载量&#xff0c;所以&#xff0c;我们应该用科学的办法…

android 调用微信语音识别,Android 仿微信语音识别

参考于&#xff1a;Android模仿微信语音聊天功能&#xff0c;这代码跑起来有问题&#xff0c;自己改动了一下&#xff0c;基本上没什么大问题先贴下效果图1、三个布局文件activity_main.xmldialog_manger.xmlitem_layout2.自定义的类(1)DialogMangerpackage com.nickming.view;…

android sendmessage和post的区别,handler中post和send方式区别

handler中post和send方式的区别1、从用法上send&#xff1a;发送的是Messagepost&#xff1a;发送的是Runnable对象具体用法&#xff0c;如下代码演示&#xff1a;MainActivity.javapublic class MainActivity extends AppCompatActivity {private TextView textView;private s…

耐心新来按部就班

当快速解决问题的思路行不通时&#xff0c;需要停下来&#xff0c;慢慢一步一步分析&#xff0c;找到问题根源&#xff0c;然后解决问题。转载于:https://www.cnblogs.com/Tpf386/p/8579365.html

Java:不朽的对象和对象复活

什么是对象复活&#xff1f; 当没有其他对象引用该对象时&#xff0c;该Java对象可以进行垃圾回收。 当JVM&#xff1a;s垃圾收集器最终将要删除未使用的对象时&#xff0c;将调用该对象的finalize()方法。 但是&#xff0c;如果我们再次使用对象自己的finalize()方法重新创建对…

老罗android oat,入门ART虚拟机(5)——OAT文件

Android安全交流群&#xff1a;478084054先贴老罗的一张图&#xff1a;再摘一段老罗的描述&#xff1a;“作为Android私有的一种ELF文件&#xff0c;OAT文件包含有两个特殊的段oatdata和oatexec&#xff0c;前者包含有用来生成本地机器指令的dex文件内容&#xff0c;后者包含生…

C#给字符串赋予字面值——字符串插入、转义序列的使用

1.占位符、字符串插入 给字符串赋予字面值时&#xff0c;经常遇见在字符串中包含变量的情况&#xff0c;用连接符进行拼接、转换的方式比较麻烦、还容易出错。C#提供了较为便捷的处理方式&#xff0c;即‘占位符’&#xff0c;以及C#6的新功能‘插入字符串’&#xff08;没记错…

Dajngo-Xadmin 修改菜单摆放排序

问题: Xadmin 默认是读取了所有被注册到 xadmin 的模型生成对应的菜单!这个是没问题的 ,但是xadmin又对菜单做了 "通过菜单名称" 排序.英文状态下我们的排列至少是 a-z排列的,但是一到中文就乱了,完全不符合我们的要求. 解决办法: 要到达按照我们 在 django 的 setti…

华为鸿蒙2.0什么核心,鸿蒙系统2.0:安卓最核心部分基本已去除,将带来全新的体验...

早些时候&#xff0c;华为在东莞举办的华为2020华为开发者大会如期召开&#xff0c;在大会上华为正式发布了鸿蒙系统2.0&#xff0c;并称将于明年应用到智能手机上&#xff0c;其中升级了EMUI 11的用户可以优先获得体验鸿蒙系统2.0的资格&#xff0c;瞬间将会议推向高潮。值得一…

fork join框架使用_Java:使用Fork / Join框架的Mergesort

fork join框架使用此项的目的是显示一个Fork / Join RecursiveAction的简单示例&#xff0c;而不是过多地探讨合并排序的可能优化方法&#xff0c;或者比使用Exkutor / Join Pool优于现有的基于Java 6的现有实现&#xff08;如ExecutorService&#xff09;的相对优势。 以下是使…

Java最大公约数和最小公倍数的求法(辗转相除法)

这道题计算了三个数的最小公倍数 1 import java.util.Scanner;2 3 public class D {4 public static int gcd(int a,int b) {5 int max a>b?a:b;6 int min a<b?a:b;7 if(max%min ! 0) {8 return gcd(min,max%min);9 …

html隐藏块元素过度动画,CSS3实现DIV图层隐藏到显示的过渡效果

本文介绍CSS3实现DIV图层隐藏到显示的过渡效果&#xff0c;可用于产品列表的产品简介过渡显示与隐藏等&#xff0c;希望对你有所帮助。CSS3的transition过渡属性对于页面元素的显隐并没有提供过渡方法&#xff01;所以DIV图层从display&#xff1a;none到display&#xff1a;bl…