Glide4.0使用浅解

周六公司聚会,回来头晕乎乎的,工作也进行不了啦,看了篇博客说glide升级到4.0了 ,我去 瞬间一个激灵(虽然我们项目用的是imageloader~~~窃喜,但是我还是挺喜欢glide这个库的),是时候了解一下了.

看看看官方介绍,说已经是谷歌内部用的版本了,而且是最稳定的一个版本了,据说再过个几周完善一下bug就发正式版了,

官方介绍传送门

看了看官方的介绍文档,也挺----迷糊的,但是已经过一番挣扎和N次google之后,已经成功由V3 升级到V4了


好了开始我们的探索之路

1,项目集成

- 项目gradle

repositories {
 mavenCentral() // jcenter() works as well because it pulls from Maven Central
}
-app  gradle

    //glide库
    compile 'com.github.bumptech.glide:glide:4.0.0-RC0'
  //这个----
    compile 'com.android.support:support-v4:25.3.1'
  //这个用于我们自定义GlideModule的注解
    annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0-RC0'
  //glide默认是httpconnection,加这个是换成okhttp
    compile "com.github.bumptech.glide:okhttp3-integration:4.0.0-RC0"
添加混淆

-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}

___
没看错没有GlideModule的混淆,这个在下文说吧

之前的3.X版本的时候我们都是在manifast文件中配置我们自定义的GlideModule,just like 如下

<meta-data
            android:name="com.xxx.xxx.xxx.xxx.MyGlideModule"
            android:value="GlideModule"/>

这次呢 我们先把这个配置去掉,原因是在官方的介绍中提及到这种在manifast配置的方式,在初始化glide的读取其中配置时比较慢(相对V4注解的方式比较慢而已)

那你让我去了这个,我原来的GlideModule配置的信息,怎么读取到呢?莫急莫急,且看小弟下文的说叨

2,配置AppGlideModule<并不是GlideModule>

/**
 * Created by wangfei
 */
@GlideModule
public class MyGlideModule extends AppGlideModule {

    @Override
    public void applyOptions(final Context context, GlideBuilder builder) {
        //获取内存的默认配置
//        MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context).build();
//        int defaultMemoryCacheSize = calculator.getMemoryCacheSize();
//        int defaultBitmapPoolSize = calculator.getBitmapPoolSize();
//        int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize);
//        int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize);
//        builder.setMemoryCache(new LruResourceCache(customMemoryCacheSize));
//        builder.setBitmapPool(new LruBitmapPool(customBitmapPoolSize));

        //内存缓存相关,默认是24m
        int memoryCacheSizeBytes = 1024 * 1024 * 20; // 20mb
        builder.setMemoryCache(new LruResourceCache(memoryCacheSizeBytes));


        //设置磁盘缓存及其路径
        //
        int MAX_CACHE_SIZE = 100 * 1024 * 1024;
        String CACHE_FILE_NAME = "imgCache";
        builder.setDiskCache(new ExternalCacheDiskCacheFactory(context,CACHE_FILE_NAME,MAX_CACHE_SIZE));
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            String downloadDirectoryPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" +
                    CACHE_FILE_NAME;
            //路径---->sdcard/imgCache
            builder.setDiskCache(new DiskLruCacheFactory(downloadDirectoryPath, MAX_CACHE_SIZE));
        } else {
            //路径---->/sdcard/Android/data/<application package>/cache/imgCache
            builder.setDiskCache(new ExternalCacheDiskCacheFactory(context, CACHE_FILE_NAME, MAX_CACHE_SIZE));
        }
    }


    @Override
    public void registerComponents(Context context, Registry registry) {
        //配置glide网络加载框架
        registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
    }
    @Override
    public boolean isManifestParsingEnabled() {
        //不使用清单配置的方式,减少初始化时间
        return false;
    }
}

简单分析

@GlideModule

这个就是上文我们把manifast去掉的理由,加上这个注解()
glide会在他内部生成一个实现类

--------------------------------------------
具体位置

final class GeneratedAppGlideModuleImpl extends GeneratedAppGlideModule {
  private final MyGlideModule appGlideModule;

