Fresco图片框架实现原理(一):初始化

使用Fresco之前,一定先要进行初始化,一般初始化的工作会在Application.onCreate()完成,当然也可以在使用Drawee之前完成。
Fresco本身提供了两种初始化方式,一种是使用使用默认配置初始化,另一种是使用用户自定义配置

开始初始化

Fresco.initialized(context)

默认参数进行初始化

public static void initialize(Context context) {
  initialize(context, null, null);
}

其中ImagePipeline 负责获取图像数据,可以是网络图片,也可以是本地图片。这里用一个 FactoryImagePipelineFactory 来创建默认的 ImagePipleline。

用户自定义配置

 public static void initialize(
      Context context, ImagePipelineConfig imagePipelineConfig) {
    initialize(context, imagePipelineConfig, null);
  }

归根接地

  /** Initializes Fresco with the specified config. */
  public static void initialize(
      Context context,
      @Nullable ImagePipelineConfig imagePipelineConfig,
      @Nullable DraweeConfig draweeConfig) {
    if (sIsInitialized) {
      FLog.w(
          TAG,
          "Fresco has already been initialized! `Fresco.initialize(...)` should only be called " +
            "1 single time to avoid memory leaks!");
    } else {
      sIsInitialized = true;
    }
    // we should always use the application context to avoid memory leaks
   总是使用Application的Context来避免内存泄漏
    context = context.getApplicationContext();
     
   第一. 初始化ImagePipelineFactory
    if (imagePipelineConfig == null) {
      ImagePipelineFactory.initialize(context);
    } else {
      ImagePipelineFactory.initialize(imagePipelineConfig);
    }
   第二, 初始化Drawee模块
    initializeDrawee(context, draweeConfig);
  }

从上面代码可以看出初始化其实是做了两个工作,一是初始化ImagePipelineFactory,二是初始化Drawee模块。
ImagePipelineFactory.initialize()方法和initializeDrawee(context, draweeConfig)方法
initializeDrawee方法

  /** Initializes Drawee with the specified config. */
用具体的draweeConfig来初始化
  private static void initializeDrawee(
      Context context,  DraweeConfig draweeConfig) {
    sDraweeControllerBuilderSupplier =
        new PipelineDraweeControllerBuilderSupplier(context, draweeConfig);
    SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);
  }

这段代码其实是在设置SimpleDraweeView模块的DraweeController.

ImagePipelineFactory.initialize()方法

初始化ImagePipelineFactory
public static void initialize(ImagePipelineConfig imagePipelineConfig) {
    sInstance = new ImagePipelineFactory(imagePipelineConfig);
  }

  私有final变量 ImagePipelineConfig 
  private final ImagePipelineConfig mConfig;
  负责管理缓存项目驱逐的内存缓存堆栈层。
  private CountingMemoryCache<CacheKey, CloseableImage>   mBitmapCountingMemoryCache;
  图像管道内存缓存接口
  private MemoryCache<CacheKey, CloseableImage> mBitmapMemoryCache;
  负责管理缓存项目驱逐的内存缓存堆栈层。 解码内存
  private CountingMemoryCache<CacheKey, PooledByteBuffer> mEncodedCountingMemoryCache;
   图像管道内存缓存接口 ,解码缓存
  private MemoryCache<CacheKey, PooledByteBuffer> mEncodedMemoryCache;
  BufferedDiskCache提供了get和put操作来负责调度磁盘缓存读/写
  private BufferedDiskCache mMainBufferedDiskCache;
  主文件缓存
  private FileCache mMainFileCache;
  图片解码接口
  private ImageDecoder mImageDecoder;
  图像管道配置
  private ImagePipeline mImagePipeline;
  生产者工厂
  private ProducerFactory mProducerFactory;
  private ProducerSequenceFactory mProducerSequenceFactory;
 小图片磁盘缓存
  private BufferedDiskCache mSmallImageBufferedDiskCache;
  小图片文件缓存
  private FileCache mSmallImageFileCache;
  private MediaVariationsIndex mMediaVariationsIndex;

  private PlatformBitmapFactory mPlatformBitmapFactory;
  平台解码器
  private PlatformDecoder mPlatformDecoder;

  private AnimatedFactory mAnimatedFactory;

  public ImagePipelineFactory(ImagePipelineConfig config) {
    用外面ImagePipelineConfig 初始化final的config,不再变化,这个变量开发者 可自行配置
    mConfig = Preconditions.checkNotNull(config);
    mThreadHandoffProducerQueue = new ThreadHandoffProducerQueue(
        config.getExecutorSupplier().forLightweightBackgroundTasks());
  }

