RxJava+Retrofit2搭建網(wǎng)絡(luò)請(qǐng)求組件完整配置勿决、示例代碼及流程梳理

本文是對(duì)之前寫(xiě)的兩篇文章的綜合概述及全流程梳理
Android 利用RxJava和Retrofit搭建網(wǎng)絡(luò)請(qǐng)求組件——監(jiān)聽(tīng)回調(diào)及部分源碼解析
Android 利用RxJava和Retrofit搭建網(wǎng)絡(luò)請(qǐng)求組件——基礎(chǔ)配置及部分源碼解析

基礎(chǔ)配置

一.定義網(wǎng)絡(luò)請(qǐng)求方法的接口文件:ApiService.class(名字隨意)

ApiService.class定義了各個(gè)請(qǐng)求方法昙楚,請(qǐng)求方式及參數(shù)類型均以注解形式標(biāo)識(shí)愿棋,示例文件僅描述了GET與POST請(qǐng)求方式

/**
 * 注意:ApiService 必須是接口,且不能實(shí)現(xiàn)其他接口
 * Created by cuiyan on 16-10-18.
*/
public interface ApiService {
    /*************************GET 請(qǐng)求方式*************************/
   
    /**
    * ApiConstants.QQ_SPORT_API 請(qǐng)求接口地址api(相對(duì)路徑)
    * 域名或ip部分會(huì)在構(gòu)建Retrofit時(shí)設(shè)置
    * 返回Observable對(duì)象(內(nèi)部包裝了Call實(shí)例)科展,訂閱(subscribe)執(zhí)行時(shí)會(huì)調(diào)用Call.execute()方法發(fā)送網(wǎng)絡(luò)請(qǐng)求。
    * 默認(rèn)情況下糠雨,返回結(jié)果為retrofit2.Call對(duì)象才睹,只有在構(gòu)建Retrofit實(shí)例時(shí)設(shè)置了RxJava2CallAdapterFactory才支持請(qǐng)求方法返回Observable對(duì)象
    */
    @GET(ApiConstants.QQ_SPORT_API)
    Observable<NewsResult> getQQSportNews(@Query("baid") String baid, @Query("apikey") String apiKey);

    @GET(ApiConstants.QQ_SPORT_API)
    Observable<NewsResult> getQQSportNews1(@QueryMap Map<String, String> paramsMap);

    /*************************POST 請(qǐng)求方式*************************/

    /**
    * @Body 注解參數(shù)均表示以實(shí)體形式提交請(qǐng)求數(shù)據(jù)
    */
    @POST(ApiConstants.QQ_SPORT_API)
    Observable<NewsResult> getQQSportNews2(@Body NewsResult bodyParam);

    /**
    * @Field 或 @FieldMap 注解參數(shù)均表示以表單形式提交參數(shù),相應(yīng)的甘邀,
    * 請(qǐng)求方法必須添加 @FormUrlEncoded 注解
    */
    @FormUrlEncoded
    @POST(ApiConstants.QQ_SPORT_API)
    Observable<NewsResult> getQQSportNews3(@Field("baid") String baid, @Field("apikey") String   apiKey);

    @FormUrlEncoded
    @POST(ApiConstants.QQ_SPORT_API)
    Observable<NewsResult> getQQSportNews4(@FieldMap Map<String, String> paramsMap);
}
二.ApiService實(shí)例構(gòu)建及相關(guān)配置
public class BaseServiceUtil {
    private static final int DEFAULT_TIMEOUT = 10;
   
    /**
     @param serviceClass 網(wǎng)絡(luò)請(qǐng)求接口描述文件類琅攘,步驟一中的ApiService.class或其他類似class
     @return S S實(shí)例,即ApiService實(shí)例
    */
    public static synchronized <S> S createService(Class<S> serviceClass) {
        return createService(serviceClass, null);
    }

    public static <S> S createService(Class<S> serviceClass, String baseUrl) {
        CommonInterceptor interceptor = new CommonInterceptor();
        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        Retrofit.Builder retrofitBuilder = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())   // 設(shè)置Converter.Factory(請(qǐng)求響應(yīng)數(shù)據(jù)轉(zhuǎn)換器)松邪,我們?cè)O(shè)置的是GsonConverterFactory
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create());  // 重點(diǎn)1:為Retrofit設(shè)置CallAdapter.Factory坞琴,這里我們?cè)O(shè)置的是RxJava2CallAdapterFactory。ApiService 中網(wǎng)絡(luò)請(qǐng)求方法默認(rèn)返回retrofit2.Call對(duì)象逗抑,添加RxJava2CallAdapterFactory后支持返回Observable對(duì)象
//      retrofitBuilder.callFactory()
        // 一定要設(shè)置且必須格式正確的baseUrl
        if (!TextUtils.isEmpty(baseUrl)) {
            retrofitBuilder.baseUrl(baseUrl);
        } else {
            retrofitBuilder.baseUrl(BuildConfig.BASE_URL);
        }
        clientBuilder.interceptors().clear();
        clientBuilder.interceptors().add(interceptor);  // 重點(diǎn)2:為OkHttpClient(實(shí)現(xiàn)了okhttp3.Call.Factory接口)設(shè)置攔截器

//        設(shè)置https證書(shū)
//        try {
//            clientBuilder.sslSocketFactory(RqbTrustManager.getInstance().getSSLSocketFactory("BKS", R.raw.rqb_ssl));
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
        OkHttpClient client = clientBuilder.build();

