Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Retrofit攻略

Retrofit攻略

編輯:關於Android編程

最開始使用AndroidStudio的時候,各種不適應,各種懷戀Eclipse,寫了幾千行代碼勉強熟悉了AndroidStudio後,感覺AndroidStudio不要太棒了。學習Retrofit也是這樣,遇到麻煩就想去用以前用過的框架,熟悉了過後我現在連吃個湯圓都喜歡串著吃,囧….

像OkHttp一樣使用

當然實際的項目開發中,不可能給我們時間慢慢去適應、去學習,要考慮技術風險和學習成本、團隊共用。所以我才覺得Retrofit這一點好貼心,基於OkHttp實現,也就是說我當我遇到我不知道該怎麼實現的功能的時候,可以無縫切換到OkHttp上面實現需求。
看看下面的代碼,同樣申明一個請求call,同樣的異步call.enqueue,同樣的回調Callback,甚至連mDatas = new Gson().fromJson(res, new TypeToken>Gson解析都是從前的味道。但是我們已經開始在使用Retrofit了,不知不覺,潛移默化中已經前進了一小步。

    private void initBtnBasic() {
        mBtnBasic.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Retrofit build = new Retrofit.Builder().baseUrl("http://zhuangbi.info/").build();
                Call call = build.create(SearchApi.class).basicSearch("牛逼");
                call.enqueue(new Callback() {
                    @Override
                    public void onResponse(Call call, Response response) {
                        try {
                            String res = response.body().string();
                            mDatas = new Gson().fromJson(res, new TypeToken>() {
                            }.getType());
                            for (SearchImage searchImage : mDatas) {
                                Log.d(TAG, "onResponse: " + searchImage.toString());
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onFailure(Call call, Throwable t) {
                        Log.e(TAG, "onFailure: ", t);
                    }
                });
            }
        });
    }
public interface SearchApi {

    @GET("search")
    Call basicSearch(@Query("q") String qTitle);
}

當然,100%的雷同也不好嘛,來了一趟總得學點東西,一下接觸陌生知識太多,跨步前進容易扯到蛋不可取,原地踏步也不爽,小步慢行。
這裡我們構建請求URL的事後使用了一個接口SearchApi
Retrofit

Retrofit.Builder().baseUrl("http://zhuangbi.info/").build().create(SearchApi.class).basicSearch("牛逼");

OkHttp

new Request.Builder()
                .url("http://zhuangbi.info/search?q=" + getString("牛逼"))
                .build();

聰明的我們一看就知道

 @GET("search")
    Call basicSearch(@Query("q") String qTitle);

這裡的@GET("search")表示使用get請求,同時search是get命令路徑的一部分

Path

當然,你也可以把他寫到參數裡面,Like This

   @GET("{path}")
    Call basicSearch(@Path("path") String path, @Query("q") String qTitle);

這樣用就行了:

Retrofit.Builder().baseUrl("http://zhuangbi.info/").build().create(SearchApi.class).basicSearch("search","牛逼");

Query & QueryMap

@Query("q") String qTitle

Query是查詢參數,就相當於http://zhuangbi.info/search?q=牛逼
?q=牛逼 部分

也許你會想如果要查詢很多參數,豈不是也要配置很多函數參數裡面,然後讓函數變得巨長
當然不用@QueryMap為你排憂解難

    @GET("search")
    Call basicSearch(@QueryMap Map map);

就這麼簡單,輕松進入到Retrofit的地盤

組裝轉換器—Gson工具升級

    private void initTrans() {
        final Retrofit build = new Retrofit.Builder()
                .baseUrl("http://zhuangbi.info/")
                .addConverterFactory(GsonConverterFactory.create())//添加轉換器
                .build();

        mBtnTrans.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Call> call = build.create(SearchApi.class).transSearch("牛逼");
                call.enqueue(new Callback>() {
                    @Override
                    public void onResponse(Call> call, Response> response) {
                        mDatas.clear();
                        //使用轉換器,內部已經幫我們實現轉換,省去了new Gson().form(jsonStr,new TypeToken<>())的過程
                        mDatas = response.body();
                        for (SearchImage searchImage : mDatas) {
                            Log.d(TAG, "onResponse: " + searchImage.toString());
                        }
                    }

                    @Override
                    public void onFailure(Call> call, Throwable t) {
                        Log.e(TAG, "onFailure: ", t);
                    }
                });
            }
        });
    }

