Retrofit 2.0 超能實踐(一)缀壤,完美支持加密Https傳輸

前陣子看到圈子里Retrofit 2.0序臂,RxJava(Android), OkHttp3.3 ,加之支持Android和 iOS 的React Native 熱更新技術(shù), 火的不要不要的, 2015年新技術(shù)一大波來襲 ,看著自己項目還在用httpClient, asyncTask的原生開發(fā) 感覺自己已成火星人,實在頂不住內(nèi)心強烈的自卑感辩尊,加之對新技術(shù)的追求,入手移動開發(fā)新三劍客康辑,運用在目前的項目中摄欲,雖然目前關(guān)于他們的介紹資料網(wǎng)上一大把,但是自己親自實踐后疮薇,發(fā)現(xiàn)坑不少胸墙,為了能方便其他人安全順利入坑,今天就先從Retrofit說起按咒,前方高能迟隅,準(zhǔn)備躲避。

Retrofit 2.0

Retrofit是SQUARE美國一家移動支付公司最近新發(fā)布的在Android平臺上http訪問的開源項目


一 什么Retrofit

官方標(biāo)語励七;A type-safe HTTP client for Android and Java
語意很明顯一款android安全類型的http客戶端智袭, 那么怎么樣才算安全?支持https掠抬?支持本地線程安全吼野?
發(fā)現(xiàn)Rertofit其內(nèi)部都是支持lambda語法(國內(nèi)稱只鏈?zhǔn)秸Z法),內(nèi)部支持okhttp, 并且支持響應(yīng)式RxJAava两波,當(dāng)然jdk1.8 和android studio工具也支持lambda瞳步。帶著這些疑問 我開始探究一下。

在此之前準(zhǔn)備入手資料:

國外博客
https://inthecheesefactory.com/blog/retrofit-2.0/en

官方github
http://square.github.io/retrofit/

OKHttp原理請看我寫的這個系列:
OkHttp 3.x 源碼解析之Interceptor 攔截器

二 Retrofit怎么使用

下文之前先給大家看下傳統(tǒng)的httpclient(urlConnection) + AsyncTask實現(xiàn)的登錄功能腰奋,這樣我們才能發(fā)現(xiàn)Retrofit的優(yōu)雅之處.

傳統(tǒng)方式:

  /**
 * Represents an asynchronous login/registration task used to authenticate
 * the user.
 */
public class UserLoginTask extends AsyncTask<Void, Void, Boolean> {

    private final String mEmail;
    private final String mPassword;

    UserLoginTask(String email, String password) {
        mEmail = email;
        mPassword = password;
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        // TODO: attempt authentication against a network service.

        try {
            // Simulate network access.
            String result = "";
            BufferedReader in = null;
            String path ="http://localhost:8080/login/?" +"email =" + mEmail + "& password =" + mPassword;
            URL url =new URL(path);
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            conn.setConnectTimeout(5 * 1000);
            conn.setRequestMethod("GET");
            InputStream inStream = conn.getInputStream();
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null)
            {
                result += "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n" + line;
            }

        }catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
   //在這里我們還要對返回的json數(shù)據(jù)進行 要主動映射到modle上
    ………
        for (String credential : DUMMY_CREDENTIALS) {
            String[] pieces = credential.split(":");
            if (pieces[0].equals(mEmail)) {
                // Account exists, return true if the password matches.
                return pieces[1].equals(mPassword);
            }
        }

        // TODO: register the new account here.
        return true;
    }

    @Override
    protected void onPostExecute(final Boolean success) {
        mAuthTask = null;


        if (success) {
            // do SomeThing
        } else {
            mPasswordView.setError(getString(R.string.error_incorrect_password));
            mPasswordView.requestFocus();
        }
    }

    @Override
    protected void onCancelled() {
        mAuthTask = null;
        showProgress(false);
    }
}

private void enterhome() {
    Intent intent = new Intent(LoginActivity.this, MainListActivity.class);
    startActivity(intent);
}