//      重點(diǎn)3:為Retrofit設(shè)置okhttp3.Call.Factory丘喻,OkHttpClient實(shí)現(xiàn)了okhttp3.Call.Factory接口,該方法內(nèi)實(shí)際調(diào)用了
//      callFactory(okhttp3.Call.Factory factory)方法為Retrofit設(shè)置okhttp3.Call.Factory钮蛛。此行與下一行可合并运授,此處有意拆開(kāi)做標(biāo)注
        retrofitBuilder.client(client); 

//      重點(diǎn)4:構(gòu)建Retrofit實(shí)例
        Retrofit retrofit = retrofitBuilder.build();   

//      重點(diǎn)5:構(gòu)建ApiService實(shí)例
        return retrofit.create(serviceClass);   
    }
}
三.網(wǎng)絡(luò)請(qǐng)求訂閱者(觀察者)
 /**
 * 網(wǎng)絡(luò)請(qǐng)求訂閱者
 * Created by cuiyan on 16/6/2 14:09
 */
public class NetRequestSubscriber<T> implements Observer<T> {
    private Dialog progressDialog;
    private Disposable disposable;
    private NetRequestCallback<T> netRequestCallback;
    private Context context;

    /**
     * @param netRequestCallback 網(wǎng)絡(luò)請(qǐng)求回調(diào)
     */
    public NetRequestSubscriber(@NonNull NetRequestCallback<T> netRequestCallback, Context context) {
        this(netRequestCallback, context, false, null);
    }

    /**
     * @param netRequestCallback    網(wǎng)絡(luò)請(qǐng)求回調(diào)
     * @param showProgress          是否顯示網(wǎng)絡(luò)請(qǐng)求加載對(duì)話框
     * @param progressTip           loading提示語(yǔ)
     * @see NetProgressDialog
     */
    public NetRequestSubscriber(@NonNull final NetRequestCallback<T> netRequestCallback, Context context, boolean showProgress, String progressTip) {
        this.netRequestCallback = netRequestCallback;
        this.context = context;
        if (showProgress) {
            progressDialog = NetProgressDialog.getInstance(context, progressTip, new OnNetProgressCancelListener() {
                @Override
                public void onCancelRequest() {
                    cancelRequest() 
                }
            });
        }
    }

    /**
     * @param netRequestCallback 網(wǎng)絡(luò)請(qǐng)求回調(diào)
     * @param progressDialog     dialog 自定義對(duì)話框
     */
    public NetRequestSubscriber(@NonNull NetRequestCallback<T> netRequestCallback, Context context, @NonNull Dialog progressDialog) {
        this.netRequestCallback = netRequestCallback;
        this.context = context;
        this.progressDialog = progressDialog;
    }


    @Override
    public void onSubscribe(@NonNull Disposable d) {
        this.disposable = d;
        showProgress();
        onRequestStart();
    }

    @Override
    public synchronized void onNext(final T t) {
        if (t == null) {
            onRequestResultNull();
        } else {
            if (t instanceof BaseResult && !Config.REQUEST_SUCCESS_CODE.equals(((BaseResult) t).getCode())) {
                ToastUtil.showToast(context, ((BaseResult) t).getMessage());
            }
            onRequestSuccess(t);
        }
    }

    @Override
    public synchronized void onError(Throwable throwable) {
        dismissProgress();
        onRequestError(throwable);
        if (throwable instanceof HttpException) {
            ToastUtil.showToast(context, ((HttpException) throwable).message() + ((HttpException) throwable).code());
        } else {
            if (BuildConfig.DEBUG) {
                ToastUtil.showToast(context, "error:" + throwable.getMessage());
            } else {
                ToastUtil.showToast(context, context.getString(R.string.error_net_request_failed));
            }
        }
    }

    /**
     * {@link NetRequestSubscriber#onError(Throwable)}
     * {@link Observer#onError(Throwable)}
     * {@link Observer#onComplete()} (Throwable)}
     * 該方法與onError方法互斥
     */
    @Override
    public void onComplete() {
        dismissProgress();
        netRequestCallback.onFinish();
    }

    private void onRequestStart() {
        if (Looper.myLooper() != context.getMainLooper()) {
            Handler handler = new Handler(context.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    netRequestCallback.onStart();
                }
            });
        } else {
            netRequestCallback.onStart();
        }
    }

    private void onRequestSuccess(final T t) {
        if (Looper.myLooper() != context.getMainLooper()) {
            Handler handler = new Handler(context.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    netRequestCallback.onSuccess(t);
                }
            });
        } else {
            netRequestCallback.onSuccess(t);
        }
    }

    private void onRequestResultNull() {
        if (Looper.myLooper() != context.getMainLooper()) {
            Handler handler = new Handler(context.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    netRequestCallback.onResultNull();
                }
            });
        } else {
            netRequestCallback.onResultNull();
        }
    }

    private void onRequestError(final Throwable throwable) {
        throwable.printStackTrace();
        if (Looper.myLooper() != context.getMainLooper()) {
            Handler handler = new Handler(context.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    netRequestCallback.onError(throwable);
                    netRequestCallback.onFinish();
                }
            });
        } else {
            netRequestCallback.onError(throwable);
            netRequestCallback.onFinish();
        }
    }

    /**
     *
     *
     */
    private void showProgress() {
        if (progressDialog != null && !progressDialog.isShowing()) {
            progressDialog.show();
        }
    }

    private void dismissProgress() {
        if (progressDialog != null && progressDialog.isShowing()) {
            progressDialog.dismiss();
        }
    }

    public void cancelRequest() {
        dismissProgress();
        if (disposable != null && !disposable.isDisposed()) {
            disposable.dispose();
        }
        netRequestCallback.onCancel();
        netRequestCallback.onFinish();
    }
}
四.網(wǎng)絡(luò)請(qǐng)求控制器
public class BaseController {
    /**
     * @param subscriber 訂閱者
     */
    @SuppressWarnings("unchecked")
    public static synchronized void sendRequest(final NetRequestSubscriber subscriber, Observable observable) {
        observable.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(subscriber);
    }

    /**
     * @param activity   用于與observable綁定,activity生命周期結(jié)束時(shí),自動(dòng)取消訂閱
     * @param observable 被觀察者
     * @param subscriber 訂閱者
     */
    @SuppressWarnings("unchecked")
    public static synchronized void sendRequest(RxActivity activity, final NetRequestSubscriber subscriber, Observable observable) {
        observable.subscribeOn(Schedulers.io())
                .compose(activity.bindToLifecycle()) //防止內(nèi)存泄漏,activity生命周期結(jié)束后取消訂閱
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(subscriber);
    }

    /**
     * @param fragment   用于與observable綁定,fragment生命周期結(jié)束時(shí),自動(dòng)取消訂閱
     * @param subscriber 訂閱者
     */
    @SuppressWarnings("unchecked")
    public static synchronized void sendRequest(RxFragment fragment, final NetRequestSubscriber subscriber, Observable observable) {
        observable.compose(fragment.bindToLifecycle()) //防止內(nèi)存泄漏,fragment生命周期結(jié)束后取消訂閱
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(subscriber);
    }
}

上述代碼構(gòu)建了完整網(wǎng)絡(luò)請(qǐng)求功能咏尝,下面貼出應(yīng)用示例。完整示例:https://github.com/670832188/TestApp

private void getNews() {
    NetRequestSubscriber<NewsResult> subscriber = new NetRequestSubscriber<>(new NetRequestCallback<NewsResult>() {
        @Override
        public void onStart() {
            setContentState(STATE_NET_PROGRESS);
        }

        @Override
        public void onSuccess(@NonNull NewsResult newsResult) {
            if (newsResult.getData() != null && newsResult.getData().size() > 0) {
                setContentState(STATE_DATA_CONTENT);
                newsAdapter.updateDataList(newsResult.getData());
            } else {
                setContentState(STATE_DATA_EMPTY);
            }
        }

        @Override
        public void onResultNull() {
            setContentState(STATE_NET_ERROR);
        }

        @Override
        public void onError(Throwable throwable) {
            setContentState(STATE_NET_ERROR);
        }

        @Override
        public void onCancel() {
            super.onCancel();
        }

        @Override
        public void onFinish() {
            super.onFinish();
        }
    }, this);

    Observable<NewsResult> observable = BaseServiceUtil.createService(ApiService.class, ApiConstants.JUHE_BASE_URL).getQQSportNews("69", Constant.JUHE_API_KEY);
    BaseController.sendRequest(this, subscriber, observable);
}

流程梳理

上述代碼中標(biāo)注了五處重點(diǎn):
1.Retrofit設(shè)置RxJava2CallAdapterFactory褂傀;
2.OkHttpClient(okhttp3.Call.Factory)設(shè)置Interceptor;
3.Retrofit設(shè)置OkHttpClient(okhttp3.Call.Factory);
4.構(gòu)建Retrofit實(shí)例
5.構(gòu)建ApiService實(shí)例
下面從Retrofit和ApiService實(shí)例構(gòu)建為入口羞酗,分析基本流程及上述上述幾個(gè)配置是如何工作的
先看一下創(chuàng)建Retrofit實(shí)例的方法Retrofit.build()源碼

 1.   public Retrofit build() {
 2.       if (baseUrl == null) {
 3.           throw new IllegalStateException("Base URL required.");
 4.       }

 5.       okhttp3.Call.Factory callFactory = this.callFactory;
 6.       if (callFactory == null) {
 7.           callFactory = new OkHttpClient();
 8.        }

 9.       Executor callbackExecutor = this.callbackExecutor;
 10.      if (callbackExecutor == null) {
 11.          callbackExecutor = platform.defaultCallbackExecutor();
 12.      }

 13.      // Make a defensive copy of the adapters and add the default Call adapter.
 14.      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
 15.      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

 16.      // Make a defensive copy of the converters.
 17.      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

 18.      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,callbackExecutor, validateEagerly);
 19.  }

第1-第3行:首先檢測(cè)了之前設(shè)置的baseUrl,沒(méi)有設(shè)置的話直接拋出異常紊服。

第5-第8行:檢測(cè)是否設(shè)置了okhttp3.Call.Factory檀轨,沒(méi)有設(shè)置的話使用默認(rèn)的OkHttpClient實(shí)例(前述代碼中說(shuō)過(guò)OkHttpClient實(shí)現(xiàn)了okhttp3.Call.Factory接口)。我們?cè)诖a中創(chuàng)建欺嗤、配置了OkHttpClient實(shí)例(設(shè)置超時(shí)時(shí)間参萄、攔截器),并設(shè)置于Retrofit實(shí)例煎饼。之所以沒(méi)有使用默認(rèn)的OkHttpClient讹挎,是因?yàn)槲覀冃枰鶕?jù)需求自定義超時(shí)時(shí)間、攔截器等

第14-15行:獲取CallAdapter.Factory列表,包括我們?cè)O(shè)置的RxJava2CallAdapterFactory和框架內(nèi)置的默認(rèn)CallAdapter.Factory筒溃,追蹤源碼可以看到马篮,內(nèi)置的是ExecutorCallAdapterFactory.ExecutorCallbackCall。
注意:列表的順序怜奖,先添加的是我們?cè)O(shè)置的RxJava2CallAdapterFactory浑测,請(qǐng)記住我們的RxJava2CallAdapterFactory是NO.1 NO.1 NO.1
第17行:獲取Converter.Factory列表,包括我們?cè)O(shè)置的GsonConverterFactory和內(nèi)置的Converter.Factory(參見(jiàn)Rerofit.Builder構(gòu)造方法)歪玲。

第18行:callFactory迁央、adapterFactories 等作為構(gòu)建參數(shù)傳入Retrofit構(gòu)造方法創(chuàng)建Retrofit實(shí)例,看一下其構(gòu)造方法:

Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
        List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
        @Nullable Executor callbackExecutor, boolean validateEagerly) {
    this.callFactory = callFactory;
    this.baseUrl = baseUrl;
    this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
    this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
    this.callbackExecutor = callbackExecutor;
    this.validateEagerly = validateEagerly;
}

成員變量callFactory 是我們?cè)O(shè)置的OkHttpClient滥崩;converterFactories包含了我們?cè)O(shè)置的GsonConverterFactory岖圈;adapterFactories 包含了我們?cè)O(shè)置的RxJava2CallAdapterFactory。暫且先有些印象钙皮,方便后續(xù)分析蜂科。

下面看ApiService實(shí)例構(gòu)建方法Retrofit .create(final Class<T> service)源碼:

1.    public <T> T create(final Class<T> service) {
2.        Utils.validateServiceInterface(service);
3.        if (validateEagerly) {
4.            eagerlyValidateMethods(service);
5.        }
6.        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, 
7.            new InvocationHandler() {
8.                private final Platform platform = Platform.get();

9.                @Override 
10                public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
11.                   // If the method is a method from Object then defer to normal invocation.
12.                   if (method.getDeclaringClass() == Object.class) {
13.                       return method.invoke(this, args);
14.                   }
15.                   if (platform.isDefaultMethod(method)) {
16.                      return platform.invokeDefaultMethod(method, service, proxy, args);
17.                   }
18.                   ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
19.                   OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);  // 請(qǐng)留意okHttpCall 
20.                   return serviceMethod.callAdapter.adapt(okHttpCall);  // 請(qǐng)留心callAdapter
21.              }
22.         });
23.    }

很直接、很暴力啊短条,利用Proxy創(chuàng)建的ApiService代理對(duì)象导匣。查看InvocationHandler的invoke方法,看看代理操作做了什么處理慌烧。我們只關(guān)心我們的網(wǎng)絡(luò)請(qǐng)求方法逐抑,我們的網(wǎng)絡(luò)請(qǐng)求方法都是ApiService定義的鸠儿,第12行判斷條件不滿足屹蚊,pass;第15行判斷條件也不成立进每,具體原因可查看platform的創(chuàng)建方法汹粤。

現(xiàn)在只剩下18-20行是我們需要關(guān)心的。先看第20行田晚,我們的網(wǎng)絡(luò)請(qǐng)求被萬(wàn)惡的代理商劫持了嘱兼,返回結(jié)果是serviceMethod.callAdapter.adapt(okHttpCall),如果你的記憶不是很差的話贤徒,應(yīng)該記得我們的請(qǐng)求方法返回的Observable對(duì)象芹壕。是很明顯,我們需要知道這個(gè)callAdapter是什么接奈,了解它的adapt方法起到了什么作用踢涌。定位到第18行,深入loadServiceMethod方法窺視一下

1.   ServiceMethod<?, ?> loadServiceMethod(Method method) {
2.       ServiceMethod<?, ?> result = serviceMethodCache.get(method);
3.       if (result != null) return result;

4.       synchronized (serviceMethodCache) {
5.           result = serviceMethodCache.get(method);
6.           if (result == null) {
7.               result = new ServiceMethod.Builder<>(this, method).build();
8.               serviceMethodCache.put(method, result);
9.           }
10.      }
11.      return result;
12.  }

很遺憾序宦,這個(gè)方法沒(méi)有窺測(cè)到ServiceMethod是神馬角色睁壁。定位到上述代碼第7行,繼續(xù)追擊ServiceMethod(ServiceMethod.class)

1.   Builder(Retrofit retrofit, Method method) {
2.       this.retrofit = retrofit;
3.       this.method = method;
4.       this.methodAnnotations = method.getAnnotations();
5.       this.parameterTypes = method.getGenericParameterTypes();
6.       this.parameterAnnotationsArray = method.getParameterAnnotations();
7.   }

8.   public ServiceMethod build() {
9.       callAdapter = createCallAdapter();
10.      responseType = callAdapter.responseType();
11.      if (responseType == Response.class || responseType == okhttp3.Response.class) {
12.          throw methodError("'"+ Utils.getRawType(responseType).getName() + "' is not a valid response body type. Did you mean ResponseBody?");
13.      }
14.      responseConverter = createResponseConverter();

15.      for (Annotation annotation : methodAnnotations) {
16           parseMethodAnnotation(annotation);
17.      }

18.      // 此處省略一萬(wàn)個(gè)字...
19.      return new ServiceMethod<>(this);  // 此處要記住,后面有用
20.   }