所謂轉換器,其實再剛才的代碼基礎上也沒多大改動嘛,不過是再構造的時候加了一句.addConverterFactory(GsonConverterFactory.create())//添加轉換器
Callback裡面的json轉換不用再使用

                Gson gson = new Gson();
                mDatas = gson.fromJson(resStr, new TypeToken>() {
                }.getType());

直接使用mDatas = response.body();就可以獲取,當然請求的函數泛型也要改寫

    @GET("search")
    Call> transSearch(@Query("q") String qTitle);

省去了在回調函數new Gson的干擾,代碼變得跟簡潔。
當然,這裡使用的是Gson,如果你需要JackSon,FastJson選擇合適的解析器傳遞進去就行了。
如果,你需要自定義解析規則,也是沒問題的,這需要自己去實現,但是這不是我們今天要說的重點,放到後面再寫一個這種例子。
總之—使用了解析器,讓我們的代碼可讀性變得更好,代碼也更簡潔。

組裝適配器—打通去往RxJava的道路

 private void initAdapt() {

        final Retrofit build = new Retrofit.Builder()
                .baseUrl("http://zhuangbi.info/")
                .addConverterFactory(GsonConverterFactory.create())//添加轉換器
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加適配器 連接RxJava
                .build();
        //使用RxBinding處理點擊事件 RxBinding基於RxJava實現
        RxView.clicks(mBtnAdapter)
                .debounce(500, TimeUnit.MILLISECONDS) //排除500ms內重復點擊事件
                .observeOn(AndroidSchedulers.mainThread())//切換到主線程執行響應內容,相當於在回調方法中執行runOnUiThread方法
                .subscribe(new Action1() {
                    @Override
                    public void call(Void aVoid) {
                        //不需要Call了,直接使用Observable,順滑的切換到RxJava領域
                        Observable> observable = build.create(SearchApi.class)
                                .search("牛逼")
                                .subscribeOn(Schedulers.io())
                                .observeOn(AndroidSchedulers.mainThread());
                        //這裡主要是為了演示,代碼簡潔,實際中不要這樣,
                        // 還要考慮取消一個請求,返回錯誤值處理,業務需求等等
                        observable.subscribe(new Action1>() {
                            @Override
                            public void call(List searchImages) {
                                for (SearchImage searchImage : searchImages) {
                                    Log.d(TAG, "call: " + searchImage.toString());
                                }
                            }
                        });
                    }
                });
    }

和解析器一樣,適配器也就是加了一句.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加適配器 連接
代碼雖然簡潔,作用巨大,注意現在我們已經不用Call來做請求了,我們使用Observable,這直接就可以利用RxJava操作。

線程切換,是這種感覺
 .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread());

2.以前的回調Callback變成了監聽者處理事件observable.subscribe,有更多

Action1
Subscription

方式讓我們選擇

3.鏈式調用,整個邏輯清晰明了
但是注意一點:
對API調用了 observeOn(MainThread) 之後,線程會跑在主線程上,包括 onComplete 也是, unsubscribe 也在主線程,然後如果這時候調用 call.cancel 會導致 NetworkOnMainThreadException ,所以一定要在 retrofit 的API調用.subscribeOn(io).observeOn(MainThread) 之後加一句 unsubscribeOn(io)

完整的就是

Api
.subscribeOn(io)
.observeOn(MainThread)
.unsubscribeOn(io)

哦,這裡取消一個命令和call的方式不同,用的是unsubscribeOn(io),Call對應的方法是cancel()