發(fā)現(xiàn)姿勢也很簡單单起,點擊loginbtn開啟一個異步線程 在AsyncTaskdoInBackground中訪問登錄API,在onPostExecute中進行UI更新劣坊;也能很簡單流暢的解決UI線程請求網(wǎng)絡(luò) 非UI線程更新UI的問題, 但是AsyncTask 處理大數(shù)據(jù)耗時就會有弊端馏臭,況且他默認(rèn)線程也是5個,容易造成泄漏,接下來介紹用Retrofit實現(xiàn)以上相同的功能的方式

2 Retrofit

  /**
 * 登錄括儒!
 */
private  void getLogin() {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://localhost:8080/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    ApiManager apiService = retrofit.create(ApiManager.class);

    Call<LoginResult> call = apiService.getData("lyk", "1234");
   call.enqueue(new Callback<LoginResult>() {
       @Override
       public void onResponse(Call<LoginResult> call, Response<LoginResult> response) {
           if (response.isSuccess()) {
               // do SomeThing
           } else {
              //直接操作UI 返回的respone被直接解析成你指定的modle 
           }
       }

       @Override
       public void onFailure(Call<LoginResult> call, Throwable t) {

           // do onFailure代碼
       }
   });
}

ApiManager接口

/**
 * Created by LIUYONGKUI on 2016-05-03.
*/
public interface ApiManager {

 @GET("login/")
 Call<LoginResult> getData(@Query("name") String name, @Query("password") String pw);

好了 看了以上代碼 或許你已經(jīng)看到了他的鏈?zhǔn)絻?yōu)雅高大上的地方了绕沈,也許看不懂,有點蒙逼帮寻,但沒關(guān)系我們繼續(xù)入門乍狐。

1 配置gradle

compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'  

com.squareup.retrofit2:converter-gson:2.0.0-beta4 此依賴非必須,只是方便我對http返回的數(shù)據(jù)進行解析固逗。

2 定義實例化

1》初始化Retrofit

 Retrofit retrofit = new Retrofit.Builder()
          .baseUrl("http://localhost:8080/")
          .addConverterFactory(GsonConverterFactory.create())
           .build();

通過 Retrofit.Builder 來創(chuàng)建一個retrofit客戶端浅蚪,接著添加host url, 然后制定數(shù)據(jù)解析器,上面依賴的gson就是用在這里做默認(rèn)數(shù)據(jù)返回的烫罩, 之后通過build()創(chuàng)建出來
Retrofit也支持且內(nèi)部自帶如下格式:

  • 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

2》編寫API

  @GET("login/")
  Call<LoginResult> getData(@Query("name") String name, @Query("password") String pw);

Call<T>是繼承Cloneable的 并支持泛型惜傲,且此類是Retrofit統(tǒng)一返回對象,支持Callback<T>回調(diào)贝攒,在2.0上已支持RxJava觀察者對象Observable<T>盗誊,此案例暫時用call ,后面入門了retrofit之后再接入RxJava隘弊,接著我們可以傳入制定的解析Modle哈踱,就會在主線程里返回對應(yīng)的model數(shù)據(jù),無需開發(fā)者手動解析json數(shù)據(jù)梨熙,返回格式由開發(fā)者自己設(shè)置开镣,這里主要用注解@get @post 設(shè)置請求方式,后面“l(fā)ogin/”是方法Url, @Query("name")來設(shè)定body的parameters.

  • 如果想用表單 @FieldMap
    @FormUrlEncoded
    @POST("/url")
    Call<T> postForm(
    @FieldMap Map<String , Object> maps);

  • 如果直接用對象 @Body

    @POST("url")
     Call<T> PostBody(
          @Body Objects objects);
    
  • 如果直接多參數(shù) @QueryMap

    @PUT("/url")
    Call<T> queryMap(
          @QueryMap Map<String, String> maps);
    
  • 如果上傳文件 @Part

    @Multipart
    @POST("/url")
    Call<ResponseBody> uploadFlie(
          @Part("description") RequestBody description,
          @Part("files") MultipartBody.Part file);
    
  • 如果多文件上傳 @PartMap()

    @Multipart
    @POST("{url}")
    Call<T> uploadFiles(
          @Path("url") String url,
          @PartMap() Map<String, RequestBody> maps);
    