// 構(gòu)造方法一并看了
21.   ServiceMethod(Builder<R, T> builder) {
22.       this.callFactory = builder.retrofit.callFactory();  // 此處請(qǐng)留意
23.       this.callAdapter = builder.callAdapter;
24.       this.baseUrl = builder.retrofit.baseUrl();
25.       this.responseConverter = builder.responseConverter;
26.       this.httpMethod = builder.httpMethod;
27.       this.relativeUrl = builder.relativeUrl;
28.       this.headers = builder.headers;
29.       this.contentType = builder.contentType;
30.       this.hasBody = builder.hasBody;
31.       this.isFormEncoded = builder.isFormEncoded;
32.       this.isMultipart = builder.isMultipart;
33.       this.parameterHandlers = builder.parameterHandlers;
34.   }

看Builder構(gòu)造方法第2行潘明,ServiceMethod持有我們之前創(chuàng)建的Retrofit實(shí)例行剂。再看第9行callAdapter = createCallAdapter(); 我們要找的callAdapter(CallAdapter)終于浮出水面钳降。我們之前說(shuō)過(guò)厚宰,創(chuàng)建Retrofit實(shí)例的時(shí)候我們?yōu)槠湓O(shè)置了CallAdapter.Factory,就是那個(gè)RxJava2CallAdapterFactory...(此處伏筆~~)牲阁。
從第9行繼續(xù)追蹤源碼固阁,最終定位到Retrofit的nextCallAdapter方法,此方法返回的CallAdapter就是我們要尋找的目標(biāo)了

1.   public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
2.       checkNotNull(returnType, "returnType == null");
3.       checkNotNull(annotations, "annotations == null");

4.       int start = adapterFactories.indexOf(skipPast) + 1;
5.       for (int i = start, count = adapterFactories.size(); i < count; i++) {
6.           CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
7.           if (adapter != null) {
8.               return adapter;
9.           }
10.      }
11.      // 此處省略一萬(wàn)字(異常處理相關(guān))
12.   }

這段代碼很就容易看懂了~城菊。遍歷CallAdapter.Factory列表备燃,通過(guò)Factory的get方法獲取匹配的CallAdapter,一猜就是通過(guò)我們?cè)O(shè)置的RxJava2CallAdapterFactory獲取的凌唬。為什么這么說(shuō)呢并齐?還記得之前我們說(shuō)過(guò),我們?cè)O(shè)置的RxJava2CallAdapterFactory是第一順位客税,狀元郎况褪。看一下第4行更耻,遍歷起點(diǎn)位置start值是多少测垛?如果你自己追蹤到這塊代碼,就會(huì)知道skipPast為null秧均,所以start等于0食侮,因此是從頭到尾遍歷列表,而我們的RxJava2CallAdapterFactory處于列表第一位目胡。那就看一看RxJava2CallAdapterFactory部分源碼吧

public final class RxJava2CallAdapterFactory extends CallAdapter.Factory {
  /**
   * Returns an instance which creates synchronous observables that do not operate on any scheduler
   * by default.
   */
  public static RxJava2CallAdapterFactory create() {
    return new RxJava2CallAdapterFactory(null, false);
  }

  private final @Nullable Scheduler scheduler;
  private final boolean isAsync;

  private RxJava2CallAdapterFactory(@Nullable Scheduler scheduler, boolean isAsync) {
    this.scheduler = scheduler;
    this.isAsync = isAsync;
  }

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    Class<?> rawType = getRawType(returnType);

    if (rawType == Completable.class) {
      // Completable is not parameterized (which is what the rest of this method deals with) so it
      // can only be created with a single configuration.
      return new RxJava2CallAdapter(Void.class, scheduler, isAsync, false, true, false, false,
          false, true);
    }

    boolean isFlowable = rawType == Flowable.class;
    boolean isSingle = rawType == Single.class;
    boolean isMaybe = rawType == Maybe.class;
    if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) {
      return null;
    }

    boolean isResult = false;
    boolean isBody = false;
    Type responseType;
    if (!(returnType instanceof ParameterizedType)) {
      String name = isFlowable ? "Flowable"
          : isSingle ? "Single"
          : isMaybe ? "Maybe" : "Observable";
      throw new IllegalStateException(name + " return type must be parameterized"
          + " as " + name + "<Foo> or " + name + "<? extends Foo>");
    }

    Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
    Class<?> rawObservableType = getRawType(observableType);
    if (rawObservableType == Response.class) {
      if (!(observableType instanceof ParameterizedType)) {
        throw new IllegalStateException("Response must be parameterized"
            + " as Response<Foo> or Response<? extends Foo>");
      }
      responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
    } else if (rawObservableType == Result.class) {
      if (!(observableType instanceof ParameterizedType)) {
        throw new IllegalStateException("Result must be parameterized"
            + " as Result<Foo> or Result<? extends Foo>");
      }
      responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
      isResult = true;
    } else {
      responseType = observableType;
      isBody = true;
    }

    return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable,
        isSingle, isMaybe, false);
  }
}

