Glide 知識(shí)梳理(5) - 自定義GlideModule

一、概述

前面說(shuō)的都是如何使用Glide提供的接口來(lái)展示圖片資源,今天這篇,我們來(lái)講一下如何改變Glide的配置。

二、定義GlideModule

2.1 步驟

首先,我們需要一個(gè)實(shí)現(xiàn)了GlideModule接口的類(lèi),重寫(xiě)其中的方法來(lái)改變Glide的配置,然后讓Glide在構(gòu)造實(shí)例的過(guò)程中,讀取這個(gè)類(lèi)中的配置信息。

  • 第一步:實(shí)現(xiàn)GlideModule接口
public class CustomGlideModule implements GlideModule {

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        //通過(guò)builder.setXXX進(jìn)行配置.
    }

    @Override
    public void registerComponents(Context context, Glide glide) {
        //通過(guò)glide.register進(jìn)行配置.
    }
}
  • 第二步:在AndroidManifest.xml中的<application>標(biāo)簽下定義<meta-data>,這樣Glide才能知道我們定義了這么一個(gè)類(lèi),其中android:name是我們自定義的GlideModule的完整路徑,而android:value就固定寫(xiě)死GlideModule
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <meta-data
            android:name="com.example.lizejun.repoglidelearn.CustomGlideModule"
            android:value="GlideModule"/>
    </application>

2.2 源碼分析

上面2.1所做的工作都是為了在Glide創(chuàng)建時(shí)可以讀取我們?cè)趦蓚€(gè)回調(diào)中配置的信息,我們來(lái)看一下Glide是如何使用這個(gè)自定義的類(lèi)的,它的整個(gè)流程在Glideget方法中:

    public static Glide get(Context context) {
        if (glide == null) {
            synchronized (Glide.class) {
                if (glide == null) {
                    Context applicationContext = context.getApplicationContext();

                    //第一步
                    List<GlideModule> modules = new ManifestParser(applicationContext).parse();
                    
                     //第二步
                    GlideBuilder builder = new GlideBuilder(applicationContext);
                    for (GlideModule module : modules) {
                        //在builder構(gòu)造出glide之前,讀取使用者自定義的配置.
                        module.applyOptions(applicationContext, builder);
                    }
                    glide = builder.createGlide();

                    //第三步
                    for (GlideModule module : modules) {
                        module.registerComponents(applicationContext, glide);
                    }
                }
            }
        }

        return glide;
    }

可以看到,整個(gè)實(shí)例化Glide的過(guò)程分為三步:

  • 第一步:去AndroidManifest中查找meta-dataGlideModule的類(lèi),然后通過(guò)反射實(shí)例化它。
  • 第二步:之后Glide會(huì)新建一個(gè)GlideBuilder對(duì)象,它會(huì)先調(diào)用我們自定義的GlideModuleapplyOptions方法,并把自己傳進(jìn)去,這樣,自定義的GlideModule就可以通過(guò)GlideBuilder提供的接口來(lái)設(shè)置它內(nèi)部的參數(shù),在builder.createGlide()的過(guò)程中就會(huì)根據(jù)它內(nèi)部的參數(shù)來(lái)構(gòu)建Glide,假如我們沒(méi)有設(shè)置相應(yīng)的參數(shù),那么在createGlide時(shí),就會(huì)采取默認(rèn)的實(shí)現(xiàn),下面就是memoryCache的例子。
    //我們?cè)赼pplyOptions中,可以通過(guò)GlideBuilder的這個(gè)方法來(lái)設(shè)定自己的memoryCache.
    public GlideBuilder setMemoryCache(MemoryCache memoryCache) {
        this.memoryCache = memoryCache;
        return this;
    }

    Glide createGlide() {
        //如果builder中沒(méi)有設(shè)定memoryCache,那么采用默認(rèn)的實(shí)現(xiàn).
        if (memoryCache == null) {
            memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
        }

        return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
    }
  • 第三步:在Glide實(shí)例化完畢之后,調(diào)用自定義GlideModuleregisterComponents,并傳入當(dāng)前的Glide實(shí)例來(lái)讓使用者注冊(cè)自己的組件,其實(shí)在Glide實(shí)例化的過(guò)程中已經(jīng)注冊(cè)了默認(rèn)的組件,如果用戶(hù)定義了相同的組件,那么就會(huì)替換之前的。
    Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
        //Glide默認(rèn)注冊(cè)的組件.
        register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
        register(File.class, InputStream.class, new StreamFileLoader.Factory());
        register(int.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
        register(int.class, InputStream.class, new StreamResourceLoader.Factory());
        register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
        register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());
        register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());
        register(String.class, InputStream.class, new StreamStringLoader.Factory());
        register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());
        register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
        register(URL.class, InputStream.class, new StreamUrlLoader.Factory());
        register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
        register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());
    }

