RxJava+Retrofit示例 ,Retrofit 注解学习

简介: RxJava+Retrofit示例 ,Retrofit 注解学习

使用之前加入依赖:


compile 'com.squareup.retrofit2:retrofit:2.1.0'

定义接口


public interface GithubService {
    @GET("users/{user}")
    Call<ResponseBody> getUserString(@Path("user") String user);
}

这里我们使用http中的get 方法获取users这个接口下,当前user的具体信息,参数为当前user名。返回内容为Http请求的ResponseBody。


Retrofit 返回ResponseBody


private void SimpleRetrofit() {
        OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
        Retrofit.Builder builder = new Retrofit.Builder()
                .baseUrl(BASE_URL);
        Retrofit retrofit = builder.client(httpClient.build()).build();
        GithubService simpleService = retrofit.create(GithubService.class);
        Call<ResponseBody> call = simpleService.getUserString(name);
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                loading.dismiss();
                try {
                    String result = response.body().string();
                    Gson gson = new Gson();
                    GithubUserBean bean = gson.fromJson(result, GithubUserBean.class);
                    setUserView(bean);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                loading.dismiss();
            }
        });
    }
private void setUserView(GithubUserBean user) {
        if (user != null) {
            viewShell.removeAllViews();
            View view = LayoutInflater.from(mContext).inflate(R.layout.user_item_layout, null);
            TextView title = (TextView) view.findViewById(R.id.title);
            TextView id = (TextView) view.findViewById(R.id.userId);
            TextView creteaTime = (TextView) view.findViewById(R.id.createTime);
            TextView updateTime = (TextView) view.findViewById(R.id.updateTime);
            TextView bio = (TextView) view.findViewById(R.id.bio);
            ImageView avatar = (ImageView) view.findViewById(R.id.avatar);
            title.setText("Name: " + user.getLogin());
            bio.setText("Bio: " + user.getBio());
            id.setText("Id: " + String.valueOf(user.getId()));
            creteaTime.setText("createTime: " + user.getCreated_at());
            updateTime.setText("updateTime: " + user.getUpdated_at());
            Glide.with(mContext).load(user.getAvatar_url()).into(avatar);
            viewShell.addView(view);
        } else {
            Toast.makeText(mContext, "result is null", Toast.LENGTH_SHORT).show();
        }
    }

GitHubUserBean 为网络请求结果json数据所对应的实体类。


通过这段代码,我们在最终的回调方法里可以友好的处理请求结果,失败时onFailure方法执行。成功时,onResponse方法执行,我们在这里用Gson解析返回的数据,并进行UI更新操作(setUserView(bean)),


这里我们这样做有些啰嗦,Gson转换的方式都是类似,唯一不同的只是每次网络请求结果对应的实体类;因此我们可以借助强大的Retrofit帮助我们完成Gson转换的步骤。当然,如果在你所在的开发环境中,接口返回的并不是json格式的数据,也没有问题的。

Retrofit官网对可转换类型给出的介绍。有这么多种,当然了如果你们家服务器返回的数据格式比较神奇,你也可以自定义转换类。


好了,言归正传,这里还是以Json 格式数据为例。


添加依赖:


compile 'com.squareup.retrofit2:converter-gson:2.1.0'

注意这里converter-gson 的版本号,要和之前Retrofit的版本号保持一致。


我们重新定义接口:


public interface GithubService {
    @GET("users/{user}")
    Call<GithubUserBean> getUser(@Path("user") String user);
}

这里我们用GithubUserBean取代ResponseBody,直接将其作为返回类型。


Retrofit 返回对象


private void LazyRetrofit() {
        OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
        Retrofit.Builder builder = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create());
        Retrofit retrofit = builder.client(httpClient.build()).build();
        GithubService service = retrofit.create(GithubService.class);
        Call<GithubUserBean> call = service.getUser(name);
        call.enqueue(new Callback<GithubUserBean>() {
            @Override
            public void onResponse(Call<GithubUserBean> call, Response<GithubUserBean> response) {
                GithubUserBean bean = response.body();
                setUserView(bean);
                loading.dismiss();
            }
            @Override
            public void onFailure(Call<GithubUserBean> call, Throwable t) {
                loading.dismiss();
            }
        });
    }

这里的实现方式和上面基本相似,只是多了一行


.addConverterFactory(GsonConverterFactory.create());

这样,我们在onResponse中获得就是对象,不再需要做额外的转换工作,可以直接使用。


RxJava+Retrofit