上述源碼做了刪減锯七,只保留了我們關(guān)心的部分。我們?cè)谠O(shè)置RxJava2CallAdapterFactory時(shí)誉己,使用的是RxJava2CallAdapterFactory無(wú)參靜態(tài)方法create()創(chuàng)建的RxJava2CallAdapterFactory實(shí)例眉尸,顯然scheduler為null,isAsync為false巨双。繼續(xù)看get方法噪猾,定位到return語(yǔ)句,返回的是RxJava2CallAdapter筑累,構(gòu)造RxJava2CallAdapter實(shí)例時(shí)傳入了一堆參數(shù)袱蜡,我們關(guān)心一下scheduler、isAsync和isBody參數(shù)疼阔,不過(guò)多解析該方法戒劫,請(qǐng)自行深度查閱半夷。scheduler為null,isAsync為false迅细,isBody為true巫橄,以此為基礎(chǔ)繼續(xù)看RxJava2CallAdapter源碼,代碼不多,全部貼出

final class RxJava2CallAdapter<R> implements CallAdapter<R, Object> {
    private final Type responseType;
    private final @Nullable Scheduler scheduler;
    private final boolean isAsync;
    private final boolean isResult;
    private final boolean isBody;
    private final boolean isFlowable;
    private final boolean isSingle;
    private final boolean isMaybe;
    private final boolean isCompletable;
    // 構(gòu)造方法
    RxJava2CallAdapter(Type responseType, @Nullable Scheduler scheduler, boolean isAsync,
            boolean isResult, boolean isBody, boolean isFlowable, boolean isSingle, boolean isMaybe,
            boolean isCompletable) {
        this.responseType = responseType;
        this.scheduler = scheduler;
        this.isAsync = isAsync;
        this.isResult = isResult;
        this.isBody = isBody;
        this.isFlowable = isFlowable;
        this.isSingle = isSingle;
        this.isMaybe = isMaybe;
        this.isCompletable = isCompletable;
    }

    @Override public Type responseType() {
        return responseType;
    }

    @Override 
    public Object adapt(Call<R> call) {
        Observable<Response<R>> responseObservable = isAsync
        ? new CallEnqueueObservable<>(call)
        : new CallExecuteObservable<>(call);

        Observable<?> observable;
        if (isResult) {
            observable = new ResultObservable<>(responseObservable);
        } else if (isBody) {
            // 這個(gè)是該方法最終返回的Observable對(duì)象
            observable = new BodyObservable<>(responseObservable);
        } else {
           observable = responseObservable;
        }

        if (scheduler != null) {
            observable = observable.subscribeOn(scheduler);
        }

        if (isFlowable) {
            return observable.toFlowable(BackpressureStrategy.LATEST);
        }
        if (isSingle) {
            return observable.singleOrError();
        }
        if (isMaybe) {
            return observable.singleElement();
        }
        if (isCompletable) {
            return observable.ignoreElements();
        }
        return observable;
    }
}

直接看adapt方法第一行responseObservable茵典,是一個(gè)Observable對(duì)象湘换,該方法最終返回的也是Observable對(duì)象,且與responseObservable相關(guān)统阿。先看看responseObservable吧彩倚,前述說(shuō)過(guò)isAsync為false,所以responseObservable是CallExecuteObservable實(shí)例扶平,感興趣您也可以看看CallEnqueueObservable源碼(Android 利用RxJava和Retrofit搭建網(wǎng)絡(luò)請(qǐng)求組件——監(jiān)聽(tīng)回調(diào)及部分源碼解析中有所提及)帆离。之前還說(shuō)過(guò)scheduler為null,isBody為true结澄,綜合下來(lái)哥谷,最終return的是BodyObservable對(duì)象(對(duì)于其他情形可自行查閱相關(guān)代碼),該BodyObservable實(shí)例的構(gòu)造參數(shù)是responseObservable(CallExecuteObservable)麻献。不管怎樣们妥,BodyObservable確實(shí)是Observable,看來(lái)代理商并沒(méi)有做什么壞事勉吻,真的與ApiService中我們期望的結(jié)果一樣监婶。到此為止,我們已經(jīng)知曉ApiService是如何返回Observable對(duì)象了齿桃,主動(dòng)脈已經(jīng)打通惑惶。不再分析BodyObservable源碼,讀者可自行查看源譬,只是做了相關(guān)封裝處理集惋,相對(duì)比較簡(jiǎn)單孕似。接下來(lái)繼續(xù)分析CallExecuteObservable源碼

final class CallExecuteObservable<T> extends Observable<Response<T>> {
  private final Call<T> originalCall;

  CallExecuteObservable(Call<T> originalCall) {
    this.originalCall = originalCall;
  }

  @Override protected void subscribeActual(Observer<? super Response<T>> observer) {
    // Since Call is a one-shot type, clone it for each new observer.
1.    Call<T> call = originalCall.clone();
2.    observer.onSubscribe(new CallDisposable(call));

3.    boolean terminated = false;
4.    try {
5.     Response<T> response = call.execute();
      if (!call.isCanceled()) {
        observer.onNext(response);
      }
      if (!call.isCanceled()) {
        terminated = true;
        observer.onComplete();
      }
    } catch (Throwable t) {
      Exceptions.throwIfFatal(t);
      if (terminated) {
        RxJavaPlugins.onError(t);
      } else if (!call.isCanceled()) {
        try {
          observer.onError(t);
        } catch (Throwable inner) {
          Exceptions.throwIfFatal(inner);
          RxJavaPlugins.onError(new CompositeException(t, inner));
        }
      }
    }
  }

  private static final class CallDisposable implements Disposable {
    private final Call<?> call;

    CallDisposable(Call<?> call) {
      this.call = call;
    }

    @Override public void dispose() {
      call.cancel();
    }