這裡還加入了一點小元素,注意我們使用的是

 RxView.clicks(mBtnAdapter)
                .debounce(500, TimeUnit.MILLISECONDS) //排除500ms內重復點擊事件
                .observeOn(AndroidSchedulers.mainThread())//切換到主線程執行響應內容,相當於在回調方法中執行runOnUiThread方法
                .subscribe(new Action1() {
                    @Override
                    public void call(Void aVoid) {

取代

  mBtnTrans.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {}

這是使用了RxBinding這個庫,基於RxJava實現,看看用著這種方式實現方式對比傳統方式在上述代碼表達出來的,線程切換、防止重復點擊的實現優勢

組裝攔截器—調試神器

我寫了,快一周也沒有找到他的,請求方式,請求頭在哪兒打印,一直好慌。知道再OkHttp的Github上看到HttpLoggingInterceptor這個庫。
使用方式:導入

    compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
   private void initLog() {

        HttpLoggingInterceptor logging = new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS);
        //自定義攔截方式
        HttpLoggingInterceptor logger = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                Log.v("Retrofit", message);
            }
        }).setLevel(HttpLoggingInterceptor.Level.BASIC);
        OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(logger).build();
        final Retrofit build = new Retrofit.Builder()
                .baseUrl("http://zhuangbi.info/")
                .addConverterFactory(GsonConverterFactory.create())//添加轉換器
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//添加適配器 連接RxJava
                .client(httpClient)
                .build();
        RxView.clicks(mBtnLog)
                .debounce(300, TimeUnit.MILLISECONDS)
                .subscribe(new Action1() {
                               @Override
                               public void call(Void aVoid) {
                                   //不需要Call了,直接使用Observable,順滑的切換到RxJava領域
                                   Observable> observable = build.create(SearchApi.class)
                                           .search("牛逼")
                                           .subscribeOn(Schedulers.io())
                                           .observeOn(AndroidSchedulers.mainThread());
                                   //這裡主要是為了演示,代碼簡潔,實際中不要這樣,
                                   // 還要考慮取消一個請求,返回錯誤值處理,業務需求等等
                                   observable.subscribe(new Action1>() {
                                       @Override
                                       public void call(List searchImages) {
                                           for (SearchImage searchImage : searchImages) {
                                               Log.d(TAG, "call: " + searchImage.toString());
                                           }
                                       }
                                   });
                               }
                           }
                );
    }

有4個請求層次,一般BASIC和HEADER用得比較多

  public enum Level {
    /** No logs. */
    NONE,
    /**
     * Logs request and response lines.
     *
     * 

Example: *

{@code
     * --> POST /greeting http/1.1 (3-byte body)
     *
     * <-- 200 OK (22ms, 6-byte body)
     * }
*/ BASIC, /** * Logs request and response lines and their respective headers. * *

Example: *

{@code
     * --> POST /greeting http/1.1
     * Host: example.com
     * Content-Type: plain/text
     * Content-Length: 3
     * --> END POST
     *
     * <-- 200 OK (22ms)
     * Content-Type: plain/text
     * Content-Length: 6
     * <-- END HTTP
     * }
*/ HEADERS, /** * Logs request and response lines and their respective headers and bodies (if present). * *

Example: *

{@code
     * --> POST /greeting http/1.1
     * Host: example.com
     * Content-Type: plain/text
     * Content-Length: 3
     *
     * Hi?
     * --> END GET
     *
     * <-- 200 OK (22ms)
     * Content-Type: plain/text
     * Content-Length: 6
     *
     * Hello!
     * <-- END HTTP
     * }
*/ BODY }

還可以通過自定義HttpLoggingInterceptor logger = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger()個性化打造自己的網絡數據請求打印。
這裡寫圖片描述

組裝監聽器—監聽Response下行數據

把攔截器,進一步升華,比如下載數據的時候,攔截下Response,讀取裡面的信息,然後再傳輸給下一層,不就可以實現監聽下行數據麼。
先實現一個監聽器

public interface ProcessListener {
    /**
     * @param current     已完成字節數
     * @param total       總字節數
     * @param isCompleted 是否已經完成
     */
    void onProcess(long current, long total, boolean isCompleted);
}

實現一個具備攔截能力的Response類

