Retrofit2的简单应用与封装

Retrofit2的简单应用与封装

标签: Retrofit网络框架封装
8533人阅读 评论(4) 收藏 举报
本文章已收录于:
分类:
Retrofit出来有一段时间了,我最近才知道有这个框架,之前一直使用的Volley,知道Retrofit后就试用了一下,感觉还是挺不错的,使用起来比Volley更方便,封装也比较简单,下面先简单介绍下它的基本使用方法

我用Android Studio开发,首先,在build.gradle中引入Retrofit的依赖
compile 'com.squareup.retrofit2:retrofit:2.0.0'
这是写这篇博客时Retrofit的最新版本,2.0版本跟之前的1.*版本还是有不少不同的地方,如果是从1.*版本切换到2.0,还是有不少地方需要进行修改。

先介绍下最基本的Get请求,访问一个url地址,返回参数,客户端接收并打印出来:

(1)定义一个接口,封装访问的方法

  1. public interface RetrofitService {  
  2.     @GET("getMethod")  
  3.     Call<String> getTest();  
  4. }  
public interface RetrofitService {
    @GET("getMethod")
    Call<String> getTest();
}

首先,可以看到注解@GET,表示这是一个GET请求,里面的getMethod是GET请求的名称,也就是URL中请求部分的地址,接下来,getTest是请求的方法名称,返回的是一个Call对象,泛型为String,注意,从Retrofit2开始,网络请求就是用的OKHttp,而之前的1.*版本只有在引入了OKHttp的前提下才会使用,否则会跟Volley一样,2.3之前使用HttpClient,2.3之后使用HttpURLConnection,OKHttp里返回的是Call对象,所以这里也是返回Call,具体细节大家可以看源码,这里不影响理解,只要知道Retrofit的所有返回都是一个Call对象即可。

(2)定义一个基类用来生成(1)中定义的接口

  1. public class RetroFactory {  
  2.     private static String baseUrl = "http://192.168.0.105:8082/MyWeb/";  
  3.   
  4.     private static Retrofit stringRetrofit = new Retrofit.Builder()  
  5.             .baseUrl(baseUrl)  
  6.             .addConverterFactory(ScalarsConverterFactory.create())  
  7.             .build();  
  8.   
  9.     public static RetrofitService getStringService() {  
  10.         RetrofitService service = stringRetrofit.create(RetrofitService.class);  
  11.         return service;  
  12.     }  
  13. }  
public class RetroFactory {
    private static String baseUrl = "http://192.168.0.105:8082/MyWeb/";

    private static Retrofit stringRetrofit = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(ScalarsConverterFactory.create())
            .build();

    public static RetrofitService getStringService() {
        RetrofitService service = stringRetrofit.create(RetrofitService.class);
        return service;
    }
}
baseUrl是网络请求的基础地址,192.168.0.105是我本机的IP,8082是服务器端口号,我是使用的汤姆猫,其它也可以,看个人习惯,MyWeb是新建的一个Web项目,用来接收客户端请求。接下来,生成一个Retrofit对象,这里主要指定2个参数,一个就是网络请求的基础地址,另一个就是转换工厂,注意,1.*版本默认使用Gson,而2.0版本则必须明确指定解析服务端相应的转换方式,我现在服务端返回给客户端的就是一个简单的字符串,所以我选用Scalars,具体转换方式有几种:

  1. Gson: com.squareup.retrofit2:converter-gson  
  2. Jackson: com.squareup.retrofit2:converter-jackson  
  3. Moshi: com.squareup.retrofit2:converter-moshi  
  4. Protobuf: com.squareup.retrofit2:converter-protobuf  
  5. Wire: com.squareup.retrofit2:converter-wire  
  6. Simple XML: com.squareup.retrofit2:converter-simplexml  
  7. Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars  
