Retrofix2使用详解

来源:互联网 发布:单例设计模式php 编辑:程序博客网 时间:2024/06/11 02:16

简介

A type-safe HTTP client for Android and Java,一种类型安全的Http联网构架。
出品公司Square,项目地址。


项目导入

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

需android 2.3以上


网络请求步骤

1.创建一个接口,设置请求的类型与参数

接口中创建一个方法,返回一个Call对象,泛型是网络请求返回的数据转换成实体类对象集合,用注解来确定请求是GET还是POST

public interface GitHubService {  @GET("users/{user}/repos")  Call<List<Repo>> listRepos(@Path("user") String user);}

2.创建一个Retrofix对象

baseUrl()方法一般放置地址的主体部分,以/结尾

Retrofit retrofit = new Retrofit.Builder()    .baseUrl("https://api.github.com/")    .build();

3.生成一个接口实现对象

调用retrofit的create方法,传入第一步创建的请求接口,生成一个请求接口的实现对象

GitHubService service = retrofit.create(GitHubService.class);

4.生成一个Call实例对象

调用请求接口的对象方法可以生成一个Call对象实例,到这一步就可以得到最后的请求地址了。

Call<List<Repo>> repos = service.listRepos("octocat");

这是一个Get请求,传的是Path的参数,所以最后的请求地址拼接起来就是https://api.github.com/users/octocat/repos

5.发送请求

调用Call实例对象的方法就可以发送联网请求了,有两种方式,一种是同步请求,一种是异步请求。

同步请求

调用execute()方法,需要捕捉异常,返回结果的响应体,泛型为接口类中定义方法里返回类的泛型一样

     try {       Response<List<Repo>> response= repos.execute();     } catch (IOException e) {           e.printStackTrace();        }

该方法源码解释

/**   * Synchronously send the request and return its response.   *   * @throws IOException if a problem occurred talking to the server.   * @throws RuntimeException (and subclasses) if an unexpected error occurs creating the request   * or decoding the response.   */  Response<T> execute() throws IOException;

异步请求

调用enqueue()方法,参数是一个回调,实现两个方法,一是请求成功调用的onResponse,可以得到响应体;二是请求失败调用的onFailure,可以查看异常。

  repos.enqueue(new Callback<List<Repo>>() {                @Override                public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {                //调用响应的body()方法,即可得到返回的数据                    List<Repo> repoList=response.body();                }                @Override                public void onFailure(Call<String> call, Throwable t) {                //t即是异常类,可以打印异常信息                      t.printStackTrace();                }            });

该方法源码解释

/**   * Asynchronously send the request and notify {@code callback} of its response or if an error   * occurred talking to the server, creating the request, or processing the response.   */  void enqueue(Callback<T> callback);

6.添加返回数据自动转换功能

上述第五步中有一句关键代码,实现了返回数据自动转换成实体类集合的过程,简单快捷。

 List<Repo> repoList=response.body();

要实现该功能,有两步。
第一,添加Gson依赖,注意对应当前Retrofix版本

com.squareup.retrofit2:converter-gson:2.2.0

第二,构建Retrofix时,加上这句关键代码

Retrofit retrofit = new Retrofit.Builder()    .baseUrl("https://api.github.com")    //关键代码,添加转换工厂    .addConverterFactory(GsonConverterFactory.create())    .build();

也可以添加其它转换工厂,注意要在后面加上:2.2.0,当前的版本号

  • 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 //String类型的支持

所有请求方式的注解及参数类型关键字

retrofix内置有五个请求方式注解 GET, POST, PUT, DELETE, and HEAD.
其中GET请求与POST请求是Http经常使用的.

GET & POST请求方式传参数

以GET为例进行说明

@GET("group/{id}/users")Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);

参数说明:
@Path:请求URL的字符替代,将网址中的{id}替换成自己传递的id
@Query:要传递的参数
如Call方法为:groupList(“9527”,”order by age desc”);
则最后发送的请求网址为:
https://api.github.com/group/9527/users?sort=order by age desc
注意@Path只能用于地址的通配,如要传参数必须用@Query

传递多个参数

要实现的请求地址如下:
https://api.github.com/group/9527/users?name=android&sort=order by age desc
方法1:添加多个@Query注解的参数

@GET("group/{id}/users")Call<List<User>> groupList(@Path("id") int groupId, @Query("name") String name);@Query("sort") String sort);

方法2:添加一个@QueryMap注解参数,里面包含多个@Query注解的参数

@GET("group/{id}/users")Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);Map<String,String> options=new HashMap();options.put("name","android");options.put("sort","order by age desc");Call call=service.groupList("9527",options);

另也可以用同一个key值来传递多个数据

Map<String,List<String>> options=new HashMap();list.add("android");list.add("IOS");options.put("name",list);

得到的URL地址为

https://api.github.com/group/9527/users?name=android&name=IOS&sort=order by age desc

方法3:用@Body直接添加一个实体类对象,只适用于POST方式

@POST("users/new")Call<User> createUser(@Body User user);

Form-encoded(URL编码)

可以指定http请求URL编码,application/x-www-form-urlencoded,传递表单数据是键值对形式,适用于POST请求方式

@FormUrlEncoded@POST("user/edit")Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);

在带有@FormURLEncode的POST请求中,只能用@Filed来传递参数,用法与@Query一致.

上传文件

指定编码方式为@Multipart,即是multipart/form-data,只有这种编码格式才可上传文件,可以用@PUT,也可以用@POST方式,参数只能用@Part注解