通俗的來(lái)說(shuō),注冊(cè)組件的目的就是告訴Glide,當(dāng)我們調(diào)用load(xxxx)方法時(shí),應(yīng)該用什么方式來(lái)獲取這個(gè)xxxx所指向的資源。因此,我們可以看到register的第一個(gè)參數(shù)就是我們load(xxxx)的類(lèi)型,第二個(gè)參數(shù)是對(duì)應(yīng)的輸入流,而第三個(gè)參數(shù)就是定義獲取資源的方式。
我們也就分兩個(gè)部分,在第三、四節(jié),我們分兩部分討論這兩個(gè)回調(diào)函數(shù)的用法:applyOptions/registerComponents

2.3 注意事項(xiàng)

  • 由于Glide是通過(guò)反射的方法來(lái)實(shí)例化GlideModule對(duì)象的,因此自定義的GlideModule只能有一個(gè)無(wú)參的構(gòu)造方法。
  • 可以看到,上面是支持配置多個(gè)GlideModule的,但是GlideModule的讀取順序并不能保證,因此,不要在多個(gè)GlideModule對(duì)同一個(gè)屬性進(jìn)行不同的配置。

三、applyOptions(Context context, GlideBuilder builder)方法詳解

在第二節(jié)中,我們已經(jīng)解釋過(guò),這個(gè)回調(diào)方法的目的就是為了讓使用者能通過(guò)builder定義自己的配置,而所支持的配置也就是GlideBuildersetXXX方法,它們包括:

  • setBitmapPool(BitmapPool bitmapPool)
    設(shè)置Bitmap的緩存池,用來(lái)重用Bitmap,需要實(shí)現(xiàn)BitmapPool接口,它的默認(rèn)實(shí)現(xiàn)是LruBitmapPool

  • setMemoryCache(MemoryCache memoryCache)
    設(shè)置內(nèi)存緩存,需要實(shí)現(xiàn)MemoryCache接口,默認(rèn)實(shí)現(xiàn)是LruResourceCache

  • setDiskCache(DiskCache.Factory diskCacheFactory)
    設(shè)置磁盤(pán)緩存,需要實(shí)現(xiàn)DiskCache.Factory,默認(rèn)實(shí)現(xiàn)是InternalCacheDiskCacheFactory

  • setResizeService(ExecutorService service)
    當(dāng)資源不在緩存中時(shí),需要通過(guò)這個(gè)Executor發(fā)起請(qǐng)求,默認(rèn)是實(shí)現(xiàn)是FifoPriorityThreadPoolExecutor

  • setDiskCacheService(ExecutorService service)
    讀取磁盤(pán)緩存的服務(wù),默認(rèn)實(shí)現(xiàn)是FifoPriorityThreadPoolExecutor

  • setDecodeFormat(DecodeFormat decodeFormat)
    用于控制Bitmap解碼的清晰度,DecodeFormat可選的值有PREFER_ARGB_8888/PREFER_RGB_565,默認(rèn)為PREFER_RGB_565