  GeneratedAppGlideModuleImpl() {
    appGlideModule = new MyGlideModule();
    if (Log.isLoggable("Glide", Log.DEBUG)) {
      Log.d("Glide", "Discovered AppGlideModule from annotation: com.wjf.fastdev.network.glide.MyGlideModule");
    }
  }

具体的大家试试后,自己看源码这里咱们主要是使用
registerComponents()方法
这个就是咱们把glide默认的网络请求方式换成okhttp
isManifestParsingEnabled

因为glide V4是兼容V3版本的所以他还会从manifast中读取GlideModule信息,但是呢,我们已经把manifast
的GlideModule已经去掉了,为了保证咱们初始化glide的效率,这个方法就是不让glide从manifast中读取了
,从而达到高效初始化的效果
applyOptions  加一些我们自己项目的设置

具体的设置和对应的作用已经在其中做过注释了
<注意:一定要有写SD卡的权限哦>
 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
细心的小伙伴们可能注意到我们没有设置图片的格式
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888)

这个设置V4上已经不提倡在这里配置了,具体配置在下文说吧


号外号外做完这一步就可以,使用我们常见的链式调用了

GlideApp.with(context)
                .asBitmap()
                .load(url)
                .placeholder(R.drawable.recommender_avatar_default)
                .error(R.drawable.recommender_avatar_default)
                .centerCrop()
                .into(imageview);

注意哦,不是Glide了而是GlideApp,应该是注解生成的吧,具体的没研究明白呢,以后再补充吧

3,新增的API(RequestBuilder,RequestOptions)

01,RequestBuilder

获取方式

 RequestBuilder requestBuilder = GlideApp.with(context).load(url);

可配置选项

RequestBuilder requestBuilder = GlideApp.with(context).load(url);
//添加加载的监听
        requestBuilder.listener(new RequestListener() {
            @Override
            public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean
                    isFirstResource) {
                return false;
            }

            @Override
            public boolean onResourceReady(Object resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
                return false;
            }
        });
//缩略图
        requestBuilder.thumbnail(0.5f);
//加载图片的url
        requestBuilder.load("IMAGE_URL");
//加载到的imageview对象
        requestBuilder.into(ImageView);

而我们常见的配置也就是这样了

GlideApp.with(context).load(url)..............;
也就是相当于新建了requestBuilder,
因此鄙人感觉没啥用处啊
02,RequestOptions

可配置选项

private static RequestOptions getRequestOptions() {

        RequestOptions options = new RequestOptions();
        //options.format(DecodeFormat.PREFER_ARGB_8888)
        //options.centerCrop()//图片显示类型
        //options.placeholder(loadingRes)//加载中图片
        //options.error(errorRes)//加载错误的图片
        //options.error(new ColorDrawable(Color.RED))//或者是个颜色值
        //options.priority(Priority.HIGH)//设置请求优先级
        //options.diskCacheStrategy(DiskCacheStrategy.ALL);
        //options.diskCacheStrategy(DiskCacheStrategy.RESOURCE)//仅缓存原图片
        //options.diskCacheStrategy(DiskCacheStrategy.ALL)//全部缓存
        //options.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)缓存缩略过的
        //options.onlyRetrieveFromCache(true)//仅从缓存加载
        //options.skipMemoryCache(true)//禁用缓存,包括内存和磁盘
        //options.diskCacheStrategy(DiskCacheStrategy.NONE)仅跳过磁盘缓存
        //options.override(200,200)加载固定大小的图片
        //options.dontTransform()不做渐入渐出的转换
        //options.transition(new DrawableTransitionOptions().dontTransition())//同上
        //options.circleCrop()设置成圆形头像<这个是V4.0新增的>
        //options.transform(new RoundedCorners(10))设置成圆角头像<这个是V4.0新增的>

        return options;
    }

使用RequestOptions

GlideApp.with(context)
                .asBitmap()
                .load(url)
                .apply(getRequestOptions()//使用自定义的配置

同样的依然可以在链式结构中调用

GlideApp.with(context)
                .asBitmap()
                .load(url)
                .placeholder(R.drawable.recommender_avatar_default)
                .error(R.drawable.recommender_avatar_default)
                //.skipMemoryCache(false)
                //.dontTransform()
                .centerCrop()
                //.diskCacheStrategy(DiskCacheStrategy.ALL)
                //.priority(Priority.HIGH)//设置请求优先级
                //这个是设置渐显的效果
                .transition(new BitmapTransitionOptions().crossFade(200))
                .into(imageview);

小小总结:感觉这两个类是用了解耦的,我们常用的还是这种链式结构的方式,以上仅仅做个参考

4,使用

 /**
     * 加载圆形头像
     *
     * @param context 如果是activity glide会与其生命周期关联,在onStop()中取消加载图片,如果
     *                想要始终加载图片则需要传入Application实例
     * @param url
     * @param target
     */
    public static void loadRoundImg(Context context, String url, ImageView target) {

        //https://github.com/wasabeef/glide-transformations--glide转换库
        GlideApp.with(context)
                .load(url)
                .placeholder(R.drawable.recommender_avatar_default)
                .error(R.drawable.recommender_avatar_default)
                .circleCrop()//直接在链式中调用就行哦
                .transition(new DrawableTransitionOptions().crossFade(1000))//渐显效果
                .into(target);
    }
显示效果
/**
     * 加载圆角图片
     *
     * @param context 如果是activity glide会与其生命周期关联,在onStop()中取消加载图片,如果
     *                想要始终加载图片则需要传入Application实例
     * @param url
     * @param target
     */
    public static void loadRoundedCornersImg(Context context, String url, ImageView target) {

        GlideApp.with(context)
                .load(url)
                .placeholder(R.drawable.recommender_avatar_default)
                .error(R.drawable.recommender_avatar_default)
                .transform(new RoundedCorners(40))
                .transition(new DrawableTransitionOptions().crossFade(200))//渐显效果
                .into(target);
    }
显示效果
/**
     * 加载原图片
     *
     * @param c
     * @param url
     * @param target
     */
    public static void loadSourceImg(Context c, String url, ImageView target) {
        GlideApp.with(c)
                .load(url)
                .transition(new DrawableTransitionOptions().crossFade(200))
                .centerCrop()
                //.sizeMultiplier(0.5f)//如果原图过大那么使用这个
                .into(target);
    }
显示效果
####其他使用

   /**
     * 加载图片不需要缓存的
     *
     * @param c
     * @param url
     * @param target
     */
    public static void loadSourseImgWithNoCache(Context c, String url, ImageView target) {
        GlideApp.with(c)
                .load(url)
                .diskCacheStrategy(DiskCacheStrategy.NONE)
                .skipMemoryCache(true)
                .centerCrop()
                .transition(new DrawableTransitionOptions().crossFade(200))
                .into(target);
    }

    /**
     * 根据资源ID加载图片
     *
     * @param c
     * @param resourceId
     * @param target
     * @param defaultId
     */
    public static void loadResourseImg(Context c, int resourceId, ImageView target, int defaultId) {
        GlideApp.with(c)
                .load(resourceId)
                .placeholder(defaultId)
                .transition(new DrawableTransitionOptions().crossFade(200))
                .centerCrop()
                .into(target);
    }

    /**
     * 根据图片路径加载图片
     *
     * @param c
     * @param imgFile
     * @param target
     * @param defaultId
     */
    public static void loadFileImg(Context c, File imgFile, ImageView target, int defaultId) {
        GlideApp.with(c)
                .load(imgFile)
                .placeholder(defaultId)
                .transition(new DrawableTransitionOptions().crossFade(200))
                .centerCrop()
                .into(target);
    }

    /**
     * 加载Gif为一张静态图片
     *
     * @param context
     * @param url
     */
    public static void LoadGiftAsBitmap(Context context, String url, ImageView imageView) {
        GlideApp.with(context).asBitmap().load(url).into(imageView);
    }

    /**
     * 你想只有加载对象是Gif时才能加载成功
     *
     * @param context
     * @param url
     */
    public static void LoadGiftAsGist(Context context, String url, ImageView imageView, int erroId) {
        GlideApp.with(context).asGif().load(url).error(erroId).into(imageView);

        //只加载一次gift图时调用
        //        GlideApp.with(context).load(url)
        //                .diskCacheStrategy(DiskCacheStrategy.SOURCE)
        //                .into(new GlideDrawableImageViewTarget(imageView,1));
    }

    /**
     * 加载缩略图,会自动与传入的fragment绑定生命周期,加载请求现在会自动在onStop中暂停在,onStart中重新开始。
     * 需要保证 ScaleType 的设置是正确的。
     *
     * @param fragment
     * @param url
     * @param imageView
     */
    public static void LoadThumbNail(Fragment fragment, String url, ImageView imageView) {
        GlideApp.with(fragment).load(url).thumbnail(0.1f).into(imageView);
    }

    /**
     * 上传一张大小为xPx*yPx像素的用户头像的图片bytes数据
     *
     * @param context
     * @param url
     * @param xPx
     * @param yPx
     */
    public static void decodeResorse(Context context, File url, int xPx, int yPx) {
        GlideApp
                .with(context)
                .load(url)
                .into(new SimpleTarget<Drawable>(xPx, yPx) {
                    @Override
                    public void onResourceReady(Drawable resource, Transition<? super Drawable> transition) {
                        //上传动作
                    }
                })
        ;
    }

    /**
     * 显示本地视频(网络视频无效)
     *
     * @param context
     * @param filePath
     * @param imageView
     */
    public static void LoadShowLocalVidio(Context context, String filePath, ImageView imageView) {
        GlideApp.with(context).load(Uri.fromFile(new File(filePath))).into(imageView);
    }

    /**
     * 在通知栏中显示从网络上请求的图片
     *
     * @param context
     * @param remoteViews
     * @param viewId
     * @param notification
     * @param notificationId
     * @param url
     */
    public static void ShowImgInNotification(Context context, RemoteViews remoteViews, int viewId, Notification
            notification, int notificationId, String url) {
        NotificationTarget target = new NotificationTarget(context, viewId, remoteViews, notification, notificationId);
        GlideApp.with(context.getApplicationContext()).asBitmap().load(url).into(target);
    }

    /**
     * 下载图片,耗时操作不能放在主线程中进行
     *
     * @param context
     * @param url
     */
    public static void downLoadImage(Context context, String url) {

        try {
            GlideApp.with(context).asBitmap().load(url).centerCrop().listener(new RequestListener<Bitmap>() {
                @Override
                public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Bitmap> target, boolean
                        isFirstResource) {
                    return false;
                }

                @Override
                public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource
                        dataSource, boolean isFirstResource) {
                    return false;
                }
            }).submit().get();
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    /**
     * 清除缓存
     *
     * @param context
     */
    public void clearCache(final Context context) {
        clearMemoryCache(context);
        new Thread(new Runnable() {
            @Override
            public void run() {
                clearDiskCache(context);
            }
        }).start();
    }

    /**
     * 清除内存缓存
     *
     * @param context
     */
    public void clearMemoryCache(Context context) {
        GlideApp.get(context).clearMemory();
    }

    /**
     * 清除磁盘缓存
     *
     * @param context
     */
    public void clearDiskCache(Context context) {
        GlideApp.get(context).clearDiskCache();
    }

注释写的已经很清楚了,不在多说

5,关于图形转换BitmapTransformation使用的问题

之前的使用bitmaptranstion的方式在V4版本中,不生效了而且必须实现如下方法

@Override
  public void updateDiskCacheKey(MessageDigest messageDigest) {
   
  }
/**
 * Created by wangfei on 2016/6/21 18:25.
 * 让图片旋转X度的转换类
 */
public class RotateTransformation extends BitmapTransformation {
    private float rotateRotationAngle = 0f;

    public RotateTransformation( float rotateRotationAngle) {

        this.rotateRotationAngle = rotateRotationAngle;
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        Matrix matrix = new Matrix();

        matrix.postRotate(rotateRotationAngle);

        return Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), toTransform.getHeight(), matrix, true);
    }




    @Override
    public void updateDiskCacheKey(MessageDigest messageDigest) {
      
    }
}

