Download模块 (十八)
RemoteDownload类继承自Download,反应一类比较特殊的Download,
即将资源转存至网盘。
这种情况下不会发生本地的IO交互。
event的触发都是由网盘提供的API负责。
<1>网盘会为本次的转存任务分配一个ID,因此要增加一个Rid。
<2>不可以pause
<3>equals 和 compareTo也会被override
<4>增加了一个资源转存网盘以后网盘生成的url。
<5>Override了public 内部类Status,去掉了FILE_BROKEN,PAUSED,增加了NO_SPACE.
<6>open也被override,因为网盘的资源不可能向本地文件一样直接打开。
<7>因为不会本地存储,那么File对象不需要,不过会需要一个FileName。
相应的,RemoteDownload显然会有一个自己的Manager,并且此manager和一般的DownloadManager基本没有啥相同点<所以没有从一个
base继承,当然了,硬要说相同点还是有>,RemoteDownloadManager除了hold住RemoteDownload的信息外<M>,
还承担了封装网盘API以及和网盘交互的工作<C>.
<1>RemoteDownloadManager自定义了一个网盘登陆成功以后的Listener作为base class<实现了一些用不到的空方法以及一些通用操作>,后面
实际使用的lisenter(获取网盘容量的Listener,获取网盘内容以后的Listener等)都基于此class 作为匿名类使用。
开放了一个HttpApiListener接口供网盘的http api操作结果的监听。
以及一系列的扩展接口专门监听具体某个http api操作的监听。
公开的Listener也可以被类自己作为内部接口使用。
<2>RemoteDownload也有自己对应的DownloadInfo,作为持久化之用,不过因为不涉及到实际下载,因此不需要传到Service去。
持久化与恢复也才采用的是Json打包,保存信息相对简单,只是sourceUrl, 网盘保存路径,以及资源大小。
<3>项目中封装了一个包含了发出http请求和接收的runnable, 本模块则是定制了一个其提供的接口。
<4>转存至网盘这个操作被封装为了一个Runnable扩展对象,代表这一次转存网盘的行为<要转存的对象的RemoteDownload可能是
新建的,也可能是从Pending的RemoteDownload中取得>,取得了有效的RemoteDownload对象,就会开始与网盘交互进行转存网盘。
和网盘的每次交互其实都是一次http post交互<参数已经被整合在url中了,会指定AccessToken,资源url以及转存到网盘的路径>,
使用项目封装好的http runnable,定义一个自己需要http listener,然后将url, method和listener填入到http runnable以后,
就可以开始start http runnable<该类内部在start的时候会new一个thread把自己塞进去然后start>
定制的listner监听了http resp的data到来的事件<网盘给了回复>,会获取返回的resp的data部分
要处理 AccessToken过期的情况,如果已经过期了,尝试重新获取,会将因此失败的RemoteDownloadTask加入到AccessFailPending list中,
在获取了AccessToken以后,再执行。
除了认证出错以外,还会有其他的错误,网盘会返回taskId = -1表示失败,出于鲁棒性和http单独线程回复延迟的考虑,返回-1时要检查一下
这次失败的Task是否还在,如果已经某种原因被删除了,那么显然不需要处理这个错误。然后可以细查errorcode和errorMsg,
网盘一段时间内不能接受太多的转存,这种情况下会将此Task加入到一个专门的ServerBusyPending List中,等待重发。
否则其他的错误都默认不进行重试,直接标记为失败并通知监听fail的listener。
如果成功<注意,这里的成功是网盘开始存取了,但是还需要一个过程才能全部转存完>,但是发现id对应的Task已经无效或者被删除,
那么还要将已经转存的再次通过网盘API删除。否则,可以认为此Task处于InProgress的阶段,在成功提交转存请求的前提下,可以再尝试
一下以前的ServerBusyPendingTask。
除了网盘本身会出错意外,还要考虑http 网络交互出错的情况:
还是检查一下id是否还是有效的,如果无效,直接不管。
如果发现是因为当前没有网络了,那么就将这个失败的Task加入到专门的NetworkFailPending list中,在网络恢复的时候<监听了此事件>
重试,否则如果不是网络无效引起的,那么就直接重试,注意重试次数有一个Limit,该limit在有resp data回来时会重置为0,
每次重试+1,会在主线程<Task里面的http runnable会自己起一个thread>delay一个Task进行重试,否则也会认为其已经失败。
<5>因为有很多的pending Task list,那么要彻底移除某个task的时候就要考虑这些list,从这些list中也要移除task。
<6>在外部调用提供的startRemoteDownload函数的时候,要检查Token的有效,以及网络的有效,失败都会导致被加入到pending list中,
设计时 只有token有效,才会创建一个RemoteDownload与之对应.
<7>刷新Token<通行证>也是通过网盘的http API交互,一样的会注册回调来监听data resp和error,
只要有resp data回来,Token的刷新就成功了,从其中获取新的Token并变更相关的flag,同时可以重试AccessfailedPendingTask了
对于任何异常和error,都视为失败,并且会logout出网盘。
logout会导致所有的信息被重置<没有login,除了login之外所有的操作都没有意义><refreshtoken是否为null会作为是否已经logout的标志
>,App的setting中也会修改相应的setting,
并且会出发logout的event,更新UI和其他的部分。
<8>Manager角色决定了其是一个单例. accessToken和refreshToken都会被持久化,并且是专门的sharedPreference,App的Setting中
会保存是否已经登入网盘,如果登入,构造的时候manager的时候都会将这些token载入,如果有一个没有,那么logout。
<9>因为设计到网络的交互,以及登陆为前提,所以很多对网盘的交互<查询容量,查询已转存的文件...>都被封装为了Runnable<很多会以
listener作为输入以及RemoteDownload>,这样可以被
放入到PendingTask list中以应对网络或登陆失败重试的情况。 这也是项目中对 操作封装 的一种手法。
<10>可以通过网盘分配给某个转存请求资源的Rid和AccessToken结合网盘的http api获得某个转存资源的状况,一样获取前要做Token检测,
获取了http resp data以后会做一些常规检测<这一部分还没有抽离出来成为函数>,在data中可以获取转存任务的状态,完成度。
并相应的设置M的状态,以及出发C的操作。注意在这一步取得网盘API回复的status也会携带可能的错误码,分别是 系统错误/资源不存在/
超时/资源存在但是取不出来,这些错误都不尝试解决,标记Task为fail,如果错误是没有存储空间,那么会设定一个专门的空间不足状态,但
也会认为是失败了,原因是空间不足这种错误用户是有可能解决的,而前面几种用户也无能为力。
还有一种status 是转存被取消,一般是由之前的主动取消引起的,移出runningTask list
,加入到ServerBusyPendingList中。这种情况下,可以重试ServerBusyPending的task。
<11>除了请求某个网盘资源的信息外,还需要一个函数来得到网盘给定目录的全部Task信息<就是网盘正在进行的转存任务>,也是通过http api
只结合AccessToken就可以。
发出请求后在http resp data 到来以后就可以从data中获取当前网盘中的所有转存task<还可以获取网盘为资源分配的id,用于http api>,
其实就是在网盘和本地做了一次task的数据同步<会将SyncedTaskflag设为true>,并且会触发相应的event,告知UI进行刷新显示。
这种同步某种意义上和从本地持久化恢复一样。会为每个同步回来的资源创建RemoteDownload<为了从网盘下载到本地>并加入到正在进行的
Task List中。
<12>获取网盘配额更加简单,也是http api和例行的accessToken检查<这一部分应该抽离成一个单独的类或者函数的>,
同样,获取了信息以后,以listener模式<manager本身只是更新对应的M,其他的操作留给感兴趣的实体C/V>通告出去。
<13>获取网盘资源的url通过给定字符串的拼接附带AccessToken,以及路径<注意要用URLEncoder的encodeUrl (路径 utf-8编码)来得到正确
的编码path>
<14>对于发起http api请求以及设置header和回调的listener,都抽离出来成了一个函数<得益于项目本身对httpRequest的良好封装>
<15>RemoteDownload代表的是网盘存在的资源<可以下载到本地,是一种可下载资源>,而RemoteDownloadTask则代表资源转存这个task,
两者容易混淆,后来进行了重命名以区分两者的截然不同。
<16>还提供了获取网盘某个路径<一般是hardcode的固定路径>下所有文件的信息,这些文件也会反应为RemoteDownload,
这一版没有考虑文件目录<直接pass不显示>,这些构造出来的RemoteDownload会被标记为COMPLETE
<17>删除网盘的文件比较简单,http api指定path即可。
<18>真正处理login的逻辑不在Manager内,而是另外一个类实现<弹出一个fragment>,manager在触发另外一个类的处理时会附带一个自己的
callback,在login有结果的时候会得知并处理。回调会发回authorizationCode和是否取消login的flag,
使用authorizationCode可以通过网盘API<其实不是网盘提供的http api,而是公司自己的一个url,通过对该url的post以及
authorizationCode,公司的Server会替客户端和网盘交互获得token>获得AccessToken和refreshToken,并且会被持久化在专用的
sharedPreference中,因为是异步的获取,还需要维护一个isGettingToken flag以防止重复获取。然后要将APP的setting中的login flag也更新。
最后出发一个网盘login结束<成功>的event。
有任何异常或者Exception都会将Token置为null并且清除所有pending的转存task,触发网盘login结束<失败>的event。
RefreshToken通过API以后,可以在resp的data中获得新的AccessToken, refresh异常或者失败会导致logout
<19>从网盘下载资源到本地和常规的下载到本地没有区别。只是url变成了网盘的。
<20>在每次成功开始一次转存/读取转存task都会schedule一次检查转存task进度的task,而该task本身在查询更新完当前的转存task以后,
如果当前还有在转存的task,那么会以某个delay再schedule自己,在将来的某个时候再次更新progress。
这里每次post前,不会cancel之前的检查task,因为考虑到有delay的原因。
<21>网盘为APP专门配置了中文路径,在code中的路径为了表示中文<String>,直接以\uXXX表示了。
RemoteDownload类继承自Download,反应一类比较特殊的Download,
即将资源转存至网盘。
这种情况下不会发生本地的IO交互。
event的触发都是由网盘提供的API负责。
<1>网盘会为本次的转存任务分配一个ID,因此要增加一个Rid。
<2>不可以pause
<3>equals 和 compareTo也会被override
<4>增加了一个资源转存网盘以后网盘生成的url。
<5>Override了public 内部类Status,去掉了FILE_BROKEN,PAUSED,增加了NO_SPACE.
<6>open也被override,因为网盘的资源不可能向本地文件一样直接打开。
<7>因为不会本地存储,那么File对象不需要,不过会需要一个FileName。
相应的,RemoteDownload显然会有一个自己的Manager,并且此manager和一般的DownloadManager基本没有啥相同点<所以没有从一个
base继承,当然了,硬要说相同点还是有>,RemoteDownloadManager除了hold住RemoteDownload的信息外<M>,
还承担了封装网盘API以及和网盘交互的工作<C>.
<1>RemoteDownloadManager自定义了一个网盘登陆成功以后的Listener作为base class<实现了一些用不到的空方法以及一些通用操作>,后面
实际使用的lisenter(获取网盘容量的Listener,获取网盘内容以后的Listener等)都基于此class 作为匿名类使用。
开放了一个HttpApiListener接口供网盘的http api操作结果的监听。
以及一系列的扩展接口专门监听具体某个http api操作的监听。
公开的Listener也可以被类自己作为内部接口使用。
<2>RemoteDownload也有自己对应的DownloadInfo,作为持久化之用,不过因为不涉及到实际下载,因此不需要传到Service去。
持久化与恢复也才采用的是Json打包,保存信息相对简单,只是sourceUrl, 网盘保存路径,以及资源大小。
<3>项目中封装了一个包含了发出http请求和接收的runnable, 本模块则是定制了一个其提供的接口。
<4>转存至网盘这个操作被封装为了一个Runnable扩展对象,代表这一次转存网盘的行为<要转存的对象的RemoteDownload可能是
新建的,也可能是从Pending的RemoteDownload中取得>,取得了有效的RemoteDownload对象,就会开始与网盘交互进行转存网盘。
和网盘的每次交互其实都是一次http post交互<参数已经被整合在url中了,会指定AccessToken,资源url以及转存到网盘的路径>,
使用项目封装好的http runnable,定义一个自己需要http listener,然后将url, method和listener填入到http runnable以后,
就可以开始start http runnable<该类内部在start的时候会new一个thread把自己塞进去然后start>
定制的listner监听了http resp的data到来的事件<网盘给了回复>,会获取返回的resp的data部分
要处理 AccessToken过期的情况,如果已经过期了,尝试重新获取,会将因此失败的RemoteDownloadTask加入到AccessFailPending list中,
在获取了AccessToken以后,再执行。
除了认证出错以外,还会有其他的错误,网盘会返回taskId = -1表示失败,出于鲁棒性和http单独线程回复延迟的考虑,返回-1时要检查一下
这次失败的Task是否还在,如果已经某种原因被删除了,那么显然不需要处理这个错误。然后可以细查errorcode和errorMsg,
网盘一段时间内不能接受太多的转存,这种情况下会将此Task加入到一个专门的ServerBusyPending List中,等待重发。
否则其他的错误都默认不进行重试,直接标记为失败并通知监听fail的listener。
如果成功<注意,这里的成功是网盘开始存取了,但是还需要一个过程才能全部转存完>,但是发现id对应的Task已经无效或者被删除,
那么还要将已经转存的再次通过网盘API删除。否则,可以认为此Task处于InProgress的阶段,在成功提交转存请求的前提下,可以再尝试
一下以前的ServerBusyPendingTask。
除了网盘本身会出错意外,还要考虑http 网络交互出错的情况:
还是检查一下id是否还是有效的,如果无效,直接不管。
如果发现是因为当前没有网络了,那么就将这个失败的Task加入到专门的NetworkFailPending list中,在网络恢复的时候<监听了此事件>
重试,否则如果不是网络无效引起的,那么就直接重试,注意重试次数有一个Limit,该limit在有resp data回来时会重置为0,
每次重试+1,会在主线程<Task里面的http runnable会自己起一个thread>delay一个Task进行重试,否则也会认为其已经失败。
<5>因为有很多的pending Task list,那么要彻底移除某个task的时候就要考虑这些list,从这些list中也要移除task。
<6>在外部调用提供的startRemoteDownload函数的时候,要检查Token的有效,以及网络的有效,失败都会导致被加入到pending list中,
设计时 只有token有效,才会创建一个RemoteDownload与之对应.
<7>刷新Token<通行证>也是通过网盘的http API交互,一样的会注册回调来监听data resp和error,
只要有resp data回来,Token的刷新就成功了,从其中获取新的Token并变更相关的flag,同时可以重试AccessfailedPendingTask了
对于任何异常和error,都视为失败,并且会logout出网盘。
logout会导致所有的信息被重置<没有login,除了login之外所有的操作都没有意义><refreshtoken是否为null会作为是否已经logout的标志
>,App的setting中也会修改相应的setting,
并且会出发logout的event,更新UI和其他的部分。
<8>Manager角色决定了其是一个单例. accessToken和refreshToken都会被持久化,并且是专门的sharedPreference,App的Setting中
会保存是否已经登入网盘,如果登入,构造的时候manager的时候都会将这些token载入,如果有一个没有,那么logout。
<9>因为设计到网络的交互,以及登陆为前提,所以很多对网盘的交互<查询容量,查询已转存的文件...>都被封装为了Runnable<很多会以
listener作为输入以及RemoteDownload>,这样可以被
放入到PendingTask list中以应对网络或登陆失败重试的情况。 这也是项目中对 操作封装 的一种手法。
<10>可以通过网盘分配给某个转存请求资源的Rid和AccessToken结合网盘的http api获得某个转存资源的状况,一样获取前要做Token检测,
获取了http resp data以后会做一些常规检测<这一部分还没有抽离出来成为函数>,在data中可以获取转存任务的状态,完成度。
并相应的设置M的状态,以及出发C的操作。注意在这一步取得网盘API回复的status也会携带可能的错误码,分别是 系统错误/资源不存在/
超时/资源存在但是取不出来,这些错误都不尝试解决,标记Task为fail,如果错误是没有存储空间,那么会设定一个专门的空间不足状态,但
也会认为是失败了,原因是空间不足这种错误用户是有可能解决的,而前面几种用户也无能为力。
还有一种status 是转存被取消,一般是由之前的主动取消引起的,移出runningTask list
,加入到ServerBusyPendingList中。这种情况下,可以重试ServerBusyPending的task。
<11>除了请求某个网盘资源的信息外,还需要一个函数来得到网盘给定目录的全部Task信息<就是网盘正在进行的转存任务>,也是通过http api
只结合AccessToken就可以。
发出请求后在http resp data 到来以后就可以从data中获取当前网盘中的所有转存task<还可以获取网盘为资源分配的id,用于http api>,
其实就是在网盘和本地做了一次task的数据同步<会将SyncedTaskflag设为true>,并且会触发相应的event,告知UI进行刷新显示。
这种同步某种意义上和从本地持久化恢复一样。会为每个同步回来的资源创建RemoteDownload<为了从网盘下载到本地>并加入到正在进行的
Task List中。
<12>获取网盘配额更加简单,也是http api和例行的accessToken检查<这一部分应该抽离成一个单独的类或者函数的>,
同样,获取了信息以后,以listener模式<manager本身只是更新对应的M,其他的操作留给感兴趣的实体C/V>通告出去。
<13>获取网盘资源的url通过给定字符串的拼接附带AccessToken,以及路径<注意要用URLEncoder的encodeUrl (路径 utf-8编码)来得到正确
的编码path>
<14>对于发起http api请求以及设置header和回调的listener,都抽离出来成了一个函数<得益于项目本身对httpRequest的良好封装>
<15>RemoteDownload代表的是网盘存在的资源<可以下载到本地,是一种可下载资源>,而RemoteDownloadTask则代表资源转存这个task,
两者容易混淆,后来进行了重命名以区分两者的截然不同。
<16>还提供了获取网盘某个路径<一般是hardcode的固定路径>下所有文件的信息,这些文件也会反应为RemoteDownload,
这一版没有考虑文件目录<直接pass不显示>,这些构造出来的RemoteDownload会被标记为COMPLETE
<17>删除网盘的文件比较简单,http api指定path即可。
<18>真正处理login的逻辑不在Manager内,而是另外一个类实现<弹出一个fragment>,manager在触发另外一个类的处理时会附带一个自己的
callback,在login有结果的时候会得知并处理。回调会发回authorizationCode和是否取消login的flag,
使用authorizationCode可以通过网盘API<其实不是网盘提供的http api,而是公司自己的一个url,通过对该url的post以及
authorizationCode,公司的Server会替客户端和网盘交互获得token>获得AccessToken和refreshToken,并且会被持久化在专用的
sharedPreference中,因为是异步的获取,还需要维护一个isGettingToken flag以防止重复获取。然后要将APP的setting中的login flag也更新。
最后出发一个网盘login结束<成功>的event。
有任何异常或者Exception都会将Token置为null并且清除所有pending的转存task,触发网盘login结束<失败>的event。
RefreshToken通过API以后,可以在resp的data中获得新的AccessToken, refresh异常或者失败会导致logout
<19>从网盘下载资源到本地和常规的下载到本地没有区别。只是url变成了网盘的。
<20>在每次成功开始一次转存/读取转存task都会schedule一次检查转存task进度的task,而该task本身在查询更新完当前的转存task以后,
如果当前还有在转存的task,那么会以某个delay再schedule自己,在将来的某个时候再次更新progress。
这里每次post前,不会cancel之前的检查task,因为考虑到有delay的原因。
<21>网盘为APP专门配置了中文路径,在code中的路径为了表示中文<String>,直接以\uXXX表示了。