四、registerComponents(Context context, Glide glide)方法詳解

registerComponents相對(duì)來(lái)說(shuō)就復(fù)雜了很多,它主要和三個(gè)接口有關(guān):

  • ModelLoaderFactory
  • ModelLoader
  • DataFetcher

為了便于理解,我們先通過(guò)它內(nèi)部一個(gè)默認(rèn)Module的實(shí)現(xiàn)來(lái)看一下源碼是如何實(shí)現(xiàn)的。

我們選取是通用的加載普通圖片的url的例子,它對(duì)應(yīng)的注冊(cè)方法是下面這句:

Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
        //注冊(cè)加載網(wǎng)絡(luò)url的組件.
        register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
    }

4.1 源碼分析

4.1.1 HttpUrlGlideUrlLoader.Factory

首先看一下HttpUrlGlideUrlLoader的內(nèi)部工廠類(lèi),它實(shí)現(xiàn)了ModelLoaderFactory<T, Y>接口

public interface ModelLoaderFactory<T, Y> {
    ModelLoader<T, Y> build(Context context, GenericLoaderFactory factories);
    void teardown();
}

它要求我們返回一個(gè)ModelLoader,我們看一下HttpUrlGlideUrlLoader.Factory是怎么做的,可以看到,它返回了HttpUrlGlideUrlLoader,而它的兩個(gè)泛型參數(shù)就是我們register中指定的前兩個(gè)參數(shù)類(lèi)型。

    public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
        private final ModelCache<GlideUrl, GlideUrl> modelCache = new ModelCache<GlideUrl, GlideUrl>(500);

        @Override
        public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new HttpUrlGlideUrlLoader(modelCache);
        }

        @Override
        public void teardown() {}
    }

4.1.2 HttpUrlGlideUrlLoader

HttpUrlGlideUrlLoader實(shí)現(xiàn)了ModelLoader接口:



public interface ModelLoader<T, Y> {
    DataFetcher<Y> getResourceFetcher(T model, int width, int height);
}

ModelLoader提供了一個(gè)DataFetcher,它會(huì)去請(qǐng)求這個(gè)抽象模型所表示的數(shù)據(jù):

  • T:模型的類(lèi)型。
  • Y:一個(gè)可以被ResourceDecoder解析出數(shù)據(jù)的表示。

GlideUrl的實(shí)現(xiàn)如下:

public class HttpUrlGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {

    private final ModelCache<GlideUrl, GlideUrl> modelCache;

    public HttpUrlGlideUrlLoader() {
        this(null);
    }

    public HttpUrlGlideUrlLoader(ModelCache<GlideUrl, GlideUrl> modelCache) {
        this.modelCache = modelCache;
    }

     //最主要的方法,它決定了我們獲取數(shù)據(jù)的方式.
    @Override
    public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
        GlideUrl url = model;
        if (modelCache != null) {
            url = modelCache.get(model, 0, 0);
            if (url == null) {
                modelCache.put(model, 0, 0, model);
                url = model;
            }
        }
        return new HttpUrlFetcher(url);
    }
}

4.1.3 HttpUrlFetcher

DataFetcher就是我們讀取數(shù)據(jù)的方式,它的關(guān)鍵方法是loadData,該loadData的返回值就是我們register的第二個(gè)參數(shù):

public interface DataFetcher<T> {
    T loadData(Priority priority) throws Exception;
    void cleanup();
    String getId();
    void cancel();
}

HttpUrlFetcher實(shí)現(xiàn)了DataFetcher接口,在它的loadData方法中,通過(guò)傳入的url發(fā)起請(qǐng)求,最終返回一個(gè)InputStream

public class HttpUrlFetcher implements DataFetcher<InputStream> {
    private static final String TAG = "HttpUrlFetcher";
    private static final int MAXIMUM_REDIRECTS = 5;
    private static final HttpUrlConnectionFactory DEFAULT_CONNECTION_FACTORY = new DefaultHttpUrlConnectionFactory();

    private final GlideUrl glideUrl;
    private final HttpUrlConnectionFactory connectionFactory;

    private HttpURLConnection urlConnection;
    private InputStream stream;
    private volatile boolean isCancelled;

    public HttpUrlFetcher(GlideUrl glideUrl) {
        this(glideUrl, DEFAULT_CONNECTION_FACTORY);
    }

    HttpUrlFetcher(GlideUrl glideUrl, HttpUrlConnectionFactory connectionFactory) {
        this.glideUrl = glideUrl;
        this.connectionFactory = connectionFactory;
    }

    @Override
    public InputStream loadData(Priority priority) throws Exception {
        return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
    }

    private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
        //就是調(diào)用HttpUrlConnection請(qǐng)求.
    }

    @Override
    public void cleanup() {
        if (stream != null) {
            try {
                stream.close();
            } catch (IOException e) {
                // Ignore
            }
        }
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
    }

    @Override
    public String getId() {
        return glideUrl.getCacheKey();
    }

    @Override
    public void cancel() {
        isCancelled = true;
    }
}

4.1.4 小結(jié)

對(duì)上面做個(gè)總結(jié),整個(gè)流程如下:通過(guò)register傳入一個(gè)ModelLoaderFactory<T, Y>工廠類(lèi),該工廠生產(chǎn)的是ModelLoader<T, Y>,而這個(gè)ModelLoader會(huì)根據(jù)T返回一個(gè)DataFetcher<Y>,在DataFetcher<Y>中,我們?nèi)カ@取數(shù)據(jù)。(在上面的例子中T就是GlideUrlY就是InputStream

4.2 自定義ModuleLoader示例:用OkHttpClient替換HttpURLConnection

下面的例子來(lái)自于這篇文章:

https://futurestud.io/tutorials/glide-module-example-accepting-self-signed-https-certificates

  • 第一步:定義ModelLoaderModelLoader.Factory
public class OkHttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {

        @Override
        public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new OkHttpGlideUrlLoader(getOkHttpClient());
        }

        @Override
        public void teardown() {}
    }

    @Override
    public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
        return new OkHttpGlideUrlFetcher(mOkHttpClient, model);
    }
}
  • 第二步:ModelLoadergetResourceFetcher返回一個(gè)DataFetcher,我們給它傳入一個(gè)OkHttpClient實(shí)例,讓它通過(guò)OkHttpClient發(fā)起請(qǐng)求。
public class OkHttpGlideUrlFetcher implements DataFetcher<InputStream> {

    public OkHttpGlideUrlFetcher(OkHttpClient client, GlideUrl url) {
        this.client = client;
        this.url = url;
    }

    @Override
    public InputStream loadData(Priority priority) throws Exception {
        Request.Builder requestBuilder = new Request.Builder().url(url.toStringUrl());
        for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
            String key = headerEntry.getKey();
            requestBuilder.addHeader(key, headerEntry.getValue());
        }
        Request request = requestBuilder.build();
        Response response = client.newCall(request).execute();
        responseBody = response.body();
        if (!response.isSuccessful()) {
            throw new IOException("Request failed with code: " + response.code());
        }
        long contentLength = responseBody.contentLength();
        stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
        return stream;
    }

}

第三步:在CustomGlideModule中注冊(cè)這個(gè)組件:

public class CustomGlideModule implements GlideModule {

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        //通過(guò)builder.setXXX進(jìn)行配置.
    }

    @Override
    public void registerComponents(Context context, Glide glide) {
        //通過(guò)glide.register進(jìn)行配置.
        glide.register(GlideUrl.class, InputStream.class, new OkHttpGlideUrlLoader.Factory());
    }
}

