Glide源碼分析之一 with() + into()解析

相關文章

Glide源碼分析之一
Glide源碼分析之二
Glide源碼分析之三

文章基于3.7.0星爪。主要參考郭神的Glide源碼解析移必。

簡單使用

      String imgUrl = "https://www.baidu.com/img/bd_logo1.png?where=super";
       
        Glide.with(this).load(imgUrl).into(imageView);

        Glide.with(getApplicationContext())
                .load(imgUrl)
                .asGif()
                .asBitmap()
                .placeholder(R.mipmap.ic_launcher)
                .error(R.mipmap.ic_launcher)
                .override(300,300)
                .fitCenter()
                .centerCrop()
                .skipMemoryCache(true)
                .diskCacheStrategy(DiskCacheStrategy.NONE)
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .diskCacheStrategy(DiskCacheStrategy.RESULT)
                .diskCacheStrategy(DiskCacheStrategy.SOURCE)
                .priority(Priority.HIGH)
                .into(imageView);

Model -(ModelLoader)-> Data -(Decoder)-> Resource -(Transform)-> TransformedResource -(Transcode)-> TranscodedResource --> Target

with() 到底做了什么猪瞬?

==關鍵類==:RequestManagerRetriever陈瘦、RequestManager

首先需要注意 with方法傳入的context對象將會決定我們Glide存活的生命周期痊项。

    /** Begin a load with Glide by passing in a context.
     * <p>
     *     This method is appropriate for resources that will be used outside of the normal fragment or activity
     *     lifecycle (For example in services, or for notification thumbnails).
     * </p>
     *
     * @see #with(android.app.Activity)
     * @see #with(android.app.Fragment)
     * @see #with(android.support.v4.app.Fragment)
     * @see #with(android.support.v4.app.FragmentActivity)
     *
     * @param context Any context, will not be retained.
     * @return A RequestManager for the top level application that can be used to start a load.
     */
    public static RequestManager with(Context context) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(context);
    }
    
    
    /**
     * Begin a load with Glide that will be tied to the given {@link android.app.Activity}'s lifecycle and that uses the
     * given {@link Activity}'s default options.
     *
     * @param activity The activity to use.
     * @return A RequestManager for the given activity that can be used to start a load.
     */
    public static RequestManager with(Activity activity) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(activity);
    }

可以看到,with()方法的重載種類非常多咖驮,既可以傳入Activity托修,也可以傳入Fragment或者是Context睦刃。其實都是先調用RequestManagerRetriever的靜態(tài)get()方法得到一個RequestManagerRetriever對象枣宫,這個靜態(tài)get()方法是一個最基礎的單例模式也颤。然后再調用RequestManagerRetriever的實例get()方法翅娶,去獲取RequestManager對象竭沫。其實無非就是兩種情況而已蜕提,即傳入Application類型的參數谎势,和傳入非Application類型的參數脏榆。

傳入Application類型的參數