好了,那我们就来个空实现呗,运行一下

 GlideApp.with(context)
                .load(url)
                .placeholder(R.drawable.recommender_avatar_default)
                .error(R.drawable.recommender_avatar_default)
                .transform(new RotateTransformation(180))
                .transition(new DrawableTransitionOptions().crossFade(1000))//渐显效果
                .into(target);
好像可以

但是呢重新加载数据的时候好像重新转换了一下

那我们看看库里面关于图像转换时的操作

/**
 * A Glide {@link BitmapTransformation} to circle crop an image.  Behaves similar to a
 * {@link FitCenter} transform, but the resulting image is masked to a circle.
 *
 * <p> Uses a PorterDuff blend mode, see http://ssp.impulsetrain.com/porterduff.html. </p>
 */
public class CircleCrop extends BitmapTransformation {
  // The version of this transformation, incremented to correct an error in a previous version.
  // See #455.
  private static final int VERSION = 1;
  private static final String ID = "com.bumptech.glide.load.resource.bitmap.CircleCrop." + VERSION;
  private static final byte[] ID_BYTES = ID.getBytes(CHARSET);

  public CircleCrop() {
    // Intentionally empty.
  }

  /**
   * @deprecated Use {@link #CircleCrop()}.
   */
  @Deprecated
  public CircleCrop(@SuppressWarnings("unused") Context context) {
    this();
  }

