【Android - 架構】之Retrofit的使用

來源:互聯網
上載者:User

標籤:make   種類   資料   adapter   格式   pid   code   反射機制   代碼   

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": "[email protected]",  "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的工作原理。

【Android - 架構】之Retrofit的使用

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.