Gson: com.squareup.retrofit2:converter-gson
Jackson: com.squareup.retrofit2:converter-jackson
Moshi: com.squareup.retrofit2:converter-moshi
Protobuf: com.squareup.retrofit2:converter-protobuf
Wire: com.squareup.retrofit2:converter-wire
Simple XML: com.squareup.retrofit2:converter-simplexml
Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

用户需要根据服务器实际返回的类型做出选择,这里选用的最后一种,当然,别忘了在build.gradle中添加依赖compile 'com.squareup.retrofit2:converter-scalars:2.0.0'。创建了Retrofit对象后,使用create方法,即可生成(1)中定义的接口

(3) 定义Servlet

Servlet是服务端用来接收客户端请求的地方,这里很简单,直接接收并返回一个字符串

  1. @WebServlet(name="getMethod",value="/getMethod")  
  2. public class GetServlet extends HttpServlet {  
  3.     private static final long serialVersionUID = 1L;  
  4.   
  5.     @Override  
  6.     protected void doGet(HttpServletRequest req, HttpServletResponse resp)  
  7.             throws ServletException, IOException {  
  8.   
  9.         resp.getWriter().write("haha");  
  10.     }  
  11. }  
@WebServlet(name="getMethod",value="/getMethod")
public class GetServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		resp.getWriter().write("haha");
	}
}

从Servlet3.0开始,不再需要web.xml来定义Servlet,直接通过注解的方式,注意这里的getMethod要跟(1)中客户端定义的@GET中的名称一致,实现doGet表示接收Get请求,完后直接返回字符串"haha"

(4)访问请求并接收返回值

  1. private void getTest() {  
  2.         Call<String> call = RetroFactory.getStringService().getTest();  
  3.         call.enqueue(new Callback<String>() {  
  4.             @Override  
  5.             public void onResponse(Call<String> call, Response<String> response) {  
  6.                 if (response.isSuccessful() && response.errorBody() == null) {  
  7.                     Log.d(TAG, "str:" + response.body().toString());  
  8.                 } else {  
  9.                     Log.d(TAG, "error code:" + response.code());  
  10.                     Log.d(TAG, "error message:" + response.message());  
  11.                 }  
  12.             }  
  13.   
  14.             @Override  
  15.             public void onFailure(Call<String> call, Throwable t) {  
  16.                 Log.d(TAG, "error:" + t.getMessage());  
  17.             }  
  18.         });  
  19.     }  
private void getTest() {
        Call<String> call = RetroFactory.getStringService().getTest();
        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                if (response.isSuccessful() && response.errorBody() == null) {
                    Log.d(TAG, "str:" + response.body().toString());
                } else {
                    Log.d(TAG, "error code:" + response.code());
                    Log.d(TAG, "error message:" + response.message());
                }
            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {
                Log.d(TAG, "error:" + t.getMessage());
            }
        });
    }
首先,定义一个Call对象,这在(1)和(2)中已经详细说明,接下来调用enqueue方法,enqueue方法表示异步请求,如果是同步则调用execute,这里实现了CallBack的2个方法,onResponse和onFailure,在onResponse中,首先需要判断下请求是否成功以及是否有错误信息,这里需要特别注意,单纯从字面上理解,onFailure才是接收错误的地方,异常都应该在onFailure中处理,但其实并不完全是这样,这里的onFailure是接收网络的异常,比如说网络没有连接,或者连接超时,这时会进入onFailure方法,但如果是网络正常,但是返回不正常,是会进入onResponse方法,比如,我将(2)中的基础地址MyWeb改成MyWeb2,那这个url地址是不存在的,服务器会报404的错误,但是不会进onFailure,而是进了onResponse,代码中的error code会打印出404,而error message会打印出not found,这个解析错误的工作Retrofit已经帮我们做了,只是我们需要明确是进了onFailure还是onResponse方法。最后,当一切正常的时候,我们通过response的body,转换成string,就可以得到(3)中服务器返回的字符串"haha"

