美文网首页移动潮流技术Android Rx系列
Novate:Retrofit2.0和RxJava的又一次完美改

Novate:Retrofit2.0和RxJava的又一次完美改

作者: Tamic | 来源:发表于2016-08-12 21:09 被阅读14695次

    作者/Tamic
    http://www.jianshu.com/p/d7734390895e

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

    背景

    Novate

    用过RxJava和Retrofit的朋友,用久了就会发现Retrofit说难不难,说易亦不易,对于实际项目中,单纯的运用Retrofit做网络请求库,开发起来还是有很多不便,诸如必须要对请求头和参数处理,API接口数目众多的情况下处理起来也不便, 还有Https证书验签,cookie持久,错误结果码处理,统一操作加载过渡UI等也存在不便,因此我对Retrofit再次进行了封装,一直关注我的朋友以前看我封装的《 基于Retrofit2.0 封装的超好用的RetrofitClient工具类》的一文,已对Retrofit结合RxJava时遇到上面的问题进行了完整封装,很多场景还未做到全面,也不是和Retrofit源码一样的Builder模式,因此感觉还是不太完美,特此我进行了长达两个月Novate 框架的开发,给旧的HttpClent迁移到Retrofit2.0也是带来了福音(不需要开发者掌握RxJava和Retrofit)。

    本框架从6月问世以来,无论对入门Rxjava还是实战retrofit的朋友,或多或少的带来的参考价值,无论你是copy源码还是采用maven形式。所以让我有动力进行下一版本(2.X)的强化!

    为何起名为Novate?**

    Novate的英文原意是用新事物代替 ,我开发目的是用新的东西来代替Retrofit结合Rxjava共同开发时的有些不易操作的地方,因此起名新奇的东西,所以结合了原来的HttpClient用法习惯,又加入了Retrofit和RxJava的特性,因此起名 :Novate

    进行下文前请先了解Retrofit和Rxjava,未阅读的请移步:

    系列导读

    介绍

    Novate的改进:

    • 优化设计:加入基础API,减少Api冗余
    • 强大的缓存模式: 支持离线缓存, 无网络智能加载缓存,可配置是否需要缓存.
    • cookie管理:自带cookie管理机制.
    • 全方位请求模式:支持多种方式访问网络(get, put, post ,delete).
    • 轻送调用:支持表单,图文一起,json上传。
    • 文件传输:支持文件下载和上传,并支持进度。
    • 动态添加:支持请求头和参数统一添加,分别添加。
    • 结果处理:支持对返回结果的统一处理。
    • 扩展性强:支持自定义的扩展API,默认Api无法满足时可自定义自己的Service
    • 悠雅方便:支持统一请求访问网络的流程控制,以方便帮你完美加入Processbar进度。
    • RxJava结合: 结合RxJava,线程智能控制.
    • 兼容retrofitAPI,兼容okhttp API
    • 强大的泛型支持,自动解析各种格式复杂数据。

    API

    依赖Gradle:

    • root:
         repositories {
            maven { url "https://jitpack.io" }
            jcenter()
        }
    
    • app:
         dependencies {
          .....
            compile 'com.tamic.novate:novate:1.5.3.2'
         }
    

    基本构建:

     Novate novate = new Novate.Builder(this)
                .baseUrl(baseUrl)
                .build();
    

    除了基本的构建还提供更了其他API

     构建你的header头和参数
     Map<String, String> headers = new HashMap<>();
      headers.put("apikey", "4545sdsddfd7sds");
    
     Map<String, String> parameters = new HashMap<>();
      parameters.put("uid", "878787878sdsd");
    

    实例化:

      Novate novate = new Novate.Builder(this)
                .addParameters(parameters)
                .connectTimeout(8)
                .baseUrl("you api url")
                .addHeader(headers)
                .addLog(true)
                .build(); 
    

    更多:

    novate = new Novate.Builder(this)
                    .addHeader(headers) //添加公共请求头
                    .addParameters(parameters)//公共参数
                    .connectTimeout(10)  //连接时间 可以忽略
                    .readTimeout(10)  //读取 可以忽略
                    .addCookie(false)  //是否同步cooike 默认不同步
                    .addCache(true)  //是否缓存 默认缓存
                    .addCache(cache, cacheTime)   //自定义缓存
                    .baseUrl("Url") //base URL
                    .addLog(true) //是否开启log
                    .cookieManager(new NovateCookieManager()) // 自定义cooike,可以忽略
                    .addInterceptor() // 自定义Interceptor
                    .addNetworkInterceptor() // 自定义NetworkInterceptor
                    .proxy(proxy) // 设置代理
                    .client(client)  //clent 默认不需要
                    .build(); 
    
    

    如果你支持https需要接入证书:

          novate.addSSL(hosts,  certificates)
    

    怎么用?

    准备证书文件数组和host 白名单;

     int[] certificates = {R.raw.myssl1, R.raw.myssl2,......}
    
     int[] hosts = {"https:// you hosturl2", "https:// you hosturl2",......}
    

    还要说明?

    certificates是你的ssl证书文件的id,项目中请放到raw资源文件下, myssl.cer怎么生成, 请用pc浏览器自动导出证书,保存, 还不清楚的话,我会醉醉。

    想对某个api不想缓存:

      novate.addCache(false)
    

    想对某个api不想cookie持久 :

      novate.addCookie(false)
    

    同样很多人想问 我想对novate进行扩展,咋办?别担心,Novate也提供了以下方法:

      novate.addInterceptor()
                 .addCallAdapterFactory()
                 .callFactory()
                 .proxy()
                 .client()
    

    上面只是列举了几个简单的api,更多的还是沿用的Retrofit的方法名,Retrofit怎么使用,Novate就怎么用.

    RxJava怎么处理?

     observable.subscribeOn(Schedulers.io())
                    .unsubscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread());
    

    内部统一已进行线程控制,所有请求都采用以上线程形式,无需你手动添加。

    RxApi

    主要处理请求的API,

    包含RxGet, RxPost, RxDelete,RxPut, RxBody,RxFrom, RxUpLoad,RxDownLoad.

    使用基本APi之前 请阅读对RxCallBack的介绍。

    RxGet

    进行get方式的请求调用,多种返回结果的方式供你选择,返回不同的数据类型参考请看RxCallBack的介绍。


    基础使用:

    返回String

      new Novate.Builder(this)
               .baseUrl("http://ip.taobao.com/")
               .build()
               .rxGet("service/getIpInfo.php", parameters, new RxStringCallback() {
    
                @Override
                public void onStart(Object tag) {
                    super.onStart(tag);
                }
    
                @Override
                public void onNext(Object tag, String response) {
                }
    
                @Override
                public void onError(Object tag, Throwable e) {
                }
    
                @Override
                public void onCancel(Object tag, Throwable e) {
                }
            });
    
    

    返回Bean

     novate.rxGet("path or url", parameters, new RxResultCallback<ResultModel>() {
    
    
           
        });
    

    返回List

        new Novate.Builder(this)
                .baseUrl("http://xxx.com/")
                .build()
                .rxGet("service/getList", parameters, new RxListCallback<List<MusicBookCategory>>() {
                    @Override
                    public void onNext(Object tag, int code, String message, List<MusicBookCategory> response) {
                       
                    }
    
    
                    @Override
                    public void onError(Object tag, Throwable e) {
                        
                    }
    
                    @Override
                    public void onCancel(Object tag, Throwable e) {
    
                    }
                });
    

    返回File

     novate.rxGet("path or url", null, new RxFileCallBack(filePath, "name.jpg") {
    
    
    
        });
    

    RxPost:

    进行Post方式的请求调用

    返回String

       novate.rxPost("path or url", parameters, new RxStringCallback() {
    
          
        });
    

    返回Bean

     novate.rxPost("path or url", parameters, new RxResultCallback<ResultModel>() {
    
    
           
        });
    

    返回List

     novate.rxPost("path or url", parameters, new RxListCallback<List<ResultModel>>() {
    
    
           ....
    
        });
    

    返回File

     novate.rxPost("path or url", null, new RxFileCallBack(filePath, "name.jpg") {
    
    
    
        });
    

    RxUpLoad

    以Body方式post数据,可以上报载文件,图片,多媒体文件等。

    Novate提供了2种方式上传文件。body和part模式,Body不包含key值,part包含key值。

    RxUploadWithBody

    以Body方式post数据,可以上报文件,图片等。

        String mPath = uploadPath; //"you File path ";
        String url = "http:/xxx.com";
    
        novate.rxUploadWithBody(url, new File(mPath), new RxStringCallback() {
    
            @Override
            public void onNext(Object tag, String response) {
            }
    
            @Override
            public void onError(Object tag, Throwable e) {
    
            }
    
            @Override
            public void onCancel(Object tag, Throwable e) {
    
    
            }
        });
    
    }
    

    RxUploadWithPart

    **上传文件,默认的key是 image **

        String mPath = uploadPath; //"you File path ";
         String url = "http:/xxx.com";
        File file = new File(mPath);
        novate.rxUploadWithPart(url, file, new RxStringCallback() {
    
            @Override
            public void onError(Object tag, Throwable e) {
            }
    
            @Override
            public void onCancel(Object tag, Throwable e) {
    
            }
    
            @Override
            public void onNext(Object tag, String response) {
            }
    
    
        });
    

    如果自定义key 请看下面

        String mPath = uploadPath; //"you File path ";
        String url = "http:/xxx.com";
    
        File file = new File(mPath);
        RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data; charset=utf-8"), file);
    
        final NovateRequestBody requestBody = Utils.createNovateRequestBody(requestFile, new UpLoadCallback() {
    
            @Override
            public void onProgress(Object tag, int progress, long speed, boolean done) {
    
                updateProgressDialog(progress);
            }
        });
    
    
        MultipartBody.Part body2 =
                MultipartBody.Part.createFormData("image", file.getName(), requestBody);
        //请将image改成你和服务器约定好的key
    
        novate.rxUploadWithPart(url, body2, new RxStringCallback() {
            @Override
            public void onError(Object tag, Throwable e) {
            }
    
            @Override
            public void onCancel(Object tag, Throwable e) {
    
            }
    
            @Override
            public void onNext(Object tag, String response) {
            }
    
    
        });
    

    上传多文件:

    rxUploadWithPartListByFile:

            List<File> fileList = new ArrayList<>();
             fileList.add(file);
             fileList.add(file);
             fileList.add(file);
            novate.rxUploadWithPartListByFile(url, fileList, new RxStringCallback() {
    
                @Override
                public void onStart(Object tag) {
                    super.onStart(tag);
                }
    
                @Override
                public void onNext(Object tag, String response) {
                }
    
                @Override
                public void onError(Object tag, Throwable e) {
                }
    
            });
    
    
    

    RxDownLoad

    以get方式下载数据,可以下载文件,图片,多媒体文件。

    使用rxGet()实现下载:

       String downUrl = "http://wap.dl.pinyin.sogou.com/wapdl/hole/201512/03/SogouInput_android_v7.11_sweb.apk";
    
        novate.rxGet(downUrl, parameters, new RxFileCallBack(FileUtil.getBasePath(this), "test.apk") {
    
            @Override
            public void onStart(Object tag) {
                super.onStart(tag);
                showPressDialog();
            }
    
            @Override
            public void onNext(Object tag, File file) {
                dismissProgressDialog();
            }
    
            @Override
            public void onProgress(Object tag, float progress, long downloaded, long total) {
                updateProgressDialog((int) progress);
            }
    
            @Override
            public void onError(Object tag, Throwable e) {
    
            }
    
            @Override
            public void onCancel(Object tag, Throwable e) {
    
            }
    
            @Override
            public void onCompleted(Object tag) {
                super.onCompleted(tag);
               
            }
        });
    

    RxDown()下载

         String downUrl = "http://wap.dl.pinyin.sogou.com/wapdl/hole/201512/03/SogouInput_android_v7.11_sweb.apk";
         new Novate.Builder(this)
                 .connectTimeout(20)
                 .writeTimeout(15)
                 .baseUrl(baseUrl)
                 .build()
                 .rxDownload(downUrl, new RxFileCallBack(FileUtil.getBasePath(this), "test.apk") {
                     @Override
                     public void onStart(Object tag) {
                         super.onStart(tag);
                         showPressDialog();
                     }
    
                     @Override
                     public void onNext(Object tag, File file) {
                         dismissProgressDialog();
                         Toast.makeText(ExampleActivity.this, "下载成功!", Toast.LENGTH_SHORT).show();
                     }
    
                     @Override
                     public void onProgress(Object tag, float progress, long downloaded, long total) {
                         updateProgressDialog((int) progress);
                     }
    
                     @Override
                     public void onProgress(Object tag, int progress, long speed, long transfered, long total) {
                         super.onProgress(tag, progress, speed, transfered, total);
                         updateProgressDialog((int) progress);
                     }
    
                     @Override
                     public void onError(Object tag, Throwable e) {
    
                     }
    
                     @Override
                     public void onCancel(Object tag, Throwable e) {
    
                     }
    
                     @Override
                     public void onCompleted(Object tag) {
                         super.onCompleted(tag);
                         dismissProgressDialog();
                     }
                 });
    

    Custom Api

    以上方法默认会处理Novate自带的BaseApiService,如果默认的BaseApiService无法满足你的需求时,Novate同样支持你自己的ApiService。

    定义一个你自己的API:

    MyAPI

     public interface MyAPI {
    
       @GET("url")
      Observable<MyBean> getdata(@QueryMap Map<String, String> maps);
    
     }
    

    Execute Call

    通过novate提供create()实例化你的API

     MyAPI myAPI = novate.create(MyAPI.class);
    

    通过novate.call()来执行你的接口,你也不用关心,novate内部同样已进行RxJava线程控制。

     novate.call(myAPI.getdata(parameters),
                new BaseSubscriber<MyBean>(ExempleActivity.this) {
    
                    @Override
                    public void onError(Throwable e) {
                        
                    }
    
                    @Override
                    public void onNext(MyBean MyBean) {
                    }
                });
    
    }
    

    取消

    每执行novate.xxx() 给上层返回了一个Subscription,上层可以调用unsubscribe()来进行取消!

         if (!subscription.isUnsubscribed()) {
             subscription.unsubscribe();
         }
    

    感谢

    感谢一直以来提供思路,和测试的朋友,不断提供建议和思路,让我不断完善novate,。 有的人看过或者读过后觉得此框架定制化严重,也有的人觉得挺好,所以提供了可配置方案:

    如果你觉得此框架的业务码和错误码定的太死,其实框架已提供定制化方案,比如可以在你的项目中Assets中修改config文件:

    如果想用自带的成功状态码0,不成功为非零的情况,可忽略一下配置。
    { "isFormat": "false", "sucessCode": [ "1", "0", "1001" ], "error": { "1001": "网络异常" } }

    如果不想对结果格式化检查,请将isFormat设置为:false

    将修改sucessCode的成功业务吗,请将你的成功的业务码加入到sucessCode节点中。

    错误码

    需要对错误码进行自定义翻译,请配置相关error信息,具体可配置成:

                 `{
               "isFormat": "false",
                  "sucessCode": [
                    "1",
                 "0",
                  "1001"
                ],
                "error": {
                  "1001": "网络异常",
                  "1002": "加入你的异常信息"
                         }
                 }
    

    缺陷

    目前1.X并没有完全运用RxJava2.0的新特性,笔者以开始联合@一叶扁舟 做兼容RxJava2.x的APi的工作, 目前Novate很遗憾无法为你提供压栈,背压式服务!
    在连续异步多个api时,诸如指定序列请求网络的场景,大白话就是你要根据上一个api的返回值再执行下一个api的情况,Novate1.x只能是靠开发者在上层的成功回调中执行,如果是1.x是对retrofit的强化,那么novate2.x将是对RxJava的运用强化。

    感谢公司设计妹子提供的Logo, 加速标识哦

    结束

    如果你对本框架有无法满足你的需求或有何更好的想法,请及时联系我进行交流,谢谢您的支持!欢迎您的star. Tamic期待更多的技术大牛开车指导!

    GitHub: https://github.com/Tamicer/Novate


    第一时间获取各位大佬的技术文章和资讯请关注微信公众号!

    开发者技术前线

    相关文章

      网友评论

      • 鹿乐吾:你好,请问有“图文一起”的具体示范吗?我用novate.rxBody试着老是失败!
      • 1aa5efcb8869:题主,请教一下,我使用的时候将post方法提取出来放到BaseService类中,然后loginService传参调用,在这里想要能从Base类中返回responebody到loginService中,可一直失败了,在onNext方法中将全局变量赋值失败了,这是怎么回事呢
      • 我一定会学会:请问一下,这个有做缓存吗?
      • CPLASF1925:支持Kotlin吗
      • 小mao:你好,请问这种 https://api.douban.com/v2/book/id/1220562 参数怎么处理,好像不能用 map吧,我用map传结果转成了https://api.douban.com/v2/book?id=1220562
        Tamic: @小mao 你的地址为何这么写?
        小mao:@Tamic 恩 我现在就是这样写的 那如果是多个参数就只能都拼接在后边吗
        Tamic:@小mao 直接追加在你的path后面
      • b0ac01817146:是否可提供源码
      • tanzhihao1qaz:Tamic辛苦了,我用executeGet时,出现错误了,提示——缺乏默认配置 <novate-config.json>文件,请问这个怎么解决?另外我用rxGet的时候返回值也是null,只有单纯使用get,手动解析才行
        tanzhihao1qaz:@Tamic 回复好快!!!还是不行,解析出来的bean还是空的,我启动了log,就是addLog,发现json字符串时已经获取到了,但是不明白为啥就是不能解析成我的bean,是不是要添加别的依赖?
        tanzhihao1qaz:我知道原因了,用executeGet请求的话,结果是空的,明明是同样的地址了,get就可以,executeGet就不可以,不知道怎么解决。。。。
        Tamic: @tanzhihao1qaz demo里面有 考进去
      • 有兴不虚昧:token过期有没有什么好的处理方式
      • luchefg:学习~
      • 流水不腐小夏:callback是怎么处理的,需要产生内存泄露吗?
        Tamic: @流水不腐小夏 有取消管理类 你cancelAll就行了
        流水不腐小夏:@Tamic 如果有多个callback,需要一条条cancel,有没有优雅的方式:smile:
        Tamic:@流水不腐小夏 判断一下为好! 你的activity消亡的的取消下订阅
      • 9277d1463f65:如缺陷一样,retrofit感觉最大的特点就是链式逻辑,可以一行代码进行多次异步转换,封装如果把这个功能抹去,就和其他网络框架无异了。我还是比较喜欢作者上一篇的封装,虽然代码有点多,但是保留了这个功能。
      • SheepYang:novate2.x什么时候有啊?
        Tamic:@SheepYang 后续会继续推出的
      • CleanCode:你好,github 上的源代码分支下不下来,很僵。
      • Avalon1:楼主有2个疑问!1.CacheInterceptor中你使用的是 .header("Cache-Control", "public, max-age=" + maxStale)这个。这样有网的环境不是也会缓存3天么、会不会不大好。
        Avalon1:还有一个就是。我在Manactivity调用了一下。好像内存泄露了。一直在持有activity的context来的。
      • coolzpw:题主,请教一下,我最近开始才接触网络请求,一般通用的请求头都是怎么设置的啊?
        coolzpw:@Tamic 恩 这个知道,现在我在看一些头部相关属性配置的文章,谢谢题主!
        Tamic:@coolzpw addheader
      • 3cf4933d1229:好文章
      • MrKC:github上下载只有demo啊,能开放源码下载吗?
        Tamic:@hello_JJ 切线分支就行啦
        CleanCode:你下载到源码了吗
        Tamic:@MrKC 有源码的 切下分支
      • 老师好我是小明同学:上传进度怎样弄,大神可以指教下吗?
        老师好我是小明同学:@Tamic 接触不就比较笨,有没有demo。。
        Tamic: @AndroidExplorer 和下载进度一样的
      • b79a056c2b14:请问下 同步请求怎么做
      • b79a056c2b14:用body一直报错
        Tamic:@拉卡拉辣俱乐部 嗯 吓我一条
        b79a056c2b14:@Tamic 回复的好快,我刚检查了一遍,是我用错了方法,换成了post 方法并且换了header content-type
        Tamic:@拉卡拉辣俱乐部 哪个版本?具体可以姿势过来
      • 8be19c94ab0f:封装得还是挺好的,对入门的新手来说,可以直接拿来就用,接口简单,也算是用上了retrofit和rxJava。但确实如上面几个朋友说的,对rxJava弱化了不少,其实也就是对rxJava的定制化扩展提供得不够。如果能在新手易上手使用的基础上,老手也能定制化rxJava的map\compose之类的转换操作,那就更好了。不然RxJava的优势体现不出来,单纯就为了使用而使用、为了封装而封装。纯属个人建议...... :smile:
        Tamic:@宇云3 感谢建议,后面会将RxJava的订阅者暴露给上层使用,前期封装的目的是,不懂retrofit和rxjava也能熟练使用,对于入门的朋有来说一下子啊解决很多问题对他们来说还是有难度的,循环渐进!
      • 行云流水之灵:你好,为什么调用novate.uploadFlies方法上传文件的时候不成功呢?
        Tamic:@行云流水之灵 //构建body 链式语法高逼格哦
        RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
        .addFormDataPart("name", "tamic")
        .addFormDataPart("password", "12345")
        .addFormDataPart("atavr", file.getName(), RequestBody.create(MediaType.parse("image/*"), file))
        .build();


        novate.upload(url, requestBody, new BaseSubscriber<ResponseBody>(ExempleActivity.this) {
        .....
        });
        行云流水之灵:那应该选择哪个方法进行多文件上传呢?
        Tamic:@行云流水之灵 有的服务端采用的是裸的body表单方式, 有的后端却是在body中part一部分,要选择性使用!
      • 3d56ce837f8c:String url = "http://shop1.m.huisou.com/apps/inquiry/index?page=1&type=1&quot;;
        novate.get(url, null, new BaseSubscriber<ResponseBody>(context) {
        @Override
        public void onError(Throwable e) {
        Log(e.getCode()+e.getMessage());
        }

        @Override
        public void onNext(ResponseBody responseBody) {
        Toast("请求成功");
        }
        });
        能帮我看下为什么是网络错误吗 我吧url放到网站上是可以请求成功的 但是用你的方法报网络错误
        3d56ce837f8c:@Tamic 我里面的page和type是会变的 用get方法直接拼接成了一个url 然后按照我上面的get方法 请求 就提示1003网络错误 但是用你的这个test方法就是可以请求成功的
        Tamic:@我的ID有点屌 我这边可以的

        novate = new Novate.Builder(this)

        .connectTimeout(8)
        .baseUrl("http://shop1.m.huisou.com/&quot;)
        .addHeader(headers)
        .addLog(true)
        .build();

        String urltest ="http://shop1.m.huisou.com/apps/inquiry/index?page=1&type=1&quot&quot;;


        novate.test(urltest, null,
        new BaseSubscriber<ResponseBody>(ExempleActivity.this) {
        @Override
        public void onError(Throwable e) {
        Log.e("OkHttp", e.getMessage());
        Toast.makeText(ExempleActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
        }




        @Override
        public void onNext(ResponseBody responseBody) {
        try {
        Toast.makeText(ExempleActivity.this, new String(responseBody.bytes()), Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
        e.printStackTrace();
        }
        }
        });

        }
        3d56ce837f8c:@我的ID有点屌 把后面的那个回车&quot去掉
      • bincn:你好,这个库是如何取消事件订阅的?
        发起请求(Retrofit + RxJava),会有一个观察者(Observer)和一个订阅者(Subscribe),如果事件队列(网络请求)正常完成,会调用 onComplete() ,这时 RxJava 底层会调用到 subscriber.unsubscribe() 取消事件订阅,但是如果事件队列出现异常,这时会调用 onError() ,此时 RxJava 底层是没有调用 subscriber.unsubscribe() ,这样是不是会出现网络异常的时候,发起事件队列因为没有取消订阅而导致内存溢出。
        Tamic:@Mawb 可以写个全局的 通过key来管理Subscription 类似回调这种形式
        bincn:@Tamic 我现在是在 activity 中利用 CompositeSubscription 来管理事件,不知道有没有其他更好的办法?
        Tamic:@Mawb 取消和压栈还在完善中,现在是每次订阅会给上层返回了一个Subscription,上层可以调用unsubscribe()
      • abd3cecc8137:请问 缓存怎么 控制 现在的情况好像是 没网就会自动加载缓存,比如某些地方不想加缓存 怎么加
        Tamic:@Tamic novate.addCache()你可以自己设置添加的
        Tamic:@abd3cecc8137 相对某个api不加缓存是吗?
      • 看秋叶一片片飘落:大神,谢谢
        Tamic:@看秋叶一片片飘落 :smile:
      • Tamic:网络请求不恩呢该在主线程里 你需要开启子线程
        Tamic:@deak_zhou 我这边没有报错呀
        03d487a151c9:@Tamic 那个图片下载报空指针是什么原因呢
        03d487a151c9:@Tamic 这块你不是用了rxjava吗,放在子线程的操作应该是框架来完成才对啊,
      • 03d487a151c9:http://files.660pp.com/d/5153555f5960/1/
        这是我测试用的图片url,直接报空指针
        03d487a151c9:@Tamic 能否给个qq或者微信啊,特别着急。
        03d487a151c9:@Tamic 能看到啊,我早上没改代码的时候用的是Retrofit2.0,没有问题,正常下载,我还读出来设置到ImageView上了。
        writeResponseBodyToDisk这个方法会报android.os.NetworkOnMainThreadException你没遇到过吗
        Tamic:@deak_zhou 图片无法加载的
      • 03d487a151c9:1、表示下载文件不能用,demo里面的那个apk下载是可以正常下载,但保存的时候由于没有放到子线程直接报android.os.NetworkOnMainThreadException。还有这个保存文件的路径为何不提供设置方法
        2、我测试的图片url用Retrofit2.0是调通的,全都正常,只是代码不好看用了这个库,代码是少了,可错误太多了,浪费了一天时间。

        Tamic:@deak_zhou 恩 解决了就行,全部放到IO 线程中,后面操作UI 需你自己post到UI线程了,有利有弊!
      • 逍遥之飞起来:支持sdk必须21以上,难怪我之前依赖不上去
        Tamic:@xiaoyao86 现在都快到24了
      • 9b1dd22c839f:Server: nginx/0.8.46
        10-21 15:35:48.632 20897-22896/com.zhaomi.wisdomstudy D/OkHttp: Date: Fri, 21 Oct 2016 07:35:48 GMT
        10-21 15:35:48.632 20897-22896/com.zhaomi.wisdomstudy D/OkHttp: Content-Type: text/html;charset=utf-8
        10-21 15:35:48.633 20897-22896/com.zhaomi.wisdomstudy D/OkHttp: Transfer-Encoding: chunked
        10-21 15:35:48.633 20897-22896/com.zhaomi.wisdomstudy D/OkHttp: Connection: keep-alive
        10-21 15:35:48.633 20897-22896/com.zhaomi.wisdomstudy D/OkHttp: Vary: Accept-Encoding
        10-21 15:35:48.633 20897-22896/com.zhaomi.wisdomstudy D/OkHttp: X-Powered-By: PHP/5.2.14
        10-21 15:35:48.633 20897-22896/com.zhaomi.wisdomstudy D/OkHttp: Set-Cookie: PHPSESSID=5e9955fe395b75b8a95e553e4f644246; path=/
        10-21 15:35:48.633 20897-22896/com.zhaomi.wisdomstudy D/OkHttp: Expires: Thu, 19 Nov 1981 08:52:00 GMT
        10-21 15:35:48.633 20897-22896/com.zhaomi.wisdomstudy D/OkHttp: Content-Encoding: gzip
        10-21 15:35:48.633 20897-22896/com.zhaomi.wisdomstudy D/OkHttp: Cache-Control: public, max-stale=259200
        10-21 15:35:48.634 20897-22896/com.zhaomi.wisdomstudy D/OkHttp: <-- END HTTP
        10-21 15:35:48.647 20897-20897/com.zhaomi.wisdomstudy E/novate: -->:Attempt to invoke virtual method 'java.util.Set java.util.concurrent.ConcurrentHashMap.keySet()' on a null object reference
        9b1dd22c839f:@Tamic 哪里要调整类型啊,
        Tamic:@啦啦啦啦啦了阿偶 类型转换出错了
        9b1dd22c839f:@啦啦啦啦啦了阿偶 为什么报这个问题java.util.Set java.util.concurrent.ConcurrentHashMap.keySet()'
      • 426b33b9f70c:post请求调用你的接口没问题,换成我自己的接口出现这个错误,您知道怎么回事吗?java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference
        Tamic:@愁容 这个是cookie不兼容导致的 ,服务端的jdk版本可能过久,去掉cookie同步就可以
        Tamic:@愁容 你的数据返回有空对象了吧
      • 上帝之手yogor: private void performSougu(){
        Map<String, String> parameter = new HashMap<String, String>();
        parameter.put("m", "souguapp");
        parameter.put("c", "appusers");
        parameter.put("a", "network");
        novate = new Novate.Builder(this)
        .connectTimeout(100)
        .baseUrl("http://lbs.sougu.net.cn/&quot;)
        .addLog(true)
        .build();
        novate.executeGet("app.php",parameter, new Novate.ResponseCallBack<NovateResponse<SouguBean>>() {
        @Override
        public void onStart() {
        }
        @Override
        public void onCompleted() {
        }

        @Override
        public void onError(Throwable e) {
        Log.d("aa",">>"+e.getMessage());
        }
        @Override
        public void onSuccee(NovateResponse<SouguBean> response) {
        Log.d("aa",">>"+response.getData());
        }
        });
        }

        这是demo里perform_Api()方法里调用的,实体bean也是demo的,api调试工具里可以打印返回的数据。我用executeGet方法怎么拿不到返回值,麻烦给看下我写的有什么问题吗?
        Tamic:@上帝之手yogor 这种我没试过 我实际项目中的使用数组格式的是可以的
        上帝之手yogor:@Tamic uil后面的分号是不小心多粘上的,demo里没有。好像数组形式的用novate.executeGet就不能解析,
        {
        "data": [
        {
        "id": 155,
        "merchant_num": 2523,
        "name": "甘肃",
        "sales_num": 10
        }
        ],
        "status": 0
        }
        这样的就不行,
        Tamic:@上帝之手yogor 你的uil后面的分号去掉
      • 上帝之手yogor:@Tamic: 大神给看看吧
        Tamic:@上帝之手yogor 好的 我看看 还有朋友提出没取消功能 这个得加上
      • 上帝之手yogor: private void performCode(){
        novate = new Novate.Builder(this)
        //.addParameters(parameters)
        .connectTimeout(5)
        .baseUrl("http://gank.io/api/data/&quot;)
        .addLog(true)
        .build();
        novate.executeGet("Android/3/1", null, new Novate.ResponseCallBack<NovateResponse<Results>>() {

        @Override
        public void onStart() {
        }
        @Override
        public void onCompleted() {
        }
        @Override
        public void onError(Throwable e) {
        }
        @Override
        public void onSuccee(NovateResponse<Results> response) {
        Log.d("aa", "/?/" +response.getData());
        }
        }
        );
        }

        onSuccee怎么返回null??
        上帝之手yogor:@Tamic 是有cookie的问题,删除cookie就正常了。但是novate.executeGet是不能解析数组的,用自定义的MyAPI 就可以解析数组形式了。
        Tamic:@上帝之手yogor 你addCookie(false)取消cookie同步试试
        Tamic:@上帝之手yogor 不会吧 除非数据为空 或者实体bean没对应吧
      • fe58ab2e6d80:你好,下载了你的github上的例子,请问如何cancle掉一个请求?
        fe58ab2e6d80:@Tamic 恩恩,好的 谢谢楼主
        Tamic:@kannerlin 现在是每次订阅会给上层返回了一个Subscription,上层可以调用unsubscribe()
        Tamic:@kannerlin 这个还没加
      • e97bb45221d5:为什么
        @Override
        public void onStart() {
        Log.i(TAG, "onStart: ");
        }

        @Override
        public void onCompleted() {
        Log.i(TAG, "onCompleted: ");
        }
        这两个方法没执行到呢?
        Tamic:@wangwei129549 没明白你指的是?
        e97bb45221d5:@Tamic
        // http://apis.juhe.cn/mobile/get?phone=YOURPHONE&key=YOURKEY

        String baseUrl = "http://apis.juhe.cn/&quot;;

        Map<String, String> parameters = new HashMap<>();
        parameters.put("phone", "1388888888");
        parameters.put("key", "090348d0001b3a04516399196407d6c8");
        Novate novate = new Novate.Builder(this)
        .baseUrl(baseUrl)
        .addLog(true)
        .build();
        novate.executeGet("mobile/get", parameters, new Novate.ResponseCallBack<PhoneModel>() {

        @Override
        public void onStart() {
        Log.i(TAG, "onStart: ");
        }

        @Override
        public void onCompleted() {
        Log.i(TAG, "onCompleted: ");
        }

        @Override
        public void onError(Throwable e) {
        Log.i(TAG, "onError: " + e.getMessage());
        }

        @Override
        public void onSuccee(PhoneModel response) {
        Log.i(TAG, "onSuccee: " + response.result.toString());
        }
        });

        log:
        I/MainActivity: onSuccee: ResultBean{province='云南', city='昆明', areacode='0871', zip='650000', company='中国移动', card='移动全球通卡'}

        onSuccee能执行到,出错时onError也能执行到
        Tamic:@wangwei129549 不会吧 你是怎么写的
      • 心灵传输者:onNext onerror都不执行是啥问题
        Tamic:@心灵传输者 具体什么场景不支持呢
      • b6c7a3ded5ff:能动态设置header吗?
        Tamic:@dyhuang 能
      • JasonChen1195:断点上传文件的话还是参考之前的那篇断点下载的实现咯?
        Tamic:@JustNormal 是的
      • Marno:嗯,觉得这样使用时很方便了,但是好像RxJava的优势就显示不出来了,如果我需要对返回的数据进行一些操作的话,比如flatmap,或者zipWith之类的,怎么办呢?
        Tamic:@_夜阑静 是有点对Rxjava弱化了,flatmap的支持正在开发中,Observable<T目前是支持泛型的
        32efddf65f3a:@Tamic 正好看到这个问题,加上返回Observable<T>感觉会比较好。
        Tamic:@Marno 可以改改
      • and_pu:关注你的简书学到了太多东西
        Tamic:@and_pu 感谢一直的支持
      • DevSiven:如果请求是json数据,还需要考虑这点
        Tamic:@傻小孩b_移动开发 已更新最新版本,支持提交json
        Tamic:@傻小孩b_移动开发 感谢建议
      • 捡淑:马克
      • 8314e3a0c30e:好牛啊,我们用的还是xutil
        Tamic:@赵丰年 看领导给不给用
      • 程序员徐公:学习了
        Tamic:@xujun_jy 互勉

      本文标题:Novate:Retrofit2.0和RxJava的又一次完美改

      本文链接:https://www.haomeiwen.com/subject/jbsesttx.html