public class ProcessResponseBody extends ResponseBody {
    private ProcessListener mProcessListener;
    private ResponseBody mResponseBody;
    private BufferedSource mBufferedSource;

    public ProcessResponseBody(ResponseBody responseBody, ProcessListener processListener) {
        mProcessListener = processListener;
        mResponseBody = responseBody;
    }

    @Override
    public MediaType contentType() {
        return mResponseBody.contentType();
    }

    @Override
    public long contentLength() {
        return mResponseBody.contentLength();
    }

    @Override
    public BufferedSource source() {
        if (mBufferedSource == null) {
            mBufferedSource = Okio.buffer(source(mResponseBody.source()));
        }
        return mBufferedSource;
    }

    private Source source(Source source) {
        return new ForwardingSource(source) {
            long nowSize = 0L;

            @Override
            public long read(Buffer sink, long byteCount) throws IOException {
                long readSize = super.read(sink, byteCount);
                nowSize += (readSize != -1 ? readSize : 0);
                mProcessListener.onProcess(nowSize, mResponseBody.contentLength(), readSize == -1);
                return readSize;
            }
        };
    }
}

調用

        final OkHttpClient httpClient = new OkHttpClient.Builder()
                .addInterceptor(new Interceptor() {
                    @Override
                    public okhttp3.Response intercept(Chain chain) throws IOException {
                        okhttp3.Response oldResponse = chain.proceed(chain.request());
                        return oldResponse.newBuilder()
                                .body(new ProcessResponseBody(oldResponse.body(), new ProcessListener() {
                                    @Override
                                    public void onProcess(long current, long total, boolean isCompleted) {
                                        Log.d(TAG, "onProcess: " + current + "/" + total + "---" + isCompleted);
                                    }
                                }))
                                .build();
                    }
                }).build();

查看Log
這裡寫圖片描述

組裝監聽器2—獲取Request上傳進度

上傳和下行,同理,實現一個具備攔截能力的Request即可
這裡寫圖片描述
太快了,模擬器裡面沒有大一點的圖片,本來是有進度條顯示,看下日志吧
這裡寫圖片描述

實現代碼:

public class ProcessRequestBody extends RequestBody {
    private ProcessListener mProcessListener;
    private RequestBody mRequestBody;

    private BufferedSink mBufferedSink;

    public ProcessRequestBody(RequestBody requestBody, ProcessListener processListener) {
        mProcessListener = processListener;
        mRequestBody = requestBody;
    }

    @Override
    public long contentLength() throws IOException {
        return mRequestBody.contentLength();
    }

    @Override
    public MediaType contentType() {
        return mRequestBody.contentType();
    }

    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        if (mBufferedSink == null) {
            mBufferedSink = Okio.buffer(sink(sink));
        }
        mRequestBody.writeTo(mBufferedSink);
        mBufferedSink.flush();
    }

    private Sink sink(Sink sink) {
        return new ForwardingSink(sink) {
            long writenBytes = 0L;

            @Override
            public void write(Buffer source, long byteCount) throws IOException {
                super.write(source, byteCount);
                writenBytes += byteCount;
                mProcessListener.onProcess(writenBytes, mRequestBody.contentLength(), writenBytes == mRequestBody.contentLength());
            }
        };
    }
}

調用

 final OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(new Interceptor() {
                    @Override
                    public okhttp3.Response intercept(Chain chain) throws IOException {

                        RequestBody requestBody = new ProcessRequestBody(chain.request().body(), new ProcessListener() {
                            @Override
                            public void onProcess(final long current, final long total, final boolean isCompleted) {

                                getActivity().runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        mDialog.show();
                                        mDialog.setProgress((int) (current * 100 / total));
                                        if (isCompleted) {
                                            mDialog.dismiss();
                                        }

                                    }
                                });
                                Log.d(TAG, "onProcess: " + current + "/" + total + isCompleted);
                            }
                        });

                        Request newRequest = chain.request().newBuilder().post(requestBody).build();
                        return chain.proceed(newRequest);

                    }
                })
                .build();

源碼下載地址:https://github.com/zhouruikevin/RxJavaSamples-master

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved