转载出处:
http://blog.csdn.net/lmj623565791/article/details/53240600;
本文出自:【张鸿洋的博客】
一、概述
最近项目准备尝试使用webp来缩小包的体积,于是抽空对相关知识进行了调研和学习。
至于什么是webp,使用webp有什么好处我就不赘述了,具体可以参考腾讯isux上的这篇文章WebP 探寻之路,大致了解下就ok了。
入手大致需要考虑以下几个问题:
- 如何将现有的jpeg/png等图转化为webp?
- webp格式的图片如何使用?
- 有没有兼容性的问题?
下面就跟着上面3个问题开始进行。
二、jpeg/png到webp的互转
这个官方提供了相互转化的工具,以及具体的使用方式,可以参考:
截个图,可以看到左侧的功能列表,包含一系列的功能,encode、decode、view等…
因为有比较详细的文档,这里简单介绍下:
首先下载工具:
我这里下载的是对应mac os的libwebp-0.4.1-mac-10.8-2.tar.gz
下载完成后解压,然后进入bin目录:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
大致有4个命令工具,分别用于png等转换为webp;webp转化为png;Git转化为webp;查看webp图片;最后一个是用于创建webp动画文件的。
(1) jpeg、png 转为webp [cwebp]
- 1
- 1
(2) webp转为jpeg、png [dwebp]
- 1
- 1
(3) gif 转化为webp
- 1
- 1
每个命令都有一堆options,可以自己研究下
三、使用
Webp在app中一般可以用于两个方面
- 一个是对与服务端交互过程中使用webp图片
- 另一个是应用中的资源文件
(1)与服务端交互使用webp图片
这种方式非常简单,因为从Android4.0开始已经对webp图片进行的支持。
下面我们写个例子,这里我准备了一个webp的图片,我直接放到assets目录,然后编写如下代码:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
找了台4.0.4(API15)的三星手机(ps:实在是找不到4.0的手机了),运行感觉还不错哟~
正在窃喜的时候,我又换了张图片,因为有些时候我们的图部分区域是透明了,于是我找了张图片,转化为webp,按照上述的代码,同样的操作,运行完成后,发现,整个图都显示不出来了。
赶紧找了个4.2.2(API17)的手机,显示正常。
于是看一眼文档:
文档上对webp decode和encode的支持,是这样写的:
- 1
- 2
- 3
- 1
- 2
- 3
https://developer.android.com/guide/appendix/media-formats.html
那么结合文档和实验,大致可以有如下的结论:
- 4.2.1+ 对于webp的decode、encode是完全支持的(包含半透明的webp图)
- 对于4.0+ 到 4.2.1 ,只支持完全不透明的decode、encode的webp图
- 4.0 以下,应该是默认不支持webp了
看到这个结论,那么就是大家的产品最低的支持版本了。
4.2.1起步的话,目前来看,我是不能接受的,所以只有引入so来做低版本兼容了。
(2)兼容so的获取
好在官方已经提供了相关webp支持的源码了,点击下载:
如果你的ndk的知识足够的话,可以自己利用源码,去生成so文件使用。
当然了,你也可以使用前人已经封装好的库:
我们这里选择使用第二个库,这里选择copy它生成的so文件以及辅助类到项目中,你也可以根据其readme打包一个aar出来使用。
首先下载下来webp-android,然后切换到webp-Android/src/main/jni
,执行ndk-build
然后等待执行结束,可以在其/webp-android/src/main/libs
目录下copy出你需要的so,如果需要其他cpu架构的so,可以自己修改Application.mk文件。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
然后将其WebDecoder的辅助类copy到项目中即可,注意保持原有包名。
ok,然后就可以用它提供的decode的方法了:
- 1
- 1
于是,上述以InputStream为webp图片源的代码可以改写为:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
ok,这样就可以对4.2.1以下的webp图片进行decode了。
服务端下发的图片为webp格式,然后app去decode显示即可。
注:webp-android这个库只提供了decode方法,如果需要encode需要自己去添加;建议有时间,看下源码中提供的方法,自己利用源码结合ndk相关知识自己做so文件的生成.
(3)应用中的资源文件
除了上述去加载外部图片的方式以外,还有个使用场景就是将项目中的资源文件直接替换为webp。
简单的使用:
直接将png转化为webp,放到res/drawable目录,我们看看效果
这样就可以了~~
从目前来看有2个选择:
- 仅替换不存在局部透明的图片,如果项目最小版本是4.0,可以不引入so直接使用。
- 全部替换(需要引入so的支持)
第一种,目前来看没什么好介绍的,换图即可。
主要看第二种的处理了,webp-android提供了一种做法是这样的:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
这样就可以happy的使用webp了。
但是我一点都不happy,使用webp很多都是已经存在的项目,让我去使用自定义类还要加属性,多麻烦,万一发现坑,我还得一个一个换回去,坚决不干。
所以我们需要一种,可以无缝切换的方式,基本不费力也能还原。
最无缝的方式,就是不动原本的布局文件了,那么如何去动态修改ImageView使其支持Webp呢(4.-)?
其实我们的SDK也有类似的做法,比如对很多View支持了tint属性,原本是不支持的,忽然就支持了,怎么做到的呢?
就是在根据布局文件中ImageView标签名称,创建的时候去做了一些手脚,如果你一脸懵逼,可以先看Android 探究 LayoutInflater setFactory。
实际上就是利用LayoutInflaterFactory
了,有了方案,那么代码就好写了:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
一般我们的项目中的Activity都存在一个基类,那么直接在其中添加上述代码即可。
大致逻辑为:对于4.2以下的版本,我们设置一个LayoutInflaterFactory
,当创建ImageView的时候,因为AppCompatActivity,ImageView的创建是由上述代码中的delegate指向的对象完成的,我们通过传入attrs,取出用户声明的src属性,经过一系列操作转化为bitmap,最好设置到创建好的ImageView上。
这样,剩下的我们直接将图换成webp就好了,如果发现不适合,只需要去掉这个factory设置的代码即可。
正在我窃喜的时候,忽然发现了一个问题。
就是假设我的资源文件更换并不彻底,还存在部分png的图,但是png的图在4.2以下的版本是不需要上述操作的。
- 那么问题来了,如何区分webp和非webp的图片资源呢?
当然是根据后缀,那么我们现在能获取的仅仅是图片的resId,还能拿到文件完整的名称吗?
让人开心的是,可以拿到的。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
当然应该也可以通过图片的header信息来判断,header判断这种方式应该会更加精确,具体可以查找下相关代码。
对了,如果你的基类是FragmentActivity,那就不需要去设置什么LayoutFactory了,直接复写其onCreateView方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
ok,到此应该对于webp都有了一定的认识,也应该大致了解了在Android使用webp的兼容性的问题,以及如何处理。
文章中还有很多细节的地方没有去处理,后面要踩得坑还有很多,后续还会有一篇博客来写踩到的坑。
如果你也想用webp,欢迎踩坑与交流。
欢迎关注我的微博:
http://weibo.com/u/3165018720
群号: 497438697 ,欢迎入群
微信公众号:hongyangAndroid
(欢迎关注,不要错过每一篇干货,支持投稿)
参考
- https://storage.googleapis.com/downloads.webmproject.org/releases/webp/index.html
- https://developers.google.com/speed/webp/docs/api
- https://groups.google.com/a/webmproject.org/forum/#!forum/webp-discuss
- http://isux.tencent.com/introduction-of-webp.html
- http://stackoverflow.com/questions/9403321/android-how-to-retrieve-file-name-and-extension-of-a-resource-by-resource-id/22063704#22063704
- https://developer.android.com/guide/appendix/media-formats.html
- https://github.com/alexey-pelykh/webp-android-backport
- http://hahack.com/wiki/sundries-webp.html#android-开发中使用-webp