这里我们就看看将RxJava 和我们之前的内容结合在一起会有怎样的效果。


首先,加入依赖


compile 'io.reactivex:rxjava:1.1.7'
    compile 'io.reactivex:rxandroid:1.2.1'

RxJava+Retrofit 实现###


private void RxRetrofit() {
        GithubService service = GenServiceUtil.createService(GithubService.class);
        final Call<GithubUserBean> call = service.getUser(name);
        final Observable myObserable = Observable.create(new Observable.OnSubscribe<GithubUserBean>() {
            @Override
            public void call(Subscriber<? super GithubUserBean> subscriber) {
                Response<GithubUserBean> bean = null;
                try {
                    bean = call.execute();
                    subscriber.onNext(bean.body());
                } catch (IOException e) {
                    e.printStackTrace();
                    subscriber.onError(e);
                }
                subscriber.onCompleted();
            }
        });
        myObserable
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .map(new Func1<GithubUserBean, GithubUserBean>() {
                    @Override
                    public GithubUserBean call(GithubUserBean o) {
                        if (TextUtils.isEmpty(o.getBio())) {
                            o.setBio("nothing !");
                        }
                        return o;
                    }
                })
                .subscribe(new Subscriber<GithubUserBean>() {
                    @Override
                    public void onCompleted() {
                        loading.dismiss();
                    }
                    @Override
                    public void onError(Throwable e) {
                        loading.dismiss();
                    }
                    @Override
                    public void onNext(GithubUserBean o) {
                        setUserView(o);
                    }
                });
    }

这里有几点需要注意:


  • RxJava 本身最大的特定就是异步,因此这里我们Retrofit执行网络请求的时候,使用了execute(同步请求),而不再是enqueue。
  • RxJava 可以使用subscribeOn和observeOn完美处理Observeable和Subscribe的执行线程问题。
  • 这里使用RxJava中map操作符,对返回内容中的为null或“” 的对象做了简单的处理。


我们引入RxJava实现了同样的功能,却使得代码量增加了很多。不禁要问,RxJava的价值到底在哪里呢?


RxJava + Retrofit 到底好在哪里


好了,为了说明为题,我们添加一个接口


public interface GithubService {
    @GET("users/{user}")
    Call<GithubUserBean> getUser(@Path("user") String user);
    @GET("users/{user}/followers")Observable<List<UserFollowerBean>> followers(@Path("user") String usr);
}

当然这里依旧需要添加依赖:


compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'

同时在Service的封装方法中添加


.addCallAdapterFactory(RxJavaCallAdapterFactory.create())

这样,RxJava就和Retrofit完美的关联在了一起。


我们在接口中,定义followers()方法直接返回了Observable,因为Observable是RxJava的源头,而且Retrofit可以很好的支持RxJava,这样就非常方便了。


private void RxRetrofitList() {
        GithubService service = GenServiceUtil.createService(GithubService.class);
        Observable<List<UserFollowerBean>> myObserve = service.followers(name);
        myObserve
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<List<UserFollowerBean>>() {
                    @Override
                    public void onCompleted() {
                        loading.dismiss();
                    }
                    @Override
                    public void onError(Throwable e) {
                        loading.dismiss();
                        e.printStackTrace();
                    }
                    @Override
                    public void onNext(List<UserFollowerBean> userFollowerBeen) {
                        setFollowersView(userFollowerBeen);
                    }
                });
    }

在接口中返回的内容就是Observable,因此不用再像之前一样单独定义Observable;在onNext 方法中,接收到返回的对象,更新UI。 这里如果我们不使用RxJava,单独使用Retrofit实现这个过程是没有任何问题的; RxJava看似没有价值;但是假设现在出现如下之一的情景


  • 需要对返回的userFollowerBeen 这个list 进行按用户名从小到大的排序
  • 需要对返回的userFollowerBeen 这个list 进行按用户ID从小到大的排序
  • 如果返回的userFollowerBeen 这个list 中,某一项的头像地址为空,则不显示该项

.....


这种情景在实际开发中太常见了,试想如果没有RxJava;那么每一次需求的变更都意味着我们需要去修改setFollowersView这个方法,需求一旦变更,就去修改这个方法,这样会不可避免的产生各种bug。那有没有办法不去修改这个方法呢?这个时候,就需要强大的RxJava了。


这里我们就看看如何在不修改setFollowersView的前提下,实现对用户名从小到大的排序:


private void RxRetrofitList() {
        GithubService service = GenServiceUtil.createService(GithubService.class);
        Observable<List<UserFollowerBean>> myObserve = service.followers(name);
        myObserve
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .map(new Func1<List<UserFollowerBean>, List<UserFollowerBean>>() {
                    @Override
                    public List<UserFollowerBean> call(List<UserFollowerBean> userFollowerBeen) {
                        for (UserFollowerBean bean : userFollowerBeen) {
                            String name = "";
                            name = bean.getLogin().substring(0, 1).toUpperCase() + bean.getLogin().substring(1, bean.getLogin().length());
                            bean.setLogin(name);
                        }
                        return userFollowerBeen;
                    }
                })
                .map(new Func1<List<UserFollowerBean>, List<UserFollowerBean>>() {
                    @Override
                    public List<UserFollowerBean> call(List<UserFollowerBean> userFollowerBean) {
                        Collections.sort(userFollowerBean, new Comparator<UserFollowerBean>() {
                            @Override
                            public int compare(UserFollowerBean o1, UserFollowerBean o2) {
                                return o1.getLogin().compareTo(o2.getLogin());
                            }
                        });
                        return userFollowerBean;
                    }
                })
                .subscribe(new Subscriber<List<UserFollowerBean>>() {
                    @Override
                    public void onCompleted() {
                        loading.dismiss();
                    }
                    @Override
                    public void onError(Throwable e) {
                        loading.dismiss();
                        e.printStackTrace();
                    }
                    @Override
                    public void onNext(List<UserFollowerBean> userFollowerBeen) {
                        setFollowersView(userFollowerBeen);
                    }
                });
    }

在代码中我们使用RxJava的map 操作符,对返回数据做了两次处理,首先将所有用户名的首字母转换为大写字母;然后对整个list按照用户名从小到大排序。因为用户名中同时包含以大小写字母打头的内容,所以为了方便,我们进行了一次转换大写的操作。

同样是随着需求变更,修改代码;但是你会发现,使用RxJava的方式,会降低出现bug的概率,而且就算是不同的人去改,也会比较方便维护。


看到了吧,这就是RxJava的优点,当然这个例子也只是冰山一角。这里提到的map操作符只是RxJava庞大操作符集合中的一员,更特别的是,RxJava的操作符还是可以自定义的,这样让我们的代码有了无限的可能;RxJava的存在不仅仅在于网络请求,可以用在别的方面;RxJava其实是体现了一种思路,所有对数据的操作都在流上完成,将最终的结果返回给观察者。同时,如果返回的followers 列表有任何异常,RxJava的onError 方法会执行,这就方便我们去处理异常数据了。


总结##



通篇通过对Android 网络请求各种实现的总结,可以看到 相对于Volley,AsyncHttpClient 等库,RxJava+Retrofit 的优势并非特别显著;在执行效率及功能上并无大的亮点;对Volley进行良好的封装同样可以实现类似Retrofit自动转Gson的功能;RxJava+Retrofit 结合会让我们写代码的方式更加有条理,虽然代码量会增多,但逻辑的清晰才是最重要的不是吗


Retrofit 注解学习 @GET @POST @Query @QueryMap @Field @FieldMap



@Path : 请求的参数值直接跟在URL后面时,用@Path配置

@Query: 表示查询参数,以?key1=value1&key2=value2的形式跟在请求域名后面时使用@Query

@QueryMap :以map的方式直接传入多个键值对的查询参数

@Field: 多用于post请求中表单字段,每个@Field后面,对应一对键值对。

@FieldMap :以map的方式传入多个键值对,作为body参数

@Body: 相当于多个@Field,以对象的形式提交


1、GET请求:


(1)情形一:

@Query


仅带查询参数:http://192.168.0.1/weather?city=北京


@GET("weather")
   Observable<WeatherEntity> getWeather(@Query("city") String city);

(2)情形二:


@Path


请求参数直接跟在请求路径下:http://192.168.0.1/weather/北京


@GET("weather/{city_name}")
    Observable<Object> getWeather(@Path("city_name") String city_name);

(3)情形三:


@Path和@QueryMap结合


此种情形用得比较少:http://192.168.0.1/weather/北京?user_id=1&user_name=jojo


@GET("weather/{city_name}")
    Observable<Object> getWeather(@Path("city_name")String city_name, @QueryMap Map<String, String> queryParams);

HashMap<String, String> queryParams= new HashMap<>();
    hashMap.put("user_id","1");
    hashMap.put("user_name","jojo");

2、POST请求:


(1)情形一: http://192.168.0.1/comment

body参数:{"comment_id":"1","content":"我是评论","user_id":"1001"}