接著我們發(fā)起一次請(qǐng)求,通過(guò)斷點(diǎn)可以發(fā)現(xiàn),調(diào)用的是OkHttpClient來(lái)進(jìn)行數(shù)據(jù)的拉取:

4.3 自定義處理的Module

上面我們分析了如何定義ModuleLoader來(lái)關(guān)聯(lián)已有的Module和最終的數(shù)據(jù)類(lèi)型,下面我們介紹一些如何定義自己的Model,也就是前面在基礎(chǔ)介紹中,所說(shuō)的load(Module)方法。

  • 第一步:定義Module的接口
public interface AutoSizeModel {
    String requestSizeUrl(int width, int height);
}
  • 第二步:實(shí)現(xiàn)Module接口
public class AutoSizeModelImpl implements AutoSizeModel {

    String mUrl;

    public AutoSizeModelImpl(String url) {
        mUrl = url;
    }

    @Override
    public String requestSizeUrl(int width, int height) {
        return mUrl;
    }
}
  • 第三步:定義ModuleLoaderModuleLoader.Factory
public class AutoSizeModelLoader extends BaseGlideUrlLoader<AutoSizeModel> {

    public static class Factory implements ModelLoaderFactory<AutoSizeModel, InputStream> {

        @Override
        public ModelLoader<AutoSizeModel, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new AutoSizeModelLoader(context);
        }

        @Override
        public void teardown() {}
    }

    public AutoSizeModelLoader(Context context) {
        super(context);
    }

    @Override
    protected String getUrl(AutoSizeModel model, int width, int height) {
        return model.requestSizeUrl(width, height);
    }
}
  • 第四步:在CustomGlideModule中進(jìn)行關(guān)聯(lián):
public class CustomGlideModule implements GlideModule {

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        //通過(guò)builder.setXXX進(jìn)行配置.
    }

    @Override
    public void registerComponents(Context context, Glide glide) {
        //通過(guò)glide.register進(jìn)行配置.
        glide.register(AutoSizeModel.class, InputStream.class, new AutoSizeModelLoader.Factory());
    }
}
  • 第五步:調(diào)用
    public void loadCustomModule(View view) {
        AutoSizeModelImpl autoSizeModel = new AutoSizeModelImpl("http://i.imgur.com/DvpvklR.png");
        Glide.with(this)
                .load(autoSizeModel)
                .diskCacheStrategy(DiskCacheStrategy.NONE)
                .skipMemoryCache(true)
                .into(mImageView);
    }

4.4 動(dòng)態(tài)指定ModelLoader

在上面的例子中,我們是在自定義的CustomGlideModule中指定了ModelModuleLoader的關(guān)聯(lián),當(dāng)然,我們也可以采用動(dòng)態(tài)指定ModelLoader的方法,也就是說(shuō),我們?nèi)サ?code>4.3中的第四步,并把第五步改成下面這樣:

    public void loadDynamicModule(View view) {
        AutoSizeModelImpl autoSizeModel = new AutoSizeModelImpl("http://i.imgur.com/DvpvklR.png");
        AutoSizeModelLoader autoSizeModelLoader = new AutoSizeModelLoader(this);
        Glide.with(this)
                .using(autoSizeModelLoader)
                .load(autoSizeModel)
                .diskCacheStrategy(DiskCacheStrategy.NONE)
                .skipMemoryCache(true)
                .into(mImageView);
    }

使用using方法,我們就可以在運(yùn)行時(shí)根據(jù)情況為同一個(gè)Module選擇不同類(lèi)型的ModuleLoader了。

五、小結(jié)

這也是我們Glide學(xué)習(xí)的最后一章,所有的源碼都可以從下面的鏈接中找到:

https://github.com/imZeJun/RepoGlideLearn

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,034評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,327評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,554評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,337評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,883評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,114評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,625評(píng)論 1 332
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,555評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,737評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,973評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,362評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,615評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,343評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,699評(píng)論 2 370

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