    @Override public boolean isDisposed() {
      return call.isCanceled();
    }
  }

先看一下我標(biāo)記的第5行Response<T> response = call.execute();這行代碼就是執(zhí)行網(wǎng)絡(luò)請(qǐng)求了踩娘,至于call實(shí)例的具體實(shí)現(xiàn)方式稍后分析,目前您只需要知道這么多喉祭。
繼續(xù)分析該方法之前养渴,我們先回憶一下,還記得訂閱者NetRequestSubscriber中有個(gè)cancelRequest()方法嗎泛烙,調(diào)用了onSubscribe(@NonNull Disposable d)方法中傳入的Disposable對(duì)象的dispose()方法來(lái)取消網(wǎng)絡(luò)請(qǐng)求理卑,為什么這個(gè)方法能取消網(wǎng)絡(luò)請(qǐng)求呢?回答這個(gè)問(wèn)題要繼續(xù)看一下subscribeActual方法蔽氨∶赀耄看我標(biāo)記的第2行observer.onSubscribe(new CallDisposable(call))帆疟;CallDisposable是CallExecuteObservable定義的內(nèi)部類,實(shí)現(xiàn)了Disposable接口宇立,dispose()方法中調(diào)用了call.cancel()方法來(lái)取消網(wǎng)絡(luò)請(qǐng)求踪宠,是不是與NetRequestSubscriber中的取消請(qǐng)求方法對(duì)上號(hào)了~。

到此為止妈嘹,通過(guò)分析Retrofit實(shí)例柳琢、ApiService實(shí)例的構(gòu)建及RxJava2CallAdapterFactory源碼追蹤,我們已經(jīng)知道了網(wǎng)絡(luò)請(qǐng)求的執(zhí)行润脸、取消以及如何返回的Observable柬脸,之前提及的幾處重點(diǎn)已經(jīng)粗略了解大半。但是我們還不知道攔截器如何被觸發(fā)工作的毙驯。下面繼續(xù)分析一下call實(shí)例是如何實(shí)現(xiàn)的倒堕,call.execute()方法到底做了什么勾當(dāng),或許它與攔截器暗中勾結(jié)呢
回顧ApiService實(shí)例的構(gòu)建方法爆价,第18行

OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);  // 請(qǐng)留意okHttpCall

上面提及的call實(shí)例就是這個(gè)okHttpCall啦涩馆。沒(méi)辦法,只能再看一看OkHttpCall源碼了允坚,做一下簡(jiǎn)單了解魂那,打入OkHttpCall內(nèi)部,直接定位到execute()方法

@Override public Response<T> execute() throws IOException {
    okhttp3.Call call;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      if (creationFailure != null) {
        if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else {
          throw (RuntimeException) creationFailure;
        }
      }

      call = rawCall;
      if (call == null) {
        try {
          // 注釋1:繼續(xù)追蹤createRawCall()方法吧...
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException e) {
          creationFailure = e;
          throw e;
        }
      }
    }

    if (canceled) {
      call.cancel();
    }
    // 此處請(qǐng)關(guān)聯(lián)上述注釋1
    return parseResponse(call.execute());
  }

看過(guò)源碼很頭疼稠项,顯然我們還要繼續(xù)追蹤createRawCall()方法涯雅,繼續(xù)吧

1.  private okhttp3.Call createRawCall() throws IOException {
2.       Request request = serviceMethod.toRequest(args);
          // 此處想哭
3.       okhttp3.Call call = serviceMethod.callFactory.newCall(request);
4.       if (call == null) {
5.           throw new NullPointerException("Call.Factory returned null.");
6.       }
7.       return call;
8. }

代碼簡(jiǎn)潔明了,繼續(xù)調(diào)查第3行吧展运,如果您的記憶不算差活逆,應(yīng)該記得之前我們?yōu)榱藢ふ褻allAdapter簡(jiǎn)單分析過(guò)此serviceMethod的創(chuàng)建過(guò)程,現(xiàn)在它又回來(lái)了:我馬三立又回來(lái)了...
在ServiceMethod的構(gòu)造方法中拗胜,可以知道callFactory其實(shí)是Retrofit實(shí)例的callFactory蔗候,而Retrofit實(shí)例的callFactory是我們?cè)O(shè)置的OkHttpClient實(shí)例,如果您已經(jīng)沒(méi)有印象請(qǐng)返回查看埂软。好啦锈遥,直接看OkHttpClient的newwCall(Request request)方法,了解Call實(shí)例時(shí)如何創(chuàng)建的

 @Override
 public Call newCall(Request request) {
     // 心中一萬(wàn)個(gè)草泥馬勘畔,又牽涉到RealCall這個(gè)類
     return new RealCall(this, request, false /* for web socket */);
 }

哎所灸,沒(méi)辦法,繼續(xù)查看RealCall這個(gè)類吧炫七,直接定位其execute()方法及相關(guān)聯(lián)方法