ImagePipelineFactory是图像管道的工厂类,这个类从其他库构造管道及其依赖关系。其目的是构造图像管道类ImagePipeline 。可以通过下面方法获取图像管道

  public ImagePipeline getImagePipeline() {
    if (mImagePipeline == null) {
      mImagePipeline =
          new ImagePipeline(
              getProducerSequenceFactory(),
              mConfig.getRequestListeners(),
              mConfig.getIsPrefetchEnabledSupplier(),
              getBitmapMemoryCache(),
              getEncodedMemoryCache(),
              getMainBufferedDiskCache(),
              getSmallImageBufferedDiskCache(),
              mConfig.getCacheKeyFactory(),
              mThreadHandoffProducerQueue,
              Suppliers.of(false));
    }
    return mImagePipeline;
  }

ImagePipelineFactory类中的所有变量都是通过ImagePipelineConfig的配置构造的。目的是为了构造出ImagePipeline对象

如果初始化时Config == null ,在ImagePipelineFactory中会初始化默认的ImagePipelineConfig配置

 /**
   * Initializes {@link ImagePipelineFactory} with default config.
   */
  public static void initialize(Context context) {
    initialize(ImagePipelineConfig.newBuilder(context).build());
  }

前面说了,ImagePipelineConfig类可以开发人员自行配置,只需要在builder模式下set项目需要改变的量,其他成员变量使用默认的即可。
ImagePipelineConfig是图像管道库的主配置类,

public class ImagePipelineConfig {

 // If a member here is marked @Nullable, it must be constructed by ImagePipelineFactory
 // on demand if needed.

 // There are a lot of parameters in this class. Please follow strict alphabetical order.
 private final Bitmap.Config mBitmapConfig;
 private final Supplier<MemoryCacheParams> mBitmapMemoryCacheParamsSupplier;
 private final CountingMemoryCache.CacheTrimStrategy mBitmapMemoryCacheTrimStrategy;
 private final CacheKeyFactory mCacheKeyFactory;
 private final Context mContext;
 private final boolean mDownsampleEnabled;
 private final FileCacheFactory mFileCacheFactory;
 private final Supplier<MemoryCacheParams> mEncodedMemoryCacheParamsSupplier;
 private final ExecutorSupplier mExecutorSupplier;
 private final ImageCacheStatsTracker mImageCacheStatsTracker;
 @Nullable private final ImageDecoder mImageDecoder;
 private final Supplier<Boolean> mIsPrefetchEnabledSupplier;
 private final DiskCacheConfig mMainDiskCacheConfig;
 private final MemoryTrimmableRegistry mMemoryTrimmableRegistry;
 private final NetworkFetcher mNetworkFetcher;
 @Nullable private final PlatformBitmapFactory mPlatformBitmapFactory;
 private final PoolFactory mPoolFactory;
 private final ProgressiveJpegConfig mProgressiveJpegConfig;
 private final Set<RequestListener> mRequestListeners;
 private final boolean mResizeAndRotateEnabledForNetwork;
 private final DiskCacheConfig mSmallImageDiskCacheConfig;
 @Nullable private final ImageDecoderConfig mImageDecoderConfig;
 private final ImagePipelineExperiments mImagePipelineExperiments;

 private static DefaultImageRequestConfig
     sDefaultImageRequestConfig = new DefaultImageRequestConfig();

 private ImagePipelineConfig(Builder builder) {
   // We have to build experiments before the rest
   mImagePipelineExperiments = builder.mExperimentsBuilder.build();
   mBitmapMemoryCacheParamsSupplier =
       builder.mBitmapMemoryCacheParamsSupplier == null ?
           new DefaultBitmapMemoryCacheParamsSupplier(
               (ActivityManager) builder.mContext.getSystemService(Context.ACTIVITY_SERVICE)) :
           builder.mBitmapMemoryCacheParamsSupplier;
   mBitmapMemoryCacheTrimStrategy =
       builder.mBitmapMemoryCacheTrimStrategy == null ?
           new BitmapMemoryCacheTrimStrategy() :
           builder.mBitmapMemoryCacheTrimStrategy;
   mBitmapConfig =
       builder.mBitmapConfig == null ?
           Bitmap.Config.ARGB_8888 :
           builder.mBitmapConfig;
   mCacheKeyFactory =
       builder.mCacheKeyFactory == null ?
           DefaultCacheKeyFactory.getInstance() :
           builder.mCacheKeyFactory;
   mContext = Preconditions.checkNotNull(builder.mContext);
   mFileCacheFactory = builder.mFileCacheFactory == null ?
       new DiskStorageCacheFactory(new DynamicDefaultDiskStorageFactory()) :
       builder.mFileCacheFactory;
   mDownsampleEnabled = builder.mDownsampleEnabled;
   mEncodedMemoryCacheParamsSupplier =
       builder.mEncodedMemoryCacheParamsSupplier == null ?
           new DefaultEncodedMemoryCacheParamsSupplier() :
           builder.mEncodedMemoryCacheParamsSupplier;
   mImageCacheStatsTracker =
       builder.mImageCacheStatsTracker == null ?
           NoOpImageCacheStatsTracker.getInstance() :
           builder.mImageCacheStatsTracker;
   mImageDecoder = builder.mImageDecoder;
   mIsPrefetchEnabledSupplier =
       builder.mIsPrefetchEnabledSupplier == null ?
           new Supplier<Boolean>() {
             @Override
             public Boolean get() {
               return true;
             }
           } :
           builder.mIsPrefetchEnabledSupplier;
   mMainDiskCacheConfig =
       builder.mMainDiskCacheConfig == null ?
           getDefaultMainDiskCacheConfig(builder.mContext) :
           builder.mMainDiskCacheConfig;
   mMemoryTrimmableRegistry =
       builder.mMemoryTrimmableRegistry == null ?
           NoOpMemoryTrimmableRegistry.getInstance() :
           builder.mMemoryTrimmableRegistry;
   mNetworkFetcher =
       builder.mNetworkFetcher == null ?
           new HttpUrlConnectionNetworkFetcher() :
           builder.mNetworkFetcher;
   mPlatformBitmapFactory = builder.mPlatformBitmapFactory;
   mPoolFactory =
       builder.mPoolFactory == null ?
           new PoolFactory(PoolConfig.newBuilder().build()) :
           builder.mPoolFactory;
   mProgressiveJpegConfig =
       builder.mProgressiveJpegConfig == null ?
           new SimpleProgressiveJpegConfig() :
           builder.mProgressiveJpegConfig;
   mRequestListeners =
       builder.mRequestListeners == null ?
           new HashSet<RequestListener>() :
           builder.mRequestListeners;
   mResizeAndRotateEnabledForNetwork = builder.mResizeAndRotateEnabledForNetwork;
   mSmallImageDiskCacheConfig =
       builder.mSmallImageDiskCacheConfig == null ?
           mMainDiskCacheConfig :
           builder.mSmallImageDiskCacheConfig;
   mImageDecoderConfig = builder.mImageDecoderConfig;
   // Below this comment can't be built in alphabetical order, because of dependencies
   int numCpuBoundThreads = mPoolFactory.getFlexByteArrayPoolMaxNumThreads();
   mExecutorSupplier =
       builder.mExecutorSupplier == null ?
           new DefaultExecutorSupplier(numCpuBoundThreads) : builder.mExecutorSupplier;
   // Here we manage the WebpBitmapFactory implementation if any
   WebpBitmapFactory webpBitmapFactory = mImagePipelineExperiments.getWebpBitmapFactory();
   if (webpBitmapFactory != null) {
     BitmapCreator bitmapCreator = new HoneycombBitmapCreator(getPoolFactory());
     setWebpBitmapFactory(webpBitmapFactory, mImagePipelineExperiments, bitmapCreator);
   } else {
     // We check using introspection only if the experiment is enabled
     if (mImagePipelineExperiments.isWebpSupportEnabled() &&
         WebpSupportStatus.sIsWebpSupportRequired) {
       webpBitmapFactory = WebpSupportStatus.loadWebpBitmapFactoryIfExists();
       if (webpBitmapFactory != null) {
         BitmapCreator bitmapCreator = new HoneycombBitmapCreator(getPoolFactory());
         setWebpBitmapFactory(webpBitmapFactory, mImagePipelineExperiments, bitmapCreator);
       }
     }
   }
 }

ImagePipelineConfig.newBuilderm看出ImagePipelineConfig的成员变量,有这么多。

  public static class Builder {
    Bitmap配置 565或者8888
    private Bitmap.Config mBitmapConfig;
    内存缓存参数提供者
    private Supplier<MemoryCacheParams> mBitmapMemoryCacheParamsSupplier;
    private CountingMemoryCache.CacheTrimStrategy mBitmapMemoryCacheTrimStrategy;
    缓存Key工厂
    private CacheKeyFactory mCacheKeyFactory;
    private final Context mContext;
    是否下采样
    private boolean mDownsampleEnabled = false;
    编码内存缓存提供者
    private Supplier<MemoryCacheParams> mEncodedMemoryCacheParamsSupplier;
    private ExecutorSupplier mExecutorSupplier;
    private ImageCacheStatsTracker mImageCacheStatsTracker;
    图片解码器
    private ImageDecoder mImageDecoder;
    private Supplier<Boolean> mIsPrefetchEnabledSupplier;
    磁盘缓存注册表
    private DiskCacheConfig mMainDiskCacheConfig;
    内存可修改注册表
    private MemoryTrimmableRegistry mMemoryTrimmableRegistry;
    private NetworkFetcher mNetworkFetcher;
    private PlatformBitmapFactory mPlatformBitmapFactory;
    private PoolFactory mPoolFactory;
    private ProgressiveJpegConfig mProgressiveJpegConfig;
    private Set<RequestListener> mRequestListeners;
    private boolean mResizeAndRotateEnabledForNetwork = true;
    小图片磁盘缓存配置
    private DiskCacheConfig mSmallImageDiskCacheConfig;
    private FileCacheFactory mFileCacheFactory;
    图片解码配置
    private ImageDecoderConfig mImageDecoderConfig;
    private final ImagePipelineExperiments.Builder mExperimentsBuilder
        = new ImagePipelineExperiments.Builder(this);

项目中用到的ImagePipelineConfig。

package com.sohu.ink;

import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;

import com.facebook.cache.disk.DiskCacheConfig;
import com.facebook.common.disk.NoOpDiskTrimmableRegistry;
import com.facebook.common.internal.Supplier;
import com.facebook.common.memory.MemoryTrimType;
import com.facebook.common.memory.MemoryTrimmable;
import com.facebook.common.memory.NoOpMemoryTrimmableRegistry;
import com.facebook.common.util.ByteConstants;
import com.facebook.imagepipeline.cache.MemoryCacheParams;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.facebook.imagepipeline.core.ImagePipelineFactory;

/**
 * <p/>
 * 功能 :
 * <p/>
 * <p>
 * <p>Copyright sohu.com 2017 All right reserved</p>
 *
 * @author ketu 时间 2017/11/22
 * @version 2.0
 *          <p>
 *          最后修改人 无
 * @email ketu@sohu-inc.com
 * @link {http://blog.csdn.net/biezhihua/article/details/49893323,
 * http://blog.csdn.net/honjane/article/details/65629799}
 */

public class ImagePipelineConfigUtils {
    //分配的可用内存
    public static final int MAX_HEAP_SIZE = (int) Runtime.getRuntime().maxMemory();

    //使用的缓存数量
    public static final int MAX_MEMORY_CACHE_SIZE = MAX_HEAP_SIZE / 4;

    //小图极低磁盘空间缓存的最大值(特性:可将大量的小图放到额外放在另一个磁盘空间防止大图占用磁盘空间而删除了大量的小图)
    public static final int MAX_SMALL_DISK_VERYLOW_CACHE_SIZE = 20 * ByteConstants.MB;

    //小图低磁盘空间缓存的最大值(特性:可将大量的小图放到额外放在另一个磁盘空间防止大图占用磁盘空间而删除了大量的小图)
    public static final int MAX_SMALL_DISK_LOW_CACHE_SIZE = 60 * ByteConstants.MB;

    //默认图极低磁盘空间缓存的最大值
    public static final int MAX_DISK_CACHE_VERYLOW_SIZE = 20 * ByteConstants.MB;

    //默认图低磁盘空间缓存的最大值
    public static final int MAX_DISK_CACHE_LOW_SIZE = 60 * ByteConstants.MB;

    //默认图磁盘缓存的最大值
    public static final int MAX_DISK_CACHE_SIZE = 100 * ByteConstants.MB;


    //小图所放路径的文件夹名
    public static final String IMAGE_PIPELINE_SMALL_CACHE_DIR = "ImagePipelineCacheSmall";

    //默认图所放路径的文件夹名
    public static final String IMAGE_PIPELINE_CACHE_DIR = "ImagePipelineCacheDefault";

    //内存配置
    public static final int MAX_CACHE_ENTRIES = 56;
    public static final int MAX_CACHE_ASHM_ENTRIES = 128;
    public static final int MAX_CACHE_EVICTION_SIZE = 30;//30M,越小,gc越频繁,越卡,合理设置此项,含义是:被fresco标记为回收的大小积累到这个值就会gc
    public static final int MAX_CACHE_EVICTION_ENTRIES = 5;


    public static ImagePipelineConfig getDefaultImagePipelineConfig(Context context) {

        //内存配置
        final MemoryCacheParams bitmapCacheParams = new MemoryCacheParams(
                MAX_MEMORY_CACHE_SIZE,// 内存缓存中总图片的最大大小,以字节为单位。
                Integer.MAX_VALUE,// 内存缓存中图片的最大数量。
                MAX_MEMORY_CACHE_SIZE,// 内存缓存中准备清除但尚未被删除的总图片的最大大小,以字节为单位。
                Integer.MAX_VALUE,// 内存缓存中准备清除的总图片的最大数量。
                Integer.MAX_VALUE);// 内存缓存中单个图片的最大大小。

        //修改内存图片缓存数量,空间策略(这个方式有点恶心)
        Supplier<MemoryCacheParams> mSupplierMemoryCacheParams = new Supplier<MemoryCacheParams>() {
            @Override
            public MemoryCacheParams get() {
                //return bitmapCacheParams;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    return new MemoryCacheParams(MAX_MEMORY_CACHE_SIZE, MAX_CACHE_ENTRIES, MAX_CACHE_EVICTION_SIZE, MAX_CACHE_EVICTION_ENTRIES, 1);
                } else {
                    return new MemoryCacheParams(
                            MAX_MEMORY_CACHE_SIZE,
                            MAX_CACHE_ASHM_ENTRIES,
                            Integer.MAX_VALUE,
                            Integer.MAX_VALUE,
                            Integer.MAX_VALUE);
                }
            }
        };

        //小图片的磁盘配置
        DiskCacheConfig diskSmallCacheConfig = DiskCacheConfig.newBuilder(context)
                .setBaseDirectoryPath(context.getApplicationContext().getCacheDir())//缓存图片基路径
                .setBaseDirectoryName(IMAGE_PIPELINE_SMALL_CACHE_DIR)//文件夹名
                .setMaxCacheSize(MAX_DISK_CACHE_SIZE)//默认缓存的最大大小。
                .setMaxCacheSizeOnLowDiskSpace(MAX_SMALL_DISK_LOW_CACHE_SIZE)//缓存的最大大小,使用设备时低磁盘空间。
                .setMaxCacheSizeOnVeryLowDiskSpace(MAX_SMALL_DISK_VERYLOW_CACHE_SIZE)//缓存的最大大小,当设备极低磁盘空间
                .setDiskTrimmableRegistry(NoOpDiskTrimmableRegistry.getInstance())
                .build();

        //默认图片的磁盘配置
        DiskCacheConfig diskCacheConfig = DiskCacheConfig.newBuilder(context)
                .setBaseDirectoryPath(context.getApplicationContext().getCacheDir())//缓存图片基路径,,Environment.getExternalStorageDirectory().getAbsoluteFile()
                .setBaseDirectoryName(IMAGE_PIPELINE_CACHE_DIR)//文件夹名
                .setMaxCacheSize(MAX_DISK_CACHE_SIZE)//默认缓存的最大大小。
                .setMaxCacheSizeOnLowDiskSpace(MAX_DISK_CACHE_LOW_SIZE)//缓存的最大大小,使用设备时低磁盘空间。
                .setMaxCacheSizeOnVeryLowDiskSpace(MAX_DISK_CACHE_VERYLOW_SIZE)//缓存的最大大小,当设备极低磁盘空间
                .setDiskTrimmableRegistry(NoOpDiskTrimmableRegistry.getInstance())
                .build();


        // 就是这段代码,用于清理缓存
        NoOpMemoryTrimmableRegistry.getInstance().registerMemoryTrimmable(new MemoryTrimmable() {
            @Override
            public void trim(MemoryTrimType trimType) {
                final double suggestedTrimRatio = trimType.getSuggestedTrimRatio();

                //Loger.d(String.format("onCreate suggestedTrimRatio : %d", suggestedTrimRatio));
                if (MemoryTrimType.OnCloseToDalvikHeapLimit.getSuggestedTrimRatio() == suggestedTrimRatio
                        || MemoryTrimType.OnSystemLowMemoryWhileAppInBackground.getSuggestedTrimRatio() == suggestedTrimRatio
                        || MemoryTrimType.OnSystemLowMemoryWhileAppInForeground.getSuggestedTrimRatio() == suggestedTrimRatio
                        ) {
                    ImagePipelineFactory.getInstance().getImagePipeline().clearMemoryCaches();
                }
            }
        });

        //最终缓存图片配置
        ImagePipelineConfig.Builder configBuilder = ImagePipelineConfig.newBuilder(context)
                .setBitmapsConfig(Bitmap.Config.ARGB_8888)
                .setBitmapMemoryCacheParamsSupplier(mSupplierMemoryCacheParams)
                .setSmallImageDiskCacheConfig(diskSmallCacheConfig)
                .setMainDiskCacheConfig(diskCacheConfig)
                .setMemoryTrimmableRegistry(NoOpMemoryTrimmableRegistry.getInstance())
                .setResizeAndRotateEnabledForNetwork(true)
                .setDownsampleEnabled(true);//设置下采样,适应png,webp


        return configBuilder.build();
    }
}

初始化Drawee模块

在Frseco.initize()代码中

  /** Initializes Drawee with the specified config. */
  private static void initializeDrawee(
      Context context,
      @Nullable DraweeConfig draweeConfig) {
    sDraweeControllerBuilderSupplier =
        new PipelineDraweeControllerBuilderSupplier(context, draweeConfig);
    SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);
  }

实际上主要是构造出SimpleDraweeView的DraweeController变量。
在了解之前先看看

  sDraweeControllerBuilderSupplier =
        new PipelineDraweeControllerBuilderSupplier(context, draweeConfig);

DraweeController是怎么生成的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容