1. 程式人生 > >打造屬於自己的網路請求框架(OKHttp+Retrofit+RxJava)

打造屬於自己的網路請求框架(OKHttp+Retrofit+RxJava)

開發者們現在都在使用OkHttp了,在很多借鑑之後,現在也來封裝屬於自己的網路請求框架。

該框架使用Retrofit,OkHttp,RxJava,RxAndroid,Gson一起封裝。

客戶端請求一般分為如下幾步:

通過API向伺服器傳送請求------->伺服器收到請求然後響應(這裡有兩種情況,一是請求成功返回Json資料,二是去請求失敗返回失敗狀態)---------->客服端拿到伺服器返回狀態解析資料或者請求失敗提示使用者

根據以上思路來看程式碼:

import android.os.Build;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;

/**
 * Created by hedong on 2016/4/19.
 */
public class LocalService {
    public static final String API_BASE_URL = "http://www.tngou.net/api/info/";//主Api路徑

    private static final LocalApi service = getRetrofit().create(LocalApi.class);

    private static Retrofit mRetrofit;
    private static OkHttpClient mOkHttpClient;

    public final static int CONNECT_TIMEOUT = 60;        //設定連線超時時間
    public final static int READ_TIMEOUT = 60;            //設定讀取超時時間
    public final static int WRITE_TIMEOUT = 60;           //設定寫的超時時間

    private static OkHttpClient genericClient() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);

        OkHttpClient httpClient = new OkHttpClient.Builder()
                .addNetworkInterceptor(interceptor)
                .retryOnConnectionFailure(true)
                .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)//設定讀取超時時間
                .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)//設定寫的超時時間
                .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)//設定連線超時時間
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request()
                                .newBuilder()
                                .addHeader("source-terminal", "Android")   //作業系統名稱(注:ios、android)//裝置型號
                                .addHeader("device-model", Build.MODEL)         //裝置型號
                                .addHeader("os-version", Build.VERSION.RELEASE)//作業系統版本號
                                //.addHeader("app-name", name);//應用名稱
                                .build();
                        return chain.proceed(request);
                    }
                }).build();
        return httpClient;
    }

    public static LocalApi getApi() {
        return service;
    }

    protected static Retrofit getRetrofit() {

        if (null == mRetrofit) {
            if (null == mOkHttpClient) {
                mOkHttpClient = genericClient();
            }


            mRetrofit = new Retrofit.Builder()
                    .baseUrl(API_BASE_URL)
                    .addConverterFactory(ResponseConverterFactory.create())
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .client(mOkHttpClient)
                    .build();

        }

        return mRetrofit;
    }
}
註釋寫的很清楚了,但是在新增header的時候,根據自己需要新增。

LocalApi是什麼呢,在這個類裡面我們定義請求方法,get,post等等,

public interface LocalApi {

    //獲取類別
    @GET("classify")
    Observable<List<HealthClassifyBean>> getHealthClassify();


}

請求發出去了,看一下怎麼解析返回的json資料呢,自定義ResponseConverterFactory繼承自Converter.Factory
public class ResponseConverterFactory extends Converter.Factory {
    public static ResponseConverterFactory create() {
        return create(new Gson());
    }

    public static ResponseConverterFactory create(Gson gson) {
        return new ResponseConverterFactory(gson);
    }

    private final Gson gson;

    private ResponseConverterFactory(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        this.gson = gson;
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        return new GsonResponseBodyConverter<>(gson, type);
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        return new GsonResponseBodyConverter<>(gson, type);
    }
}
這只是跟Retrofit繫結,真正的拿到資料,解析的json的在下面:
/**
 * Created by hedong on 2016/4/19.
 */