代碼如下:

    public static RequestManager with(Context context) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(context);
    }
    
    
    
    public RequestManager get(Context context) {
        if (context == null) {
            throw new IllegalArgumentException("You cannot start a load on a null Context");
        } else if (Util.isOnMainThread() && !(context instanceof Application)) {
            if (context instanceof FragmentActivity) {
                return get((FragmentActivity) context);
            } else if (context instanceof Activity) {
                return get((Activity) context);
            } else if (context instanceof ContextWrapper) {
                return get(((ContextWrapper) context).getBaseContext());
            }
        }

        return getApplicationManager(context);
    }


    private RequestManager getApplicationManager(Context context) {
        // Either an application context or we're on a background thread.
        if (applicationManager == null) {
            synchronized (this) {
                if (applicationManager == null) {
                    // Normally pause/resume is taken care of by the fragment we add to the fragment or activity.
                    // However, in this case since the manager attached to the application will not receive lifecycle
                    // events, we must force the manager to start resumed using ApplicationLifecycle.
                    applicationManager = new RequestManager(context.getApplicationContext(),
                            new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
                }
            }
        }

        return applicationManager;
    }

這里插一句 不知道有沒有人好奇

RequestManagerTreeNode是干嘛的呢?

上文提到獲取所有childRequestManagerFragments的RequestManager就是通過該類獲得坞生,就一個方法:getDescendants是己,作用就是基于給定的Context,獲取所有層級相關的RequestManager升熊。上下文層級由Activity或者Fragment獲得级野,ApplicationContext的上下文不會提供RequestManager的層級關系辰企,而且Application生命周期過長牢贸,所以Glide中對請求的控制只針對于Activity和Fragment。

繼續(xù)說竹习,傳入Application類型整陌,其實這是最簡單的一種情況泌辫,因為Application對象的生命周期即應用程序的生命周期甥郑,因此Glide并不需要做什么特殊的處理伍俘,它自動就是和應用程序的生命周期是同步的邪锌,如果應用程序關閉的話,Glide的加載也會同時終止癌瘾。

傳入非Application參數的情況

代碼如下:

public RequestManager get(Fragment fragment) {
        if (fragment.getActivity() == null) {
            throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
        }
        if (Util.isOnBackgroundThread()) {
            return get(fragment.getActivity().getApplicationContext());
        } else {
            FragmentManager fm = fragment.getChildFragmentManager();
            return supportFragmentGet(fragment.getActivity(), fm);
        }
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public RequestManager get(Activity activity) {
        if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            return get(activity.getApplicationContext());
        } else {
            assertNotDestroyed(activity);
            android.app.FragmentManager fm = activity.getFragmentManager();
            return fragmentGet(activity, fm);
        }
    }

如果我們是在非主線程當中使用的Glide觅丰,那么不管你是傳入的Activity還是Fragment,都會被強制當成Application來處理妨退。

方法中傳入的是Activity妇萄、FragmentActivity、v4包下的Fragment咬荷、還是app包下的Fragment冠句,最終的流程都是一樣的聚唐,那就是會調用fragmentGet()方法,向當前的Activity當中添加一個隱藏的RequestManagerFragment。

其實,最終都是調用了fragmentGet()這個方法去獲取RequestManager,

 @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
        RequestManagerFragment current = getRequestManagerFragment(fm);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
            //非常重要的一個方法,就是通過這個方法將我們空的fragment關聯(lián)到了Requestmanager關聯(lián)綁定到了一起
            //RequestManager和RequestManagerFragment都是一一對應的
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }
    
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
        RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
        if (current == null) {
            current = pendingRequestManagerFragments.get(fm);
            if (current == null) {
                current = new RequestManagerFragment();
                pendingRequestManagerFragments.put(fm, current);
                fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
                //這里
                handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
            }
        }
        return current;
    }

可以看到RequestManagerRetriever其實就是一個RequestManager的生產類同欠。

那這個RequsetManager是干什么的呢忱反?

其實RequestManager是用于管理Glide的圖片加載請求的和完成glide對象的構造。最重要的一點就是用于監(jiān)聽我們整個組件的生命周期。

那么這里為什么要添加一個隱藏的Fragment呢?

因為Glide需要知道加載的生命周期。很簡單的一個道理,如果你在某個Activity上正在加載著一張圖片,結果圖片還沒加載出來,Activity就被用戶關掉了,那么圖片還應該繼續(xù)加載嗎?當然不應該。可是Glide并沒有辦法知道Activity的生命周期,于是Glide就使用了添加隱藏Fragment的這種小技巧,因為Fragment的生命周期和Activity是同步的低滩,如果Activity被銷毀了婶溯,F(xiàn)ragment是可以監(jiān)聽到的渔扎,這樣Glide就可以捕獲這個事件并停止圖片加載了倘核。

生命周期事件的傳遞

Glide精妙設計之一

with()的源碼設計中比較重要惠况,核心的一點來說就是榨了,將Glide和組件的生命周期相掛鉤唆垃。

總體來說矾瑰,第一個with()方法的源碼還是比較好理解的。其實就是為了得到一個RequestManager對象而已尊残,然后Glide會根據我們傳入with()方法的參數來確定圖片加載的生命周期扎阶,并沒有什么特別復雜的邏輯,就是一個準備好基礎配置的方法。

load()方法到底做了什么?

==關鍵詞==
DrawableTypeRequest,GenericRequestBuilder(是我們在glide當中配置所有參數的父類枝冀,也就是說,只要是在glide當中配置參數鸵钝,就一定是通過這個類或者他的子類來實現(xiàn)的)

ModelLoader(通過數據來源,將數據來源加載成原始數據)

RequestTracker(直譯的話就是請求追蹤器申屹,跟蹤圖片請求的整個周期决乎,可以做取消丑蛤,重啟一些失敗的圖片請求生命周期的管理主要由RequestTracker和TargetTracker處理砰盐。builder.createGlide() 創(chuàng)建Glide對象列疗。

由于with()方法返回的是一個RequestManager對象疤剑,那么很容易就能想到吨悍,load()方法是在RequestManager類當中的,所以說我們首先要看的就是RequestManager這個類。

那么我們先來看load()方法菱蔬,這個方法中的邏輯是非常簡單的掉房,只有一行代碼讨阻,就是先調用了fromString()方法醇坝,再調用load()方法摊腋,然后把傳入的圖片URL地址傳進去。(也可以從源碼中看出础锐,load有多個重載方法耀怜,支持String,file,Integer,byte等各種數據來源)

而fromString()方法也極為簡單,就是調用了loadGeneric()方法,并且指定參數為String.class,因為load()方法傳入的是一個字符串參數恩够。那么看上去疆股,好像主要的工作都是在loadGeneric()方法中進行的了峰鄙。

 /**
     * Returns a request builder to load the given {@link java.lang.String}.
     * signature.
     *
     * @see #fromString()
     * @see #load(Object)
     *
     * @param string A file path, or a uri or url handled by {@link com.bumptech.glide.load.model.UriLoader}.
     */
    public DrawableTypeRequest<String> load(String string) {
        return (DrawableTypeRequest<String>) fromString().load(string);
    }
    
     public DrawableTypeRequest<String> fromString() {
     //傳入的是String的class對象
        return loadGeneric(String.class);
    }
    
     private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
        ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);as
        ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
                Glide.buildFileDescriptorModelLoader(modelClass, context);
        if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
            throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
                    + " which there is a registered ModelLoader, if you are using a custom model, you must first call"
                    + " Glide#register with a ModelLoaderFactory for your custom model class");
        }

        return optionsApplier.apply(
                new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
                        glide, requestTracker, lifecycle, optionsApplier));
    }

在loadGeneric()方法第一行可以看到有一句Glide.buildStreamModelLoader(modelClass, context)點進去可以看到他不僅返回了ModelLoader對象浸间,而且還初始化了Glide。點進去看看:

/**
     * A method to build a {@link ModelLoader} for the given model that produces {@link InputStream}s using a registered
     * factory.
     *
     * @see #buildModelLoader(Class, Class, android.content.Context)
     */
    public static <T> ModelLoader<T, InputStream> buildStreamModelLoader(Class<T> modelClass, Context context) {
        return buildModelLoader(modelClass, InputStream.class, context);
    }
    
    
     public static <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass,
            Context context) {
         if (modelClass == null) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Unable to load null model, setting placeholder only");
            }
            return null;
        }
        return Glide.get(context).getLoaderFactory().buildModelLoader(modelClass, resourceClass);
    }
    
    /**
     * Get the singleton.
     *
     * @return the singleton
     */
    public static Glide get(Context context) {
        if (glide == null) {
            synchronized (Glide.class) {
                if (glide == null) {
                    Context applicationContext = context.getApplicationContext();
                    //解析清單文件配置的自定義GlideModule的metadata標簽吟榴,返回一個GlideModule集合
                    List<GlideModule> modules = new ManifestParser(applicationContext).parse();

                    GlideBuilder builder = new GlideBuilder(applicationContext);
                    for (GlideModule module : modules) {
                        module.applyOptions(applicationContext, builder);
                    }
                    //初始化了glide的單例魁蒜。
                    glide = builder.createGlide();
                    for (GlideModule module : modules) {
                        module.registerComponents(applicationContext, glide);
                    }
                }
            }
        }

        return glide;
    }

我們看到通過反射的方式獲取我們在清單文件中聲明的自定義的GlideModule對象。在獲取到GlideModule集合之后吩翻,遍歷了集合并調用相應的applyOptions和registerComponents方法兜看,而Glide對象的生成是通過GlideBuilder的createGlide方法創(chuàng)建。(底下有例子)

看到這里不知道大家會不會跟我有一樣的疑問狭瞎,就是

GlideModule是個啥细移?干嘛用的?

可以通過GlideBuilder進行一些延遲的配置和ModelLoaders的注冊熊锭。注意:
所有的實現(xiàn)的module必須是public的弧轧,并且只擁有一個空的構造函數,以便Glide懶加載的時候可以通過反射調用球涛。
GlideModule是不能指定調用順序的劣针。因此在創(chuàng)建多個GlideModule的時候,要注意不同Module之間的setting不要沖突了亿扁。

接下來看一下glide = builder.createGlide();這句代碼做了什么

Glide createGlide() {
        if (sourceService == null) {
        //查看核心線程數
            final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
            //初始化線程池
            sourceService = new FifoPriorityThreadPoolExecutor(cores);
        }
        if (diskCacheService == null) {
            diskCacheService = new FifoPriorityThreadPoolExecutor(1);
        }

        MemorySizeCalculator calculator = new MemorySizeCalculator(context);
        //初始化bitmapPool
        //圖片池用的是targetPoolSize(即一般是緩存大小是屏幕的寬高4*4).
        if (bitmapPool == null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
               
                int size = calculator.getBitmapPoolSize();
                bitmapPool = new LruBitmapPool(size);
            } else {
                bitmapPool = new BitmapPoolAdapter();
            }
        }

        ////內存緩存用的是targetMemoryCacheSize (即一般是緩存大小是屏幕的寬 * 高 * 4 * 2)
        if (memoryCache == null) {
            memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
        }

        if (diskCacheFactory == null) {
        //磁盤緩存 默認大小:250 MB,默認目錄:image_manager_disk_cache.
        //Glide默認是用InternalCacheDiskCacheFactory類來創(chuàng)建硬盤緩存的捺典,這個類會在應用的內部緩存目錄下面創(chuàng)建一個最大容量250MB的緩存文件夾,使用這個緩存目錄而不用sd卡从祝,意味著除了本應用之外襟己,其他應用是不能訪問緩存的圖片文件的。
            diskCacheFactory = new InternalCacheDiskCacheFactory(context);
        }

        if (engine == null) {
        //引擎初始化
            engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
        }

        if (decodeFormat == null) {
            decodeFormat = DecodeFormat.DEFAULT;
        }

        return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
    }

看到這里其實大體的glide所做的內容我們已經清楚牍陌,其實Glide還支持動態(tài)的緩存大小調整擎浴,在存在大量圖片的Activity/Fragment中,可以通過setMemoryCategory方法來提高Glide的內存緩存大小毒涧,從而加快圖片的加載速度贮预。

Glide.get(getApplicationContext()).setMemoryCategory(MemoryCategory.HIGH);

MemoryCategory有3個值可供選擇:

  1. MemoryCategory.HIGH(初始緩存大小的1.5倍)
  2. MemoryCategory.NORMAL(初始緩存大小的1倍)
  3. MemoryCategory.LOW(初始緩存大小的0.5倍)

Glide磁盤緩存策略分為四種,默認的是RESULT:

  1. ALL:緩存原圖和處理圖
  2. NONE:什么都不緩存
  3. SOURCE:只緩存原圖
  4. RESULT:只緩存處理圖
這么惡心的ModelLoader到底是干嘛用的?

ModelLoader對象是用于加載圖片各種資源的,而我們給load()方法傳入不同類型的參數仿吞,這里也會得到不同的ModelLoader對象滑频。該接口有兩個目的:將任意復雜的model轉換為可以被decode的數據類型,允許model結合View的尺寸獲取特定大小的資源













最后我們可以看到唤冈,loadGeneric()方法是要返回一個DrawableTypeRequest對象的峡迷,因此在loadGeneric()方法的最后又去new了一個DrawableTypeRequest對象,然后把剛才獲得的ModelLoader對象你虹,還有一大堆雜七雜八的東西都傳了進去绘搞。

那DrawableTypeRequest是做什么的呢

DrawableTypeRequest
這個類中的代碼本身就不多,主要看一下構造方法和我們會用到的兩個比較重要的方法asGif()和asBitmap()傅物。這兩個方法分別是用于強制指定加載靜態(tài)圖片和動態(tài)圖片夯辖。將我們的圖片轉化為BitmapTypeRequest或者GifTypeRequest兩種圖片格式。

asBitmap()與asGif()

不管我們傳入的是一張普通圖片董饰,還是一張GIF圖片楼雹,Glide都會自動進行判斷,并且可以正確地把它解析并展示出來尖阔。

但是如果我想指定圖片的格式該怎么辦呢?就比如說榨咐,我希望加載的這張圖必須是一張靜態(tài)圖片介却,我不需要Glide自動幫我判斷它到底是靜圖還是GIF圖。

好的我們只需要反向操作下块茁,兩種情況:

1. 傳入gif鏈接齿坷,使用asBitmap()方法,gif圖則無法正常播放数焊,而是會停在第一幀的圖片永淌。
2. 傳入靜態(tài)圖片鏈接,使用asGif()方法佩耳,會顯示error()設置的圖片遂蛀,沒錯,如果指定了只能加載動態(tài)圖片干厚,而傳入的圖片卻是一張靜圖的話李滴,那么結果自然就只有加載失敗。

看一下代碼:

  DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
            ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
            RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
        super(context, modelClass,
                buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
                        GlideDrawable.class, null),
                glide, requestTracker, lifecycle);
        this.streamModelLoader = streamModelLoader;
        this.fileDescriptorModelLoader = fileDescriptorModelLoader;
        this.optionsApplier = optionsApplier;
    }

  
    public BitmapTypeRequest<ModelType> asBitmap() {
        return optionsApplier.apply(new BitmapTypeRequest<ModelType>(this, streamModelLoader,
                fileDescriptorModelLoader, optionsApplier));
    }

  
    public GifTypeRequest<ModelType> asGif() {
        return optionsApplier.apply(new GifTypeRequest<ModelType>(this, streamModelLoader, optionsApplier));
    }

而從源碼中可以看出蛮瞄,它們分別又創(chuàng)建了一個BitmapTypeRequest和GifTypeRequest所坯,如果沒有進行強制指定的話,那默認就是使用DrawableTypeRequest挂捅。
好的芹助,那么我們再回到RequestManager的load()方法中。剛才已經分析過了,fromString()方法會返回一個DrawableTypeRequest對象状土,接下來會調用這個對象的load()方法无蜂,把圖片的URL地址傳進去。點進去看看load()是在DrawableRequestBuilder類中声诸,我們也可以看到DrawableRequestBuilder是DrawableTypeRequest的父類酱讶。看代碼:

   @Override
    public DrawableRequestBuilder<ModelType> load(ModelType model) {
        super.load(model);
        return this;
    }
   
   
       public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
        this.model = model;
        isModelSet = true;  //注意這個boolean 在into方法時我們會講到
        return this;
    } 

其實這個model是什么彼乌,說白了就是我們傳進來的數據對象泻肯。就是數據來源,可以支持多種類型慰照,圖片灶挟,url,字節(jié)毒租,文件等稚铣。

DrawableTypeRequest的父類是DrawableRequestBuilder,DrawableRequestBuilder中有很多個方法墅垮,這些方法其實就是Glide絕大多數的API了惕医。里面有不少我們在上篇文章中已經用過了,比如說placeholder()方法算色、error()方法抬伺、diskCacheStrategy()方法、override()方法灾梦,當然還有最重要的into()方法峡钓。其實通過源碼得知,DrawableRequestBuilder在這些方法中也沒有做什么處理若河,主要是通過父類的方法來做相應處理能岩。

最重要的來了,在DrawableRequestBuilder類中有一個into()方法萧福,也就是說拉鹃,最終load()方法返回的其實就是一個DrawableTypeRequest對象。

    @Override
    public Target<GlideDrawable> into(ImageView view) {
        return super.into(view);
    }
Glide精妙設計之二

其實通過Glide支持鏈式調用就可以知道鲫忍,他是使用了建造者模式構建的毛俏,類似于我們的Dialog,Retrofit饲窿。泛型煌寇,接口的使用。

相關文章:

獲取到系統(tǒng)可用的處理器核心數目

glideModule使用例子

Class的isAssignableFrom方法

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末逾雄,一起剝皮案震驚了整個濱河市阀溶,隨后出現(xiàn)的幾起案子腻脏,更是在濱河造成了極大的恐慌,老刑警劉巖银锻,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件永品,死亡現(xiàn)場離奇詭異,居然都是意外死亡击纬,警方通過查閱死者的電腦和手機鼎姐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來更振,“玉大人炕桨,你說我怎么就攤上這事】贤螅” “怎么了献宫?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長实撒。 經常有香客問我姊途,道長,這世上最難降的妖魔是什么知态? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任捷兰,我火速辦了婚禮,結果婚禮上负敏,老公的妹妹穿的比我還像新娘寂殉。我一直安慰自己,他們只是感情好原在,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著彤叉,像睡著了一般庶柿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秽浇,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天浮庐,我揣著相機與錄音,去河邊找鬼柬焕。 笑死审残,一個胖子當著我的面吹牛,可吹牛的內容都是我干的斑举。 我是一名探鬼主播搅轿,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼富玷!你這毒婦竟也來了璧坟?” 一聲冷哼從身側響起既穆,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雀鹃,沒想到半個月后幻工,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡黎茎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年囊颅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片傅瞻。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡踢代,死狀恐怖,靈堂內的尸體忽然破棺而出俭正,到底是詐尸還是另有隱情奸鬓,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布掸读,位于F島的核電站串远,受9級特大地震影響,放射性物質發(fā)生泄漏儿惫。R本人自食惡果不足惜澡罚,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肾请。 院中可真熱鬧留搔,春花似錦、人聲如沸铛铁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饵逐。三九已至括眠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間倍权,已是汗流浹背掷豺。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留薄声,地道東北人当船。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像默辨,于是被迫代替她去往敵國和親德频。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容