3》 調(diào)用API
Retrofit支持異步和同步咽扇,案例中用call.enqueue(new Callback<LoginResult>)來采用異步請求邪财,如果 調(diào)用call.execute() 則采用同步方式

   Call<LoginResult> call = apiService.getData("lyk", "1234");
   call.enqueue(new Callback<LoginResult>() {
       @Override
       public void onResponse(Call<LoginResult> call, Response<LoginResult> response) {
         
       }

       @Override
       public void onFailure(Call<LoginResult> call, Throwable t) {

           
       }
   });
}

取消請求

直接用call實例進行cancel即可

  call.cancel(); 

如果還未理解請閱讀參考入門資料:Retrofit 2.0:有史以來最大的改進

三 進階拓展

通過以上的介紹和案列,我們了解了怎樣運用Retrofit請求網(wǎng)絡(luò)數(shù)據(jù)质欲,展現(xiàn)數(shù)據(jù)更新UI树埠,用什么數(shù)據(jù)模型接收 Retroifit就會返回什么類型的數(shù)據(jù),我們也不用關(guān)心是否在主線程里訪問網(wǎng)絡(luò) 還是子線程更新ui的問題把敞,但實際開發(fā)中會存在很多問題弥奸,很多同學(xué)會遇到:Retrofit的內(nèi)部Log都無法輸出 , header怎么加入,請求怎么支持https奋早,包括怎么結(jié)合RxJava.? 不用擔(dān)心盛霎,這些Retrofit 2.0 都提供了支持okhttp的自定義的Interceptor(攔截器),通過不同的Interceptor可以實現(xiàn)不同的自定義請求形式耽装,比如統(tǒng)一加head愤炸,參數(shù),加入證書(ssl)等掉奄,前提必須結(jié)合okhttp來實現(xiàn) , 通過給OkHttpClient添加Interceptor规个,然后給Retrofit設(shè)置http客戶端即可.Retrofit提供了
.client()方法供我們傳入自定義的網(wǎng)絡(luò)客戶端凤薛,當(dāng)然默認(rèn)請求客戶端就是okhttps.

OkHttp入門請移步:
~https://github.com/square/okhttp
~ OKHttp源碼解析

1 開啟Log

可以用攔截器自己實現(xiàn), retrofit已經(jīng)提供了HttpLoggingInterceptor 里面有四種級別诞仓,輸出的格式 可以看下面介紹缤苫。


public enum Level {
    /** No logs. */
    NONE,
    /**
     * Logs request and response lines.
     *
     * <p>Example:
     * <pre>{@code
     * --> POST /greeting http/1.1 (3-byte body)
     *
     * <-- 200 OK (22ms, 6-byte body)
     * }</pre>
     */
    BASIC,
    /**
     * Logs request and response lines and their respective headers.
     *
     * <p>Example:
     * <pre>{@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
     * }</pre>
     */
    HEADERS,
    /**
     * Logs request and response lines and their respective headers and bodies (if present).
     *
     * <p>Example:
     * <pre>{@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
     * }</pre>
     */
    BODY
  }


開啟請求頭

     Retrofit retrofit = new Retrofit.Builder().client(new OkHttpClient.Builder()
                            
                         .addNetworkInterceptor(
                                    new   HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))       
     .build())

開啟body日志

.addNetworkInterceptor(
                                    new   HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) 


基礎(chǔ)輸出

.addNetworkInterceptor(
                                    new   HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) 


2 增加頭部信息