大家可以看到,一个基础的Retrofit请求是相当简单的,去除代码中的日志打印,寥寥几行代码就可以实现了,下面看看Post请求,也很简单:

(1)在RetrofitService接口中,增加要访问的POST方法

  1. @FormUrlEncoded  
  2. @POST("createUser")  
  3. Call<Void> createPerson(@Field("name") String name, @Field("age") String age);  
@FormUrlEncoded
@POST("createUser")
Call<Void> createPerson(@Field("name") String name, @Field("age") String age);
首先,第一个注解FormUrlEncoded表示对POST请求的参数进行编码,这是Retrofit2强制要求的,否则运行会报错,@POST注解表示这是一个POST方法,createUser是请求的名称,这里假设我只提交参数,不需要返回值,所以Call里的泛型是Void,完后方法里定义了2个参数注解,名字和年龄,表示客户端要传的2个参数

(2)定义Servlet

  1. @WebServlet(name="createUser",value="/createUser")  
  2. public class CreateUserServlet extends HttpServlet {  
  3.     private static final long serialVersionUID = 1L;  
  4.   
  5.     @Override  
  6.     protected void doPost(HttpServletRequest req, HttpServletResponse resp)  
  7.             throws ServletException, IOException {  
  8.   
  9.         String name = req.getParameter("name");  
  10.         String age = req.getParameter("age");  
  11.           
  12.         System.out.println("name:" + name);  
  13.         System.out.println("age:" + age);  
  14.     }  
  15. }  
@WebServlet(name="createUser",value="/createUser")
public class CreateUserServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		String name = req.getParameter("name");
		String age = req.getParameter("age");
		
		System.out.println("name:" + name);
		System.out.println("age:" + age);
	}
}
这里基本和上面的GET方法类似,只不过为了接收POST请求,这里实现了doPost方法,完后接收客户端传递过来的参数并打印
(3)访问请求

  1. private void createPerson() {  
  2.         Call<Void> call = RetroFactory.getStringService().createPerson("gesanri""10");  
  3.         call.enqueue(new Callback<Void>() {  
  4.             @Override  
  5.             public void onResponse(Call<Void> call, Response<Void> response) {  
  6.             }  
  7.   
  8.             @Override  
  9.             public void onFailure(Call<Void> call, Throwable t) {  
  10.             }  
  11.         });  
  12.     }  
private void createPerson() {
        Call<Void> call = RetroFactory.getStringService().createPerson("gesanri", "10");
        call.enqueue(new Callback<Void>() {
            @Override
            public void onResponse(Call<Void> call, Response<Void> response) {
            }

            @Override
            public void onFailure(Call<Void> call, Throwable t) {
            }
        });
    }
调用(1)中定义的方法createPerson,完后传了2个参数,最后调用enqueue方法启动网络访问,完后服务器的Console中就可以看到打印

name:gesanri
age:10

可以看到,不管是GET还是POST请求,Retrofit都可以非常简单的处理。

下面我们来考虑一个稍微复杂点的情况,更接近于真实应用中的场景,首先,访问服务器接口都是有返回的,而且不会返回一个简单的字符串,一般是一个json格式。我们假设服务器返回的结果统一为如下格式:

{"code":0, "message":"123", "data:":泛型}
其中code是一个int,用0表示成功,非0表示失败,message是成功或失败的提示信息,而data则是返回的实际结果,可以为任意类型

现在我们来定义一个POST请求,模拟客户端请求服务器的数据,返回一个Person对象列表,Person有2个参数,姓名和年龄,客户端打印所有Person信息并且在界面显示第一个Person的信息,返回成功的示例如下:

{"code":0, "message":"获取用户成功!", "data:":[{"name":"张三", "age":23},{"name":"李四", "age":28}]}

