【Android - 框架】之Retrofit的使用
Retrofit是Square公司发布的一个可以应用在Android和Java中的Http客户端访问框架,其底层应用的是OkHttp。
在这个帖子中,我们以下面这个Http请求为例:
https://api.github.com/users/basil2style
其请求结果(JSON)如下所示:
{ "login": "basil2style", "id": 1285344, "avatar_url": "https://avatars.githubusercontent.com/u/1285344?v=3", "gravatar_id": "", "url": "https://api.github.com/users/basil2style", "html_url": "https://github.com/basil2style", "followers_url": "https://api.github.com/users/basil2style/followers", "following_url": "https://api.github.com/users/basil2style/following{/other_user}", "gists_url": "https://api.github.com/users/basil2style/gists{/gist_id}", "starred_url": "https://api.github.com/users/basil2style/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/basil2style/subscriptions", "organizations_url": "https://api.github.com/users/basil2style/orgs", "repos_url": "https://api.github.com/users/basil2style/repos", "events_url": "https://api.github.com/users/basil2style/events{/privacy}", "received_events_url": "https://api.github.com/users/basil2style/received_events", "type": "User", "site_admin": false, "name": "Basil", "company": "MakeInfo", "blog": "http://www.themakeinfo.com", "location": "Toronto,Canada", "email": "basiltalias92@gmail.com", "hireable": true, "bio": "Developer | Marketer | Reader | Cinephile | Entrepreneur", "public_repos": 35, "public_gists": 4, "followers": 64, "following": 155, "created_at": "2011-12-26T00:17:22Z", "updated_at": "2016-11-12T00:58:15Z" }
接下来我们从Retrofit的用法到原理,来介绍一下这个框架。
一、Retrofit的用法:
0、配置Retrofit的开发环境:
我们在使用Retrofit之前需要先导入Retrofit的包,本DEMO是在Android Studio中开发的,因此只需要在gradle文件中导入依赖即可。下面是Retrofit的依赖:
compile 'com.squareup.retrofit2:retrofit:2.1.0'
另外,我们从网络中获取到的数据的格式是不同的,可能是JSON/GSON格式,也可能是Jackson、Wire等其他格式,我们需要在后面的编码中用到一个格式转化工厂ConverterFactory,因此我们还需要导入一些有关格式的依赖。本DEMO中使用的是JSON/GSON格式,因此导入相关依赖如下:
compile 'com.squareup.retrofit2:converter-gson:2.1.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
至此,Retrofit的开发环境搭建就完成了。
1、创建一个从网络中获取数据的Service接口:
Retrofit使用动态代理的方式,将一个网络请求转换成一个接口,用户只需要调用一个接口就可以进行网络访问。这个接口的代码如下:
public interface RetrofitService { @GET("/users/basil2style") Call<InfoData> getInfoData(); }
从上面这段代码中可以看出来, @GET 标签中的值只是这个请求的一部分。一般我们在进行网络请求的时候,大多数请求都是从一个服务器发送的数据,因此我们可以对这些请求的根URL进行统一的管理。在这个DEMO中,我把网络请求的根URL放在SharedData类中:
public class SharedData { /** * Base Url */ public static final String BASE_URL = "https://api.github.com"; }
和其他网络请求相同,Retrofit可以通过 @GET 和 @POST 两种方法进行网络请求,这些请求有时候需要携带参数,有时候不需要,上面的getInfoData()方法就是一个不携带任何参数的网络请求方法。当然,这里还需要创建一个Bean类InfoData,用来存储从网络中获取到的数据:
public class InfoData { private String login; private int id; private String avatarUrl; private String gravatarId; private String url; private String htmlUrl; private String followersUrl; private String followingUrl; private String gistsUrl; private String starredUrl; private String subscriptionsUrl; private String organizationsUrl; private String reposUrl; private String eventsUrl; private String receivedEventsUrl; private String type; private boolean siteAdmin; private String name; private String company; private String blog; private String location; private String email; private boolean hireable; private String bio; private int publicRepos; private int publicGists; private int followers; private int following; private String createdAt; private String updatedAt; // Getter、Setter方法 }
再来介绍一下Call。Call可以用来发送一个请求,Call对象中有两个方法:enqueue()和execute(),前者用来发送一个异步请求,后者又来发送一个同步请求,下面会有代码介绍。
2、初始化Retrofit对象:
创建Retrofit对象的代码如下:
Retrofit retrofit = new Retrofit.Builder() .baseUrl(SharedData.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build();
可以看到,Retrofit对象是通过Retrofit.Builder内部类创建出来的,baseUrl是需要访问的根URL;由于请求的数据是JSON格式的,我们可以使用一个和JSON有关的转换工厂来处理这些数据,这里用到的是和JSON差不多的GSON转化工厂,即GsonConverterFactory。
3、调用Retrofit对象进行网络访问:
获取到Retrofit对象后,我们通过这个对象获取到网络请求接口RetrofitService,再调用其中的getInfoData()方法获取到网络数据即可。具体代码如下:
RetrofitService service = retrofit.create(RetrofitService.class); Call<InfoData> call = service.getInfoData(); call.enqueue(new Callback<InfoData>() { @Override public void onResponse(Call<InfoData> call, Response<InfoData> response) { InfoData data = response.body(); Message message = Message.obtain(); message.what = 1; message.obj = data; handler.sendMessage(message); } @Override public void onFailure(Call<InfoData> call, Throwable t) { handler.sendEmptyMessage(0); } });
这里还需要说明一下,Retrofit不像Volley可以直接把异步数据拉回到主线程,Retrofit中Callback类的onResponse()方法仍然是在异步线程中的,如果我们要将数据拿到主线程,需要使用AsyncTask或Handler,本DEMO中使用的是Handler,以下是Handler中的代码:
private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: InfoData data = (InfoData) msg.obj; Toast.makeText(MainActivity.this, data.getBlog(), Toast.LENGTH_SHORT).show(); break; case 0: Toast.makeText(MainActivity.this, "获取数据失败", Toast.LENGTH_SHORT).show(); break; } } };
到此为止Retrofit访问网络获取数据的DEMO就完成了,运行结果如下图所示:
二、RetrofitService接口方法种类:
上面的RetrofitService接口中的getInfoData()方法只是一个没有携带任何参数的GET请求,我们在访问网络数据的时候有时需要使用POST请求,并且可能会携带一些参数等,下面来逐个介绍一下这些情况下接口方法的编写方式。
特别说明:下面访问的接口和上面DEMO中的接口没有任何关系,两两之间也没有任何关系!
1、没有参数的GET请求:
@GET("users/list")
Call<List<User>> getUserList();
2、有参数的GET请求:
(1)第一种方式:在GET标签中添加参数:
@GET("users/list?sort=desc")
Call<List<User>> getUserList();
(2)第二种方式:在方法参数中设置参数:
@GET("users/list")
Call<List<User>> getUserList(@Query("sort") String sort);
3、在GET标签中设置路径参数:
@GET("group/{id}/users") Call<List<User>> groupList(@Path("id") int groupId);
4、传入参数列表:
@GET("group/{id}/users") Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
5、POST方式请求:
@POST("users/new")
Call<User> createUser(@Body User user);
三、Retrofit原理:
Retrofit用注解来描述一个网络请求,将一个网络请求抽象成一个接口,然后使用Java动态代理的方式动态的将这个接口“翻译”成一个网络请求,最后调用OkHttp去执行这个请求。
Retrofit中的动态代理主要体现在下面这行代码:
RetrofitService service = retrofit.create(RetrofitService.class);
create()方法的源码如下:
/** Create an implementation of the API defined by the {@code service} interface. */ public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); public Object invoke(Object proxy, Method method, Object... args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } ServiceMethod serviceMethod = loadServiceMethod(method); OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } }); }
可见,所谓的动态代理,就是给程序员一种可能:在执行某个操作(如打开某个Class)之前,插入一段你想要执行的代码。比如你在执行某个操作之前判断用户是否已经登录,等。
当外界(Activity)通过create()方法打开这个接口并调用其中的方法时,底层就调用了上面这段代码中的invode()方法。这个方法是通过Java反射机制获取到接口中的getInfoData()方法,用这个方法作为参数创建一个ServiceMethod对象,最后使用OkHttp的API调用进行网络访问。
总结一下:Retrofit使用注解的方式将一个网络请求封装成一个接口,然后使用动态代理的方法,在插入的代码中通过反射机制找到接口中的方法,封装到OkHttp中进行网络访问,最后对网络访问得到的Call对象进行enqueue()或execute()操作,在回调的方法中处理网络获取到的数据。
以上就是Retrofit的工作原理。
posted on 2016-12-22 13:24 ITGungnir 阅读(1073) 评论(0) 编辑 收藏 举报