- 需求:项目中新增了类似微信朋友圈的功能,我的任务是做send moment(发送动态)功能;
- 功能介绍:用户可以发布纯文本、图片、视频、位置信息,发送的动态可以仅对部分好友可见或者公开状态,还可以控制发布的动态是否显示到我们公司的地图上;
- 功能要求:用户点击发送时,先判断是否符合发送的app制定的发送规则,若符合时不管有无网络,页面直接pop回上一个界面,不让用户在发送时等待发送的结果,而是让send moment在后台来执行,当后台发送成功时在屏幕提示下,并刷新界面,以提升用户体验!
- 功能分析:
由于时间比较紧,要求在一天内完成,这里我使用了sqlite缓存发送moment的参数及图片或视频资源文件;
新建一个Moment类,添加各种需要的属性,还需要额外添加一个sendState属性(可以为枚举),定义不同的发送状态;
创建数据库及表,将Moment的每个属性添加为表中的字段,这样表中的一行就是一个Moment对象;
后台发送其实是通过一个单例对象,当用户点击发送时,先将send moment时需要的参数及资源文件缓存到数据库中,发送到服务器时直接从数据库中取出所有待发送的moment,取出的应该为数组,直接遍历此数组进行发送,发送过程中根据实际情况标记发送状态,并更新数据库,比如无网络时需要将sendState设置为等待发送状态再保存到数据库,方便以后网络状态发生改变时,继续发送; - 实现:
分别添加三个类:Moment(实体类)、MomentDao(对数据库增删改查)、SendMomentModule(处理业务逻辑的)
操作数据库底层使用的是FMDB;
2017年3月20日对离线发送动态的优化:
优化一:若发送的有图片和视频资源,则将其先存入沙盒中,再拿到沙盒中资源文件的路径,将其路径缓存到数据库;
优化二:创建两个NSOperation,第一个封装数据查询需要发送的动态,第二个封装发送动态到服务器,并让这个两个操作之间产生依赖关系,第二个依赖第一个,当查询完成后才能执行发送的操作,且查询结果为0时取消所有操作;
优化三:点击发送时若网络符合发送则立即发送不做缓存,缩短发送时间;
当用户点击立即发送moment时:
1.判断网络是否正常、用户是否登录,若符合立即发送就直接发送到服务器,不做缓存,当发送失败时,先将图片缓存到沙盒,拿到沙盒的路径再将此条需要发送的moment缓存到数据库中,标记为发送失败
2.若当前网络不符合发送moment,则提示用户,并将此条需要发送的moment缓存图片视频到沙盒及数据库,标记为发送失败;
3.当网络正常时,从数据库取出所有标记失败待发送moment存在数组中,然后按照创建日期排序,最早的日期优先发送;
3月21日发现的问题:
- 离线发送时,若iPhone重启了,则不能发送成功;通过log发现通过数据库中的视频和图片沙盒路径获取的数据为nil,才知道获取时出现了问题;
原因:离线缓存图片和视频到沙盒后,把视频和图片拿到路径报错到数据库中的错误问题,不能存储完整的沙盒路径,因为沙盒/var/mobile/Containers/Data/Application后面的/FEB39C8C-26CA-4DD9-B135-4FC2BB3F7B94会发送改变的,所以在存入数据库的图片和视频沙盒路径时只存入图片视频的名称,取的时候再拼接完整路径即可
3月22日发现的问题,已解决:
3月22日发现iPhone离线发送moment,连无网络的提示信息都没有,通过log发现发送的有图片时,在缓存图片时,以下方法回调的image为nil导致的
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:option resultHandler:^(UIImage * _Nullable image, NSDictionary * _Nullable info)
原因:经过多次测试,iPhone拍摄的照片PHImageManager可以转换为UIImage成功,若是网络上保存的图片在相册中则有些会获取失败,下面是我设置的info信息:
PHImageRequestOptions *option = [[PHImageRequestOptions alloc]init];
option.networkAccessAllowed = YES;
option.synchronous = YES;
option.resizeMode = PHImageRequestOptionsResizeModeExact;
option.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat;
解决方法:
第一种:在从相册获取照片的时候就转换为UIImage,缓存的时候也不必转换了,但是项目里该一处地方,好多地方都要改,所以我使用下面这种方法;
第二种方法:其实问题出在了PHImageRequestOptions的option.resizeMode设置这里,设置为````option.resizeMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;```就解决了
总结:用到多线程的技术是非常多的,有NSOperation和GCD的组队列,还有就是逻辑性比较强,最好先画个图,脑子里清晰了再写代码,这种实现并不是一下就能完成的,根据测试反馈的问题或者产品的需求不断的完善吧;
注意:使用这个一定要注意是否有嵌套主线程,造成线程阻塞,且用 AFNetworking时,由于AF内部是同步的,建议你用group,不要用dispatch_semaphore,并使用notofy的函数,防止主线程中同步执行造成死锁;