(1) 定义实体类Person

  1. public class Person implements Serializable{  
  2.     private String name;  
  3.     private int age;  
  4.   
  5.     public String getName() {  
  6.         return name;  
  7.     }  
  8.   
  9.     public void setName(String name) {  
  10.         this.name = name;  
  11.     }  
  12.   
  13.     public int getAge() {  
  14.         return age;  
  15.     }  
  16.   
  17.     public void setAge(int age) {  
  18.         this.age = age;  
  19.     }  
  20. }  
public class Person implements Serializable{
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
这就是上面提到的Person实体类对象,定义get和set方法,很简单

(2) 定义实体类BaseEntity

  1. public class BaseEntity<E> implements Serializable {  
  2.     private int code;  
  3.     private String message;  
  4.     private E data;  
  5.   
  6.     public int getCode() {  
  7.         return code;  
  8.     }  
  9.   
  10.     public void setCode(int code) {  
  11.         this.code = code;  
  12.     }  
  13.   
  14.     public String getMessage() {  
  15.         return message;  
  16.     }  
  17.   
  18.     public void setMessage(String message) {  
  19.         this.message = message;  
  20.     }  
  21.   
  22.     public E getData() {  
  23.         return data;  
  24.     }  
  25.   
  26.     public void setData(E data) {  
  27.         this.data = data;  
  28.     }  
  29. }  
public class BaseEntity<E> implements Serializable {
    private int code;
    private String message;
    private E data;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public E getData() {
        return data;
    }

    public void setData(E data) {
        this.data = data;
    }
}
这个BaseEntity是我们通用的服务器返回值对象,也就是上面说的json对象,注意这里用到了泛型,因为code和message是固定的,而data对象是不固定的,服务器可以返回任意类型的data对象

(3)定义POST请求

在上面提到的RetrofitService中,增加一个POST方法

  1. @FormUrlEncoded  
  2. @POST("getUsers")  
  3. Call<BaseEntity<List<Person>>> getUsers(@FieldMap Map<String, String> map);  
@FormUrlEncoded
@POST("getUsers")
Call<BaseEntity<List<Person>>> getUsers(@FieldMap Map<String, String> map);
这里的2个注解上面已经解释过,主要看getUsers方法,首先,它的参数我们用了一个FieldMap,这里传了一个Map,在上面介绍基本POST请求中,我们是将参数一个接一个的加在参数列表里,这在参数较少的时候可以,但如果参数比较多的话,接一排参数既不美观也容易出错,用FieldMap是不错的选择,另外Retrofit2也可以传一个Body,不过这种需要定义一个实体类,用来包含所有的参数对象,所以综合起来还是选用FieldMap。再来看返回值,Call对象里面的泛型为<BaseEntity<List<Person>>>,也就是我们上面提到的,通用网络返回值类型,其中data参数的类型为List<Person>

(4)提供新的生成RetrofitService的方法

  1. private static Retrofit jsonRetrofit = new Retrofit.Builder()  
  2.             .baseUrl(baseUrl)  
  3.             .addConverterFactory(JacksonConverterFactory.create())  
  4.             .build();  
  5.   
  6.     public static RetrofitService getJsonService() {  
  7.         RetrofitService service = jsonRetrofit.create(RetrofitService.class);  
  8.         return service;  
  9.     }  
private static Retrofit jsonRetrofit = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(JacksonConverterFactory.create())
            .build();

    public static RetrofitService getJsonService() {
        RetrofitService service = jsonRetrofit.create(RetrofitService.class);
        return service;
    }
在介绍Get请求的时候,我们用到了ScalarsConverterFactory,它可以转换成String类型,但现在我们的服务器通用返回类型是json格式,所以我们需要一个新的能转换json类型的转换类,可以选用gson或jackson,在数据量较大的情况下,gson的效率相比jackson还是有较大差距,这里选用jackson,所以重新生成一个Retrofit对象,用到JacksonConverterFactory的转换类,并返回一个新的RetrofitService对象,注意这里要记得在build.gradle中引入依赖compile 'com.squareup.retrofit2:converter-jackson:2.0.0'

(5)定义处理网络请求的公共类

  1. public class BaseTask<T> {  
  2.     private Call<BaseEntity<T>> mCall;  
  3.     private Context mContext;  
  4.     private final int SUCCESS = 0;  
  5.     private final String TAG = "response";  
  6.   
  7.     public BaseTask(Context context, Call call) {  
  8.         mCall = call;  
  9.         mContext = context;  
  10.     }  
  11.   
  12.     public void handleResponse(final ResponseListener listener) {  
  13.         mCall.enqueue(new Callback<BaseEntity<T>>() {  
  14.             @Override  
  15.             public void onResponse(Call<BaseEntity<T>> call, Response<BaseEntity<T>> response) {  
  16.                 if (response.isSuccessful() && response.errorBody() == null) {  
  17.                     if (response.body().getCode() == SUCCESS) {  
  18.                         listener.onSuccess((T) response.body().getData());  
  19.                     } else {  
  20.                         Toast.makeText(mContext, response.body().getMessage(), Toast.LENGTH_LONG).show();  
  21.   
  22.                         listener.onFail();  
  23.                     }  
  24.                 } else {  
  25.                     Log.d(TAG, "error code:" + response.code());  
  26.                     Log.d(TAG, "error message:" + response.message());  
  27.   
  28.                     Toast.makeText(mContext, "网络请求返回异常!", Toast.LENGTH_LONG).show();  
  29.                 }  
  30.             }  
  31.   
  32.             @Override  
  33.             public void onFailure(Call<BaseEntity<T>> call, Throwable t) {  
  34.                 Log.d(TAG, "error:" + t.getMessage());  
  35.   
  36.                 Toast.makeText(mContext, "网络请求出现异常!", Toast.LENGTH_LONG).show();  
  37.             }  
  38.         });  
  39.     }  
  40. 。  
  41.     public interface ResponseListener<T> {  
  42.         void onSuccess(T t);  
  43.   
  44.         void onFail();  
  45.     }  
  46. }  
public class BaseTask<T> {
    private Call<BaseEntity<T>> mCall;
    private Context mContext;
    private final int SUCCESS = 0;
    private final String TAG = "response";

    public BaseTask(Context context, Call call) {
        mCall = call;
        mContext = context;
    }

    public void handleResponse(final ResponseListener listener) {
        mCall.enqueue(new Callback<BaseEntity<T>>() {
            @Override
            public void onResponse(Call<BaseEntity<T>> call, Response<BaseEntity<T>> response) {
                if (response.isSuccessful() && response.errorBody() == null) {
                    if (response.body().getCode() == SUCCESS) {
                        listener.onSuccess((T) response.body().getData());
                    } else {
                        Toast.makeText(mContext, response.body().getMessage(), Toast.LENGTH_LONG).show();

                        listener.onFail();
                    }
                } else {
                    Log.d(TAG, "error code:" + response.code());
                    Log.d(TAG, "error message:" + response.message());

                    Toast.makeText(mContext, "网络请求返回异常!", Toast.LENGTH_LONG).show();
                }
            }

            @Override
            public void onFailure(Call<BaseEntity<T>> call, Throwable t) {
                Log.d(TAG, "error:" + t.getMessage());

                Toast.makeText(mContext, "网络请求出现异常!", Toast.LENGTH_LONG).show();
            }
        });
    }
。
    public interface ResponseListener<T> {
        void onSuccess(T t);

        void onFail();
    }
}
Retrofit提供的网络请求的回调格式是通用的,所以我们可以将其抽出来,写在一个类中,避免所有的网络请求都去写一些重复的代码,这里的泛型T在这个接口中就是对应的List<Person>,我们真正需要处理的也就是这个对象,code和message都是辅助的功能,可以在公共类中处理。

这里定义了一个内部接口ResponseListener,它包含两个方法,onSuccess和onFail,对应在网络请求成功的前提下,数据的获取成功和失败,比如说,我这里去请求获取用户数据,如果获取成功,就进入onSuccess方法,如果用户不存在,则进入onFail方法。

在构造函数中,我们接收Call对象,这里将需要访问的网络请求传入,完后在handleResponse方法中,用call对象来请求网络,并接收和处理返回值,当code为0时,表示成功,回调onSuccess方法,否则回调onFail方法,至于其它的网络方面的异常情况,都可以在这里处理

(6)定义Servlet