1.   final class RealCall implements Call {

2.       @Override
3.       public Response execute() throws IOException {
4.           synchronized (this) {
5.               if (executed) throw new IllegalStateException("Already Executed");
6.               executed = true;
7.           }
8.           captureCallStackTrace();
9.           try {
10.              client.dispatcher().executed(this);
11.              Response result = getResponseWithInterceptorChain();
12.              if (result == null) throw new IOException("Canceled");
13.              return result;
14.          } finally {
15.              client.dispatcher().finished(this);
16.          }
17.      }

18.      Response getResponseWithInterceptorChain() throws IOException {
19.          // Build a full stack of interceptors.
20.          List<Interceptor> interceptors = new ArrayList<>();
21.          interceptors.addAll(client.interceptors());  // 我們?cè)O(shè)置的攔截器爬立,處于列表第一位
               // 以下幾個(gè)為內(nèi)置攔截器
22.          interceptors.add(retryAndFollowUpInterceptor); 
23.          interceptors.add(new BridgeInterceptor(client.cookieJar()));
24.          interceptors.add(new CacheInterceptor(client.internalCache()));
25.          interceptors.add(new ConnectInterceptor(client));
26.          if (!forWebSocket) {
27.              interceptors.addAll(client.networkInterceptors());
28.          }
29.          interceptors.add(new CallServerInterceptor(forWebSocket));
               // 注意此處傳入的index參數(shù)為0,攔截器列表也被傳入RealInterceptorChain構(gòu)造方法
30.          Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);
31.          return chain.proceed(originalRequest);
32.      }
33.   }

看一下第11行代碼万哪,執(zhí)行這行代碼獲取網(wǎng)絡(luò)請(qǐng)求響應(yīng)數(shù)據(jù)侠驯,但是仍然看不出攔截器是如何起作用的抡秆,但是請(qǐng)注意注釋部分,追擊getResponseWithInterceptorChain()方法試試吟策±旁看第31行代碼:執(zhí)行chain.proceed(originalRequest)獲取的響應(yīng)數(shù)據(jù),硬著頭皮看看chain是什么鬼踊挠。定位到第30行乍桂,查閱一下RealInterceptorChain構(gòu)造方法及proceed方法

1.  public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection, int index, Request request) {
2.       this.interceptors = interceptors;
3.       this.connection = connection;
4.       this.streamAllocation = streamAllocation;
5.       this.httpCodec = httpCodec;
6.       // 上一步傳入此處的index為0
7.       this.index = index;
8.       this.request = request;
9.  }

10. public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {
11.     if (index >= interceptors.size()) throw new AssertionError();
12.     calls++;
13.     // If we already have a stream, confirm that the incoming request will use it.
14.     if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
15.         throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
                   + " must retain the same host and port");
16.     }

17.      // If we already have a stream, confirm that this is the only call to chain.proceed().
18.     if (this.httpCodec != null && calls > 1) {
             throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
                   + " must call proceed() exactly once");
19      }

20.     // Call the next interceptor in the chain.
21.     RealInterceptorChain next = new RealInterceptorChain( interceptors, streamAllocation, httpCodec, connection, index + 1, request);
          // 上一步傳入的index 為0,因此取我們?cè)O(shè)置的攔截器
22.     Interceptor interceptor = interceptors.get(index);
23.     Response response = interceptor.intercept(next);

24.     // Confirm that the next interceptor made its required call to chain.proceed().
25.     if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
            throw new IllegalStateException("network interceptor " + interceptor
                       + " must call proceed() exactly once");
26.     }

27.     // Confirm that the intercepted response isn't null.
28.     if (response == null) {
            throw new NullPointerException("interceptor " + interceptor + " returned null");
29.     }

30.     return response;
31. }

定位到第22行效床,終于看到了攔截器睹酌,取index為0,所以取第一個(gè)剩檀,就是我們?cè)O(shè)置的攔截器了憋沿;再看第23行,執(zhí)行interceptor.intercept(next)攔截請(qǐng)求。攔截器觸發(fā)流程解析就此終結(jié)沪猴,至于攔截器的用法請(qǐng)參考Retrofit Interceptor(攔截器) 攔截請(qǐng)求并做相關(guān)處理

到此為止辐啄,我們已經(jīng)梳理了網(wǎng)絡(luò)請(qǐng)求大致流程,我們做的配置也做了解析运嗜,本文也到此結(jié)束


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末壶辜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子担租,更是在濱河造成了極大的恐慌砸民,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奋救,死亡現(xiàn)場(chǎng)離奇詭異岭参,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)尝艘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門演侯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人背亥,你說(shuō)我怎么就攤上這事秒际。” “怎么了隘梨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵程癌,是天一觀的道長(zhǎng)舷嗡。 經(jīng)常有香客問(wèn)我轴猎,道長(zhǎng),這世上最難降的妖魔是什么进萄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任捻脖,我火速辦了婚禮锐峭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘可婶。我一直安慰自己沿癞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布矛渴。 她就那樣靜靜地躺著椎扬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪具温。 梳的紋絲不亂的頭發(fā)上蚕涤,一...
    開(kāi)封第一講書(shū)人閱讀 51,573評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音铣猩,去河邊找鬼揖铜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛达皿,可吹牛的內(nèi)容都是我干的天吓。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼峦椰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼龄寞!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起汤功,我...
    開(kāi)封第一講書(shū)人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤萄焦,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后冤竹,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拂封,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年鹦蠕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冒签。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡钟病,死狀恐怖萧恕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肠阱,我是刑警寧澤票唆,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站屹徘,受9級(jí)特大地震影響走趋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜噪伊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一簿煌、第九天 我趴在偏房一處隱蔽的房頂上張望氮唯。 院中可真熱鬧,春花似錦姨伟、人聲如沸惩琉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瞒渠。三九已至,卻和暖如春技扼,著一層夾襖步出監(jiān)牢的瞬間在孝,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工淮摔, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留私沮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓和橙,卻偏偏與公主長(zhǎng)得像仔燕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子魔招,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容