Retrofit2链式调用及文件下载


Retrofit2+Rxjava 是当下较为流行的网络请求方式,这里将其结合进行网络请求的链式调用以及文件下载,来解决项目中开屏广告页的设计。

目前,很多APP中启动都有开屏广告,其中广告有静态图片、Gif图片或者短视频等,通常为了更好的用户体验,很多都是采用预下载的形式,即本此下载,下次打开生效。

一、开屏广告的设计

近期维护的一个项目中,刚好也有开屏广告,其做法如下图所示,打开app首先进入主页,获取广告路径,然后跳转广告页显示,结束后再跳转回主页。这种做法,可以保证每次打开都是最新的广告,但在显示广告页前会有短暂的几秒显示首页,体验不好。

经过改造,采用预加载的方式,大致流程如下图所示,在首页中获取图片路径并下载到本地,下次打开时显示。

通过上面的方式可以让广告也显示的更加自然,提高用户体验。

二、Retrofit2 文件下载

这里,为了开屏广告显示的更加流畅,节省流量,广告图片下载到本地。这里结合Rxjava 和 Rxandroid进行使用,如下代码:

public class DownloadPicAPI {
    public interface DownloadPicService {
        @Streaming
        @GET
        Observable<ResponseBody> downloadPic(@Url String fileUrl);
    }
    public static Observable<ResponseBody> downloadPic(Context context, String fileUrl) {
        DownloadPicService service = RestHelper.getBaseRetrofit(context)
        .create(DownloadPicService.class);
        return service.downloadPic(fileUrl);
    }
}

其中 RestHelper 是对Retrofit和Rxjava进行封装的工具类,避免文件过大下载出错,添加 @Streaming 注解,请求完成将返回的 ResponseBody 保存到文件中。如下代码:

/**
 * 保存到本地
 *
 * @param body     
 * @param dir      目录
 * @param fileName 文件名
 * @return
 */
private boolean writeResponseBodyToDisk(ResponseBody body, String dir, String fileName) {
    try {
        File dirFile = new File(dir);
        if (!dirFile.exists()) {
            dirFile.mkdirs();
        }
        File saveFile = new File(dir + "/" + fileName);
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            byte[] fileReader = new byte[4096];
            inputStream = body.byteStream();
            outputStream = new FileOutputStream(saveFile);
            while (true) {
                int read = inputStream.read(fileReader);
                if (read == -1) {
                    break;
                }
                outputStream.write(fileReader, 0, read);
            }
            outputStream.flush();
            return true;
        } catch (IOException e) {
            return false;
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
        }
    } catch (IOException e) {
        return false;
    }
}

三、flatMap组合网络请求

这里,首先要获取广告信息,然后需要下载图片,两次网络请求,会出现嵌套调用,但是 Rxjava 的出现,可以让程序链式的调用,采用 flatMap操作符,如下代码:

 SplashAdAPI.requestSplashAd (getActivity() )
.flatMap (new Func1 < SplashAdModel, Observable<? >> ()
{
    @Override
    public Observable<ResponseBody> call (SplashAdModel splashAdModel)
    {
        if (splashAdModel.bizSucc)
        {

            String fileName = getActivity().getCacheDir().getAbsolutePath()
                              + "/splash_ad_pic.png";
            File file = new File (fileName);
            if (file.exists() )
            {
                /*存在缓存图片*/
                if (splashAdModel.url.equals (UserInfoHelper
                                              .getSplashAdUrl (getActivity() ) ) )
                {
                    /*无新url,则不下载图片*/
                    return null;
                }
                else
                {
                    /*有新url,删除缓存文件,更新本地url,下载图片*/
                    file.delete();
                    UserInfoHelper.saveSplashAdUrl (getActivity(), splashAdModel.url);
                    return DownloadPicAPI.downloadPic (getActivity(), splashAdModel.url);
                }
            }
            else
            {
                /*不存在缓存问价则下载*/
                UserInfoHelper.saveSplashAdUrl (getActivity(), splashAdModel.url);
                return DownloadPicAPI.downloadPic (getActivity(), splashAdModel.url);
            }
        }
        return null;
    }
})
.subscribeOn (Schedulers.io() )
.observeOn (AndroidSchedulers.mainThread() )
.subscribe (new Observer<Object>()
{
    @Override
    public void onCompleted() { }
    @Override
    public void onError (Throwable e)
    {
        Log.e ("imtianx", "onError: ----------", e);
    }
    @Override
    public void onNext (Object responseBody)
    {
        /*写入文件*/
        writeResponseBodyToDisk ( (ResponseBody) responseBody,
                                  getActivity().getCacheDir().getAbsolutePath(),
                                  "splash_ad_pic.png");
    }
});

上述代码中,SplashAdAPI 是获取图片url接口,DownloadPicAPI 是步骤二中的下载文件的接口。

使用 getCacheDir() 而非 getExternalCacheDir() 可以避免SD卡权限的问题和部分手机无外部存储而出现的空指针异常。或者,判断SD卡是否可用来选择存放位置。此外,将文件放在该缓存目录下,便于app卸载后清除数据。

使用 flatMap 操作符,轻松的将两次请求链接。这种开屏广告的设计仅是自己的看法,有更好做法的欢迎留言,交流。


文章作者: imtianx
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明来源 imtianx !
评论
 上一篇
Git 子模块:git submodule Git 子模块:git submodule
工作中,可能会遇到在一个Git仓库 中添加 其他 Git 仓库的场景。比如,在项目中引用第三方库。或者在模块化开发中,某些公共的模块是需要单独维护的,使用单独的仓库比较方便,但是在项目中需要引用,就会出现这样的场景。这里使用 Git 的 g
下一篇 
Tomcat 配置https证书 Tomcat 配置https证书
HTTPS 是安全套接字层超文本传输协议,在http 的基础上加入了 SSL协议,需要使用证书来校验身份。 HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。其默认端口为:443。越来越多的
2017-09-22
  目录