public class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
    private final Gson gson;
    private final Type type;

    public GsonResponseBodyConverter(Gson gson, Type type) {
        this.gson = gson;
        this.type = type;
    }

    /**
     * {
     * "status": true,
     * "data": [
     * {
     * "description": "",
     * "id": 6,
     * "keywords": "",
     * "name": "",
     * "seq": 1,
     * "title": ""
     * },
     * {
     * "description": "",
     * "id": 5,
     * "keywords": "",
     * "name": "",
     * "seq": 2,
     * "title": ""
     * }
     * ]
     * }
     *
     * @param value
     * @return
     * @throws IOException
     */
    @Override
    public T convert(ResponseBody value) throws IOException {
        String response = value.string();
        Log.d("Network", "response>>" + response);
        try {

            JSONObject jsonObject = new JSONObject(response);
            if (jsonObject.getString("status").equals("true")) {
                //result==true表示成功返回,繼續用本來的Model類解析
                String data = jsonObject.getString("data");

                return gson.fromJson(data, type);

            } else {
                //ErrResponse 將msg解析為異常訊息文字
                ErrResponse errResponse = gson.fromJson(response, ErrResponse.class);
                throw new ResultException(0, errResponse.getMsg());
            }

        } catch (JSONException e) {
            e.printStackTrace();
            Log.e("Network", e.getMessage());
            return null;
        }
    }
}

這裡解釋一下:



在開始寫後臺的時候最好定好規範,以免造成不必要的麻煩。以上格式只是參考,使用者可自行修改。

我們也可以跟伺服器約定錯誤型別,捕獲異常:

/**
 * 用於捕獲伺服器約定的錯誤型別
 */
public class ResultException extends RuntimeException {
    private int errCode = 0;

    public ResultException(int errCode, String msg) {
        super(msg);
        this.errCode = errCode;
    }

    public int getErrCode() {
        return errCode;
    }
}
自定義回撥,獲取http請求對應的狀態碼:
public abstract class AbsAPICallback<T> extends Subscriber<T> {
    //對應HTTP的狀態碼
    private static final int UNAUTHORIZED = 401;
    private static final int FORBIDDEN = 403;
    private static final int NOT_FOUND = 404;
    private static final int REQUEST_TIMEOUT = 408;
    private static final int INTERNAL_SERVER_ERROR = 500;
    private static final int BAD_GATEWAY = 502;
    private static final int SERVICE_UNAVAILABLE = 503;
    private static final int GATEWAY_TIMEOUT = 504;

    protected AbsAPICallback() {

    }

    @Override
    public void onError(Throwable e) {
        Throwable throwable = e;
        //獲取最根源的異常
        while (throwable.getCause() != null) {
            e = throwable;
            throwable = throwable.getCause();
        }

        if (e instanceof HttpException) {//HTTP錯誤
            HttpException httpException = (HttpException) e;
            switch (httpException.code()) {
                case UNAUTHORIZED:
                case FORBIDDEN:
                case NOT_FOUND:
                case REQUEST_TIMEOUT:
                case GATEWAY_TIMEOUT:
                case INTERNAL_SERVER_ERROR:
                case BAD_GATEWAY:
                case SERVICE_UNAVAILABLE:
                default:
                    //Toast.makeText(App.getInstance(), R.string.server_http_error, Toast.LENGTH_SHORT).show();
                    break;
            }
        } else if (e instanceof SocketTimeoutException) {
            //Toast.makeText(App.getInstance(), R.string.network_error, Toast.LENGTH_SHORT).show();
        } else if (e instanceof ResultException) {//伺服器返回的錯誤
            ResultException resultException = (ResultException) e;
          //  Toast.makeText(App.getInstance(), resultException.getMessage(), Toast.LENGTH_SHORT).show();
        } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) {
           // Toast.makeText(App.getInstance(), R.string.data_error, Toast.LENGTH_SHORT).show(); //均視為解析錯誤
        } else if(e instanceof ConnectException){
           // Toast.makeText(App.getInstance(), R.string.server_http_error, Toast.LENGTH_SHORT).show();
        } else {//未知錯誤

        }

        onCompleted();
    }

    protected abstract void onDone(T t);

    @Override
    public void onCompleted() {

    }

    @Override
    public void onNext(T t) {
        onDone(t);
    }
}

程式碼裡面都註釋很清楚。

最後在Activity怎麼呼叫呢,直接貼程式碼:

  private void requestData() {
        LocalService.getApi().getHealthClassify()
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new AbsAPICallback<List<HealthClassifyBean>>() {
                    @Override
                    protected void onDone(List<HealthClassifyBean> list) {

                        //請求成功,做相應的頁面操作

                    }

                    @Override
                    public void onError(Throwable e) {
                        super.onError(e);
                        //e.getMessage() 可獲取伺服器返回錯誤資訊
                    }
                });
    }
ok,到此就結束了。