SDWebImage 源码学习笔记·前传 ☞ 升级 4.0/5.0 后适配 WebP/GIF

SDWebImage-源码学习笔记.png

2019.12.27 更新:最近发现 SDWebImage 升级到 5.0 以后加载 WebP 和 Gif 的方式也发生了改变,详见文末。

一、前言

前段时间对项目中使用的 SDWebImage 进行了一次升级 (3.7.6 → 4.4.2),升级过程中遇到了一些问题,于是又把源码读了一遍,为了记录读码过程,就有了这个系列:

二、升级

现在转回到本篇的主要内容,首先当然是升级了,不过最新版的 SDWebImage 里边分了几个子 pod ,默认下载不全。为了满足我们的日常使用要求,还需要加上 WebP 和 GIF 这两个子 pod:

1
2
3
4
5
platform :ios, '7.0'

pod 'SDWebImage', '~> 4.0'
pod 'SDWebImage/WebP'
pod 'SDWebImage/GIF'

不过,在执行 pod install 的时候,发现 pod 'SDWebImage/WebP' 卡在了 Installing libwebp (0.6.0) 的地方。

据说 WebP 的解析库是 Google 的开源库,所以需要使用 VPN。笔者使用了蓝灯 Lantern,然后发现 浏览器可以正常访问 Google 网站,可是命令行 pod install 依然会卡住,可以通过下边两条命令检测命令行是否已添加 VPN:

1
curl www.google.com

执行上述命令后,如果返回了网页信息,就说明 VPN 已经添加成功。

于是猛查资料,才有了下文:

  1. 下载 Lantern

  2. 安装后,打开 Lantern,页面如下,查询自己 VPN 软件 (Lantern) 的http代理:

Lantern端口号.png

可以看出 Lantern 的 http 代理运行在 52425(选中部分) 端口,不同机器上的端口号也会不同。
为什么说是 http 而不是 https 呢?将地址栏中的域名复制出来贴到任意文本编辑器,就会发现变成了 http://localhost:52425/axxx

3.在命令行输入以下指令,查看端口使用情况:

1
sudo lsof -i -P

查看端口使用情况.png

可以看出来基本使用的只有 2 个代理端口,52425 和 52427,结合第一步可以得出,52425 是 http 的代理端口,52427 是 https 的代理端口 😎

4.配置命令行代理端口,有两种方式:
A、给本地 git 设置 http/https 代理

1
2
3
4
5
6
7
8
9
// 添加 http/https 代理的指令 https://127.0.0.1:XXXX  (XXXX代表端口号)
git config --global http.proxy https://127.0.0.1:52425
git config --global https.proxy https://127.0.0.1:52427

// 取消 http/https 代理的指令
git config --global --unset http.proxy
git config --global --unset https.proxy

// 这种方法可以 pod install 成功,不过 curl www.google.com 不成功,即打不出 Google 的网页信息。

B、设置系统全局变量 http/https 代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
切换到etc目录下     cd /etc            

以管理员身份编辑   sudo vim bashrc 

输入密码进入后,  G $ 定位到文件末尾 按下键盘字母 i 开始输入,在最下面位置粘贴下面两句

export http_proxy=127.0.0.1:52425
export https_proxy=127.0.0.1:52427 

然后退出保存  按Esc  :wq

接下来让修改文件生效  source bashrc

最后一步 验证成功:  curl www.google.com  如果收到 google 的 html 数据代表已通。

完成后 pod 内部的结构如下:

SDWebImage4.0.png

三、使用

为叙述方便,此处将图片的 URL 定义成了宏:

1
2
3
4
#define URL_Normal          [NSURL URLWithString:@"此处是 普通静态图 地址"]
#define URL_WebP_Normal [NSURL URLWithString:@"此处是 静态 WebP 地址"]
#define URL_WebP_Dynamic [NSURL URLWithString:@"此处是 动态 WebP 地址"]
#define URL_GIF [NSURL URLWithString:@"此处是 GIF 地址"]

第一种方案

  • png/jpg,直接使用 SDWebImage 提供的普通方法即可。
  • WebP,只要导入了 WebP 的子 pod,直接使用普通方法即可正常加载静态、动态的 WebP。
  • GIF,此处使用了 FLAnimatedImageView 代替 UIImageView 来展示 GIF。因为 SDWebImage4.0 以后,如果继续 直接 使用 UIImageView 将只展示 GIF 的第一帧,可以使用推荐的 FLAnimatedImageView 替换 UIImageView。
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
    // 普通静态图
UIImageView *imgV = [[UIImageView alloc] initWithFrame:CGRectMake(125, 70, 160, 160)];
imgV.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:imgV];
[imgV sd_setImageWithURL:URL_Normal];

// WebP
UIImageView *imgVB = [[UIImageView alloc] initWithFrame:CGRectMake(125, 70+160+10, 160, 160)];
imgVB.backgroundColor = [UIColor lightGrayColor];
imgVB.contentMode = UIViewContentModeScaleAspectFill;
[self.view addSubview:imgVB];
// WebP 静态图
// [imgVB sd_setImageWithURL:URL_WebP_Normal];
// WebP 动态图
[imgVB sd_setImageWithURL:URL_WebP_Dynamic];

// GIF
FLAnimatedImageView *imgView = [[FLAnimatedImageView alloc] initWithFrame:CGRectMake(125, 70+160+10+160+10, 160, 200)];
imgView.backgroundColor = [UIColor lightGrayColor];
imgView.contentMode = UIViewContentModeScaleAspectFill;
[self.view addSubview:imgView];
[imgView sd_setShowActivityIndicatorView:YES];
[imgView sd_setImageWithURL:URL_GIF
placeholderImage:[UIImage imageNamed:@"placeholder"]
options:1];

第二种方案

统一都使用 UIImageView 展示图片,不过和上边的第一种方案有以下不同:

  • 使用 SDWebImageManagerloadImageWithURL: options: progress: completed: 方法下载图片;
  • 根据得到的 data 判断图片类型: SDImageFormat format = [NSData sd_imageFormatForImageData:data];,基本原理是根据 data 的第一个字节来判断;
  • WebP 使用专用方法 sd_imageWithWebPData: 解码;
  • GIF 也使用专用方法 sd_animatedGIFWithData: 解码。
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
UIImageView *gifView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 70+160+10+160+10+200+10, kScreenW-20, 200)];
gifView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:gifView];
self.imgView = gifView;

SDWebImageManager *mgr = [SDWebImageManager sharedManager];
__weak typeof(self) weakSelf = self;
[mgr loadImageWithURL:URL_GIF // URL_WebP_Dynamic // URL_WebP_Normal // URL_Normal
options:1
progress:nil
completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL)
{
SDImageFormat format = [NSData sd_imageFormatForImageData:data];
switch (format) {
case SDImageFormatGIF:
weakSelf.imgView.image = [UIImage sd_animatedGIFWithData:data];
break;
case SDImageFormatWebP:
weakSelf.imgView.image = [UIImage sd_imageWithWebPData:data];
break;
case SDImageFormatPNG:
case SDImageFormatJPEG:
weakSelf.imgView.image = image;
break;
default:
break;
}
}];

第三种方案

自定义 HHImageView,在它的 init 系列方法中添加了对 GIF 的解析:

1
[[SDWebImageCodersManager sharedInstance] addCoder:[SDWebImageGIFCoder sharedCoder]];

这种方案对最终的调用方来说,非常简洁,不需要对各种图片区别对待。

1
2
3
4
5
6
7
8
9
10
11
12
    HHImageView *customView = [[HHImageView alloc] initWithFrame:CGRectMake(125, 70, 160, 160)];
customView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:customView];
[customView sd_setShowActivityIndicatorView:YES];
// 静态图
// [customView sd_setImageWithURL:URL_Normal];
// WebP 动图
// [customView sd_setImageWithURL:URL_WebP_Normal];
// WebP 静态图
// [customView sd_setImageWithURL:URL_WebP_Dynamic];
// GIF
[customView sd_setImageWithURL:URL_GIF];

# 2019.12.27 更新:

  • 5.0 之后的版本中,默认添加了对 Gif 的支持,也就是说,需要移除 podfile 中的 pod 'SDWebImage/GIF'(不移除的话,pod install/update 会报错),然后直接使用加载 png/jpg 的方法就可以加载 Gif 图了,比如:
    1
    2
    > imageViewA.sd_setImage(with: URL(string: "http://hbimg.b0.upaiyun.com/43efd35d1e9cadc6d8ff5cdc5faccec06f1082bb4efc4-o8K27E_fw658"), completed: nil) // 皮卡丘揉脸的动画^_^
    >
  • 对于 WebP,将之前的 subspec 抽出来,创建了一个单独的库 SDImageWebPCoder,使用时需要两步操作:
    • 在 podfile 文件中用下边的内容替换之前的 pod 'SDWebImage/GIF'
      1
      2
      >   pod 'SDImageWebPCoder'
      >
  • 使用前,先给 SDImageCodersManager 添加一个 coder:WebPCoder,如下:
    1
    2
    3
    >   let WebPCoder = SDImageWebPCoder.shared
    > SDImageCodersManager.shared.addCoder(WebPCoder)
    >
  • 最后,直接使用通用的方法加载 WebP,示例代码如下:
    1
    2
    3
    4
    5
    >   // 静态图
    > imageViewA.sd_setImage(with: URL(string: "https://www.gstatic.com/webp/gallery/2.webp"), completed: nil)
    > // 动图
    > imageViewB.sd_setImage(with: URL(string: "http://littlesvr.ca/apng/images/world-cup-2014-42.webp"), completed: nil)
    >

本文完整示例代码见: HHSDWebImageStudy

# 参考

0%