  /**
   * @deprecated Use {@link #CircleCrop()}
   */
  @Deprecated
  public CircleCrop(@SuppressWarnings("unused") BitmapPool bitmapPool) {
    this();
  }

  // Bitmap doesn't implement equals, so == and .equals are equivalent here.
  @SuppressWarnings("PMD.CompareObjectsWithEquals")
  @Override
  protected Bitmap transform(
      @NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
    return TransformationUtils.circleCrop(pool, toTransform, outWidth, outHeight);
  }

  @Override
  public boolean equals(Object o) {
    return o instanceof CircleCrop;
  }

  @Override
  public int hashCode() {
    return ID.hashCode();
  }

  @Override
  public void updateDiskCacheKey(MessageDigest messageDigest) {
    messageDigest.update(ID_BYTES);
  }
}

好的,那么我们就按照他的方式试一下吧

/**
 * Created by wangfei on 2016/6/21 18:25.
 * 让图片旋转X度的转换类
 */
public class RotateTransformation extends BitmapTransformation {
    private static final String ID = "com.wjf.fastdev.network.glide.RotateTransformation";
    private static final byte[] ID_BYTES = ID.getBytes(CHARSET);
    private float rotateRotationAngle = 0f;

    public RotateTransformation( float rotateRotationAngle) {

        this.rotateRotationAngle = rotateRotationAngle;
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        Matrix matrix = new Matrix();

        matrix.postRotate(rotateRotationAngle);

        return Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), toTransform.getHeight(), matrix, true);
    }


    @Override
    public boolean equals(Object o) {
        return o instanceof RotateTransformation;
    }

    @Override
    public int hashCode() {
        return ID.hashCode();
    }

    @Override
    public void updateDiskCacheKey(MessageDigest messageDigest) {
        messageDigest.update(ID_BYTES);
    }
}

结果不显示那个闪动的问题了<图就不上传了.......>

解决自定义BitmapTransformation 不生效的问题

1,自定义类的变量
 private static final String ID = "com.wjf.fastdev.network.glide.RotateTransformation";
 private static final byte[] ID_BYTES = ID.getBytes(CHARSET);
2,复写如下三个方法
@Override
    public boolean equals(Object o) {
        return o instanceof RotateTransformation;
    }

    @Override
    public int hashCode() {
        return ID.hashCode();
    }

    @Override
    public void updateDiskCacheKey(MessageDigest messageDigest) {
        messageDigest.update(ID_BYTES);
    }

好了,先这样了,转眼12点了,睡觉,明天还有一大堆任务要做.有什么问题大家一起在评论区讨论吧,这个文章还不完整,有什么缺点请大家多多包涵,正如标题所言,是浅解........

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

推荐阅读更多精彩内容