@Filed 方式处理


@FormUrlEncoded //使用@Field时记得添加@FormUrlEncoded
    @POST("comment")
    void doComments(@Field("comment_id")String comment_id, @Field("content")String content, @Field("user_id") String user_id);

@FieldMap 方式处理


@FormUrlEncoded
    @POST("comment")
    void doComments(@FieldMap Map<String, String> paramsMap );

通过键值对,以表单的形式提交:


HashMap<String, String> hashMap = new HashMap<>();
        hashMap.put("comment_id","1");
        hashMap.put("content","我是评论");
        hashMap.put("user_id","1001");

@Body方式处理


@POST("comment")
    void doComments(@Body Object reqBean);

@POST("comment")
    void doComments(@Body List<Object> requestList);

(2)情形二:Retrofit文件上传: http://192.168.0.1/upload/


/**
     * 文件上传
     */
    @POST("upload/")
    Observable<Object> uploadFile(@Body RequestBody requestBody);

只不过文件上传传入的是RequestBody类型,下面是构建RequestBody的方式:


File file = new File(mFilePath); //mImagePath为上传的文件绝对路径
        //构建body
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("multipart/form-data"), file))
                .build();

3、PUT请求:


(1)情形一:http://192.168.0.1/comment/88

@PUT("comment/{comment_id}")
    void comment(@Path("comment_id") String comment_id);

(2)情形二:http://192.168.0.1/comment/88?user_id=1001


@PUT("comment/{comment_id}")
    void comment(@Path("comment_id") String comment_id @Query("user_id") String user_id);

(3)情形三:http://192.168.0.1/comment/88?user_id=1001


加body参数:{"content":"我是评论","type":"1"}

此类请求的应用场景:适合于body中需要传入多个请求参数,这样可以将多个请求的参数字段封装到一个实体中,这样就不用写多个@Filed了。


public class RequestBean {
    public String content;
    public String type;
    //实际中可能还有多个请求字段
}

RequestBean  requestBean = new RequestBean();
    requestBean .content = "我是评论";
    requestBean .type = "1";

@PUT("comment/{comment_id}")
    void comment(@Path("comment_id") String comment_id @Query("user_id") String user_id @Body RequestBean reqBean);

4、DELETE请求:


假如删除评论:http://192.168.0.1/comment/88


@DELETE("comment/{comment_id}")
    void comment(@Path("comment_id") String comment_id);

其他可能的delete请求,配置方式都是类似的,这里就不多举例了。主要是要好好理解下面这些常用的请求参数注解的作用和用法。


综上所述,可以归纳出上面几个注解的用法:


@Path : 请求的参数值直接跟在URL后面时,用@Path配置 @Query: 表示查询参数,以?key1=value1&key2=value2的形式跟在请求域名后面时使用@Query @QueryMap :以map的方式直接传入多个键值对的查询参数 @Field: 多用于post请求中表单字段,每个@Field后面,对应一对键值对。 @FieldMap :以map的方式传入多个键值对,作为body参数 @Body: 相当于多个@Field,以对象的形式提交


目录
相关文章
|
3月前
retrofit+okhttp+rxjava
retrofit+okhttp+rxjava
|
5月前
|
JSON Java 数据格式
rxjava2+retrofit2
rxjava2+retrofit2
47 1
|
缓存
Retrofit配置及各情况处理
Retrofit配置及各情况处理
195 0
|
设计模式 API
定制Retrofit
定制Retrofit
112 0
定制Retrofit
|
JSON 安全 Java
Retrofit入门
Retrofit入门
|
JSON Java 数据格式
rxjava2+retrofit2 简介
rxjava2+retrofit2 简介
96 0
|
API Android开发 Java
RxJava2 和 Retrofit2 结合使用详解
不讲 rxjava 和 retrofit 而是直接上手 2 了,因为 2 封装的更好用的更多。 1. 观察者模式 常见的 button 点击事件为例,button 是被观察者,listener 是观察者,setOnClickListener 过程是订阅,有了订阅关系后在 button 被点击的时候,监听者 listener 就可以响应事件。
|
JSON Java API
|
Android开发
Retrofit2源码解析(一)
从源码分析Retrofit的原理
2959 0
|
缓存 API Android开发
浅谈OkHttp以及Retrofit+RxJava的封装使用
1.为什么我们要使用OkHttp?OkHttp有什么优点?  说OkHttp之前我们先说另外两个网络请求库——HttpUrlConnection和HttpClient。
2202 0