  1. @WebServlet(name="getUsers",value="/getUsers")  
  2. public class GetUsersServlet extends HttpServlet {  
  3.     private static final long serialVersionUID = 1L;  
  4.   
  5.     @Override  
  6.     protected void doPost(HttpServletRequest req, HttpServletResponse resp)  
  7.             throws ServletException, IOException {  
  8.   
  9.         System.out.println("id:" + req.getParameter("id"));  
  10.         System.out.println("name:" + req.getParameter("name"));  
  11.           
  12.         resp.setCharacterEncoding("utf-8");   
  13.         resp.getWriter().write("{\"code\":1, \"message\":\"获取用户不存在!\", \"data\":null}");  
  14.         //resp.getWriter().write("{\"code\":0, \"message\":\"获取用户成功!\", \"data\":[{\"name\":\"张三\", \"age\":23},{\"name\":\"李四\", \"age\":28}]}");  
  15.     }  
  16. }  
@WebServlet(name="getUsers",value="/getUsers")
public class GetUsersServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		System.out.println("id:" + req.getParameter("id"));
		System.out.println("name:" + req.getParameter("name"));
		
		resp.setCharacterEncoding("utf-8"); 
		resp.getWriter().write("{\"code\":1, \"message\":\"获取用户不存在!\", \"data\":null}");
		//resp.getWriter().write("{\"code\":0, \"message\":\"获取用户成功!\", \"data\":[{\"name\":\"张三\", \"age\":23},{\"name\":\"李四\", \"age\":28}]}");
	}
}
这里接收客户端传来的Map参数,打印出来,这里就不查询数据库了,假设得到参数后,就返回结果,直接将json对象返回给客户端

(7)客户端访问网络

  1. private void getUsers() {  
  2.         Map<String, String> map = new HashMap<String, String>();  
  3.         map.put("id""123");  
  4.         map.put("name""gesanri");  
  5.   
  6.         new BaseTask<List<Person>>(this, RetroFactory.getJsonService().getUsers(map)).handleResponse(new BaseTask.ResponseListener<List<Person>>() {  
  7.                 @Override  
  8.                 public void onSuccess(List<Person> o) {  
  9.                     for (int i = 0; i < o.size(); i++) {  
  10.                         Person person = o.get(i);  
  11.                         Log.d(TAG, "name:" + person.getName());  
  12.                         Log.d(TAG, "age:" + person.getAge());  
  13.                     }  
  14.                 }  
  15.   
  16.                 @Override  
  17.                 public void onFail() {  
  18.                 }  
  19.         });  
  20.     }  
private void getUsers() {
        Map<String, String> map = new HashMap<String, String>();
        map.put("id", "123");
        map.put("name", "gesanri");

        new BaseTask<List<Person>>(this, RetroFactory.getJsonService().getUsers(map)).handleResponse(new BaseTask.ResponseListener<List<Person>>() {
                @Override
                public void onSuccess(List<Person> o) {
                    for (int i = 0; i < o.size(); i++) {
                        Person person = o.get(i);
                        Log.d(TAG, "name:" + person.getName());
                        Log.d(TAG, "age:" + person.getAge());
                    }
                }

                @Override
                public void onFail() {
                }
        });
    }
可以看到,通过上面的封装,客户端的工作就轻松了很多,只需要新建一个BaseTask对象,并调用handleResponse方法来接收回调即可。

实际项目中,情况可能远比上面复杂,这里主要起到一个抛砖引玉的作用,万事开头难,有了基本的思路,后面的工作就好办了。

源码下载



4
0
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值