@Multipart@PUT("user/photo")Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);

添加表头@Headers

//单项表头@Headers("Cache-Control: max-age=640000")@GET("widget/list")Call<List<Widget>> widgetList();//多项表头@Headers({    "Accept: application/vnd.github.v3.full+json",    "User-Agent: Retrofit-Sample-App"})@GET("users/{username}")Call<User> getUser(@Path("username") String username);//动态表头@GET("user")Call<User> getUser(@Header("Authorization") String authorization)

用retrofix2实现网络请求案例

1.构建一个小型服务器

使用eclipse创建一个web server项目HttpRequestDemo,然后创建一个继承HttpServlet的实现类。
这是一个用户登录的验证服务,正确的用户名qq123456,密码666666,返回一个json字符串。
这里处理的是GET请求,post请求用的也是一模一样的代码。

public class WebServerDemo extends HttpServlet {    private static final long serialVersionUID = 29328731L;    public void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        String name =request.getParameter("name");        String password=request.getParameter("pwd");        String result="";        String msg="";        if(!"qq123456".equalsIgnoreCase(name)){            result="fail";            msg="user name is fail";        }else{            if(!"666666".equals(password)){                result="fail";                msg="password is fail";            }else{                result="success";                msg="mession success";            }        }        String returnJson="{\"result\":\""+result+"\",\"msg\":\""+msg+"\"}";        response.setContentType("text/html");        PrintWriter out = response.getWriter();        out.println("<HTML>");        out.println("  <HEAD><TITLE>login page</TITLE></HEAD>");        out.println("  <BODY>");        out.println(returnJson);        out.println("  </BODY>");        out.println("</HTML>");        out.flush();        out.close();    }}

在WebRoot/WEB_INF/web.xml里配置servlet与外部访问的路径地址

<?xml version="1.0" encoding="UTF-8"?>....  <!--配置servlet对应的类名与路径名 -->  <servlet>    <servlet-name>WebServerDemo</servlet-name>    <servlet-class>server.WebServerDemo</servlet-class>  </servlet>    <!--将servlet与web对应 -->  <servlet-mapping>    <servlet-name>WebServerDemo</servlet-name>    <!--/login即是外部访问的路径-->    <url-pattern>/login</url-pattern>  </servlet-mapping>    .....</web-app>

最后将HttpRequestDemo部署至Tomcat中即可完成这个用户登录验证的小型服务器。 下载地址

2.发送登录请求

首先创建一个as自带的loginactivity登录界面,然后简化一些代码,减少登录过程中的一些验证。
然后运用上文步骤来发送联网请求。

public interface LoginService {        @GET("{filepath}/login")        Call<String> login4Path(@Path("filepath") String filepath);    }
//注意这里的baseUrl是本机的IP地址,端口是tomcat默认的8080//这里并没有添加任何转换器mRetrofit=new Retrofit.Builder()                .baseUrl("http://192.168.0.199:8080/")                .build();
  Call<String> call=mRetrofit.create(LoginService.class).login4Path("HttpRequestDemo"); call.enqueue(new Callback<String>() {            @Override            public void onResponse(Call<String> call, Response<String> response) {                String resultStr=response.body();                mResultTextView.setText(resultStr);                showProgress(false);                if(resultStr.contains("fail")){                    mPasswordView.requestFocus();                    Toast.makeText(getApplicationContext(),"登录失败",Toast.LENGTH_SHORT).show();                }else if(resultStr.contains("success")){                    startActivity(new Intent(RetrofixDemoActivity.this,LitepalDemoActivity.class));                }            }            @Override            public void onFailure(Call<String> call, Throwable t) {                t.printStackTrace();            }        });

点击登录按钮,程序崩溃,一直报如下异常,无法创建一个转换器,LoginService.login4Path这个方法的结果无法转换成String.

java.lang.IllegalArgumentException: Unable to create converter for class java.lang.String for method LoginService.login4Path at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:751) at retrofit2.ServiceMethod$Builder.createResponseConverter(ServiceMethod.java:737) at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:168) at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:169) at retrofit2.Retrofit$1.invoke(Retrofit.java:146) at $Proxy0.login4Path(Native Method)

这个错误的原因在于如果构建时retrofix实例时没有添加转换器则Call对象的泛型只能使用RequestBody,这是比较容易忽略的异常。
将所有Call里的泛型换成RequestBody类,然后再运行程序,登录成功会跳转至另一个界面。

Call<ResponseBody> call=service.login4Path("HttpRequestDemo");        call.enqueue(new Callback<ResponseBody>() {            @Override            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {                ResponseBody body=response.body();                String resultStr= "";                try {                    resultStr = body.string();                } catch (IOException e) {                    e.printStackTrace();                }......

这里不管输入什么都是返回用户名错误,因为这个请求并没有传参数,只是简单地返回了用户名不相同时的result与msg.
这里写图片描述
用一个Textview来显示服务器返回的所有数据。

3.用Post方式进行请求

 public interface LoginService {        @POST("{filepath}/login")        Call<ResponseBody> login4Path(@Path("filepath") String filePath,                                      @Query("name")String name,                                      @Query("pwd")String pwd);    }
  String email = mEmailView.getText().toString();   String password = mPasswordView.getText().toString(); LoginService service=mRetrofit.create(LoginService.class); Call<ResponseBody>call= service.login4Path("HttpRequestDemo",email,password);  call.enqueue(new Callback<ResponseBody>() {...}

运行结果:
这里写图片描述

demo地址

0 0
原创粉丝点击