通用請求頭

 new Retrofit.Builder()
           .addConverterFactory(GsonConverterFactory.create())
           
           .client(new OkHttpClient.Builder()
                   .addInterceptor(new Interceptor() {
                       @Override
                       public Response intercept(Chain chain) throws IOException {
                           Request request = chain.request()
                                   .newBuilder()
                                   .addHeader("mac", "f8:00:ea:10:45")
                                   .addHeader("uuid", "gdeflatfgfg5454545e")
                                   .addHeader("userId", "Fea2405144")
                                   .addHeader("netWork", "wifi")
                                   .build();
                           return chain.proceed(request);
                       }
                   })

                   .build()

單獨加入

@Headers({ "Accept: application/vnd.github.v3.full+json", "User-Agent: Retrofit-your-App"})
@get("users/{username}")
Call<User>   getUser(@Path("username") String username);

3 添加證書Pinning

證書可以在自定義的OkHttpClient加入certificatePinner 實現(xiàn)

OkHttpClient client = new OkHttpClient.Builder()
    .certificatePinner(new CertificatePinner.Builder()
            .add("YOU API.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
            .add("YOU API..com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
            .add("YOU API..com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
            .add("YOU API..com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
            .build())

4 支持https

加密和普通http客戶端請求支持https一樣,步驟如下:

1 CertificateFactory 得到Context.getSocketFactory
2 添加證書源文件
3 綁定到okhttpClient
4設(shè)置okhttpClient到retrofit中

證書同樣可以設(shè)置到okhttpclient中墅拭,我們可以把證書放到raw路徑下

   SLSocketFactory sslSocketFactory =getSSLSocketFactory_Certificate(context,"BKS", R.raw.XXX);

準(zhǔn)備證書源文件

加入證書源文件活玲,我的證書是放在Raw下面的:
證書

綁定證書

protected static SSLSocketFactory getSSLSocketFactory(Context context, int[] certificates) {

if (context == null) {
    throw new NullPointerException("context == null");
}

CertificateFactory certificateFactory;
try {
    certificateFactory = CertificateFactory.getInstance("X.509");
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(null, null);

    for (int i = 0; i < certificates.length; i++) {
        InputStream certificate = context.getResources().openRawResource(certificates[i]);
        keyStore.setCertificateEntry(String.valueOf(i), certificateFactory.generateCertificate(certificate));

        if (certificate != null) {
            certificate.close();
        }
    }
    SSLContext sslContext = SSLContext.getInstance("TLS");
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init(keyStore);
    sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
   return sslContext.getSocketFactory();   

構(gòu)建HostnameVerifier

 protected static HostnameVerifier getHostnameVerifier(final String[] hostUrls) {

    HostnameVerifier TRUSTED_VERIFIER = new HostnameVerifier() {

        public boolean verify(String hostname, SSLSession session) {
            boolean ret = false;
            for (String host : hostUrls) {
                if (host.equalsIgnoreCase(hostname)) {
                    ret = true;
                }
            }
            return ret;
        }
    };

return TRUSTED_VERIFIER;

}

設(shè)置setSocketFactory

  okhttpBuilder.socketFactory(HttpsFactroy.getSSLSocketFactory(context, certificates));

certificates 是你raw下證書源ID, int[] certificates = {R.raw.myssl}

設(shè)置setNameVerifie

okhttpBuilder.hostnameVerifier(HttpsFactroy.getHostnameVerifier(hosts));

hosts是你的host數(shù)據(jù) 列如 String hosts[]`= {“https//:aaaa,com”, “https//:bbb.com”}

實現(xiàn)自定義 添加到Retrofit

  okHttpClient = okhttpBuilder.build(); 
  Retrofit retrofit = new Retrofit.Builder() .client(okHttpClient) .build();

如果信任所有https請求,
可以直接將OkHttpClient的HostnameVerifier設(shè)置為false


  OkHttpClient client = new OkHttpClient();
 
    client.setHostnameVerifier(new HostnameVerifier() {
        @Override
        public boolean verify(String s, SSLSession sslSession) {
            return true;
        }
    });
    TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
        @Override
        public void checkClientTrusted(
                java.security.cert.X509Certificate[] x509Certificates,
                String s) throws java.security.cert.CertificateException {
        }

        @Override
        public void checkServerTrusted(
                java.security.cert.X509Certificate[] x509Certificates,
                String s) throws java.security.cert.CertificateException {
        }

        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return new java.security.cert.X509Certificate[] {};
        }
    } };
    try {
        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        client.setSslSocketFactory(sc.getSocketFactory());
    } catch (Exception e) {
        e.printStackTrace();
    }


         clent.protocols(Collections.singletonList(Protocol.HTTP_1_1))
         .build();



常規(guī)問題歸總

1 url被轉(zhuǎn)義

   http://api.myapi.com/http%3A%2F%2Fapi.mysite.com%2Fuser%2Flist

請將@path改成@url

   public interface APIService { 
    @GET Call<Users> getUsers(@Url String url);}

或者:

  public interface APIService {
    @GET("{fullUrl}")
    Call<Users> getUsers(@Path(value = "fullUrl", encoded = true) String fullUrl);
}

2Method方法找不到

java.lang.IllegalArgumentException: Method must not be null

請指定具體請求類型@get @post等

   public interface APIService { 

   @GET Call<Users> getUsers(@Url String url);
}

3Url編碼不對谍婉,@fieldMap parameters must be use FormUrlEncoded

如果用fieldMap加上FormUrlEncoded編碼

@POST()
@FormUrlEncoded
Observable<ResponseBody> executePost(
        @FieldMap Map<String, Object> maps);

上層需要轉(zhuǎn)換將自己的map轉(zhuǎn)換為FieldMap

 @FieldMap(encoded = true) Map<String, Object> parameters,

4 paht和url一起使用

Using @Path and @Url paramers together with retrofit2

java.lang.IllegalArgumentException: @Path parameters may not be used with @Url. (parameter #4

如果你是這樣的:

 @GET
Call<DataResponse> getOrder(@Url String url,
 @Path("id") int id);

請在你的url指定占位符.url:

www.myAPi.com/{Id}

總結(jié)

看了以上的知識點你發(fā)現(xiàn)Retrofit同樣支持RxJava,通過以下設(shè)置Call適配模式.就可以完美關(guān)聯(lián)RxJava舒憾。

 retrofit .addCallAdapterFactory(RxJavaCallAdapterFactory.create())    

關(guān)于 Retrofit+ RxJava的案列, 結(jié)尾源碼已經(jīng)結(jié)合,以及實際遇到的坑可以看本人的系列文章(Retrofit+Rxjava使用技巧一文)穗熬。RxJava也是一款強大的多線程通訊利器镀迂,也支持本地線程安全,從以前編程習(xí)慣遷移到這種鏈?zhǔn)斤L(fēng)格唤蔗, 估計入門會讓你頭痛探遵,但會讓你在實際應(yīng)用開發(fā)中無時無刻,隨心所欲進行多線程響應(yīng)式編程開發(fā)措译。一句話 :誰用誰知道别凤!

Retrofit 2.0系列請閱讀


參考文章:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蝠嘉,隨后出現(xiàn)的幾起案子最疆,更是在濱河造成了極大的恐慌,老刑警劉巖蚤告,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件努酸,死亡現(xiàn)場離奇詭異,居然都是意外死亡杜恰,警方通過查閱死者的電腦和手機获诈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來心褐,“玉大人舔涎,你說我怎么就攤上這事《旱” “怎么了亡嫌?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我挟冠,道長于购,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任知染,我火速辦了婚禮价涝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘持舆。我一直安慰自己色瘩,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布逸寓。 她就那樣靜靜地躺著居兆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪竹伸。 梳的紋絲不亂的頭發(fā)上泥栖,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音勋篓,去河邊找鬼吧享。 笑死,一個胖子當(dāng)著我的面吹牛譬嚣,可吹牛的內(nèi)容都是我干的钢颂。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼拜银,長吁一口氣:“原來是場噩夢啊……” “哼殊鞭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尼桶,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤操灿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后泵督,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體趾盐,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年小腊,在試婚紗的時候發(fā)現(xiàn)自己被綠了救鲤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡溢豆,死狀恐怖蜒简,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情漩仙,我是刑警寧澤搓茬,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布犹赖,位于F島的核電站,受9級特大地震影響卷仑,放射性物質(zhì)發(fā)生泄漏峻村。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一锡凝、第九天 我趴在偏房一處隱蔽的房頂上張望粘昨。 院中可真熱鬧,春花似錦窜锯、人聲如沸张肾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吞瞪。三九已至,卻和暖如春驾孔,著一層夾襖步出監(jiān)牢的瞬間芍秆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工翠勉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留妖啥,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓对碌,卻偏偏與公主長得像荆虱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子俭缓,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

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