Glide使用与源码解析

Glide.png

glide中文文档

Glide介绍

Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。Glide 支持拉取,解码和展示视频快照,图片,和GIF动画。Glide的Api是如此的灵活,开发者甚至可以插入和替换成自己喜爱的任何网络栈。默认情况下,Glide使用的是一个定制化的基于HttpUrlConnection的栈,但同时也提供了与Google Volley和Square OkHttp快速集成的工具库。

虽然Glide 的主要目标是让任何形式的图片列表的滚动尽可能地变得更快、更平滑,但实际上,Glide几乎能满足你对远程图片的拉取/缩放/显示的一切需求。

Glide的使用

本文是从4.10.0开始介绍的

  • 最小SDK版本需要使用API 14(或者更高版本)
  • Complie SDK Version需要使用API 27(或者更高版本)
  • Glide使用的SupportLibrary 版本是27,如果需要不同的SupportLibrary版本可以用exclude将Glide的SupportLibrary从依赖中去掉,具体的在集成时说明

Glide导包

implementation('com.github.bumptech.glide:glide:4.10.0')
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
//如果使用了在Kotlin中使用了Glide注解,需要引入kapt依赖代替annotationProcessor依赖
//kapt 'com.github.bumptech.glide:compiler:4.10.0'
//若使用了不是27的SupportLibrary版本,使用以下代码
implementation('com.github.bumptech.glide:glide:4.10.0') {
    exclude group: "com.android.support"
}
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'

Glide的基本用法

String url = "http://img1.dzwww.com:8080/tupian_pl/20150813/16/7858995348613407436.jpg";
ImageView imageView = (ImageView) findViewById(R.id.imageView);
Glide.with(context)
    .load(url)
    .into(imageView);
  • with(Context context) - 需要上下文,这里还可以使用 Activity、FragmentActivity、android.support.v4.app.Fragment、android.app.Fragment 的对象。将 Activity/Fragment 对象作为参数的好处是,图片的加载会和 Activity/Fragment 的生命周期保持一致,例如:onPaused 时暂停加载,onResume 时又会自动重新加载。所以在传参的时候建议使用 Activity/Fragment 对象,而不是 Context。
  • load(String url) - 这里我们所使用的一个字符串形式的网络图片的 URL,后面会讲解 load() 的更多使用方式
  • into(ImageView imageView) - 你需要显示图片的目标 ImageView
String url = "http://img1.dzwww.com:8080/tupian_pl/20150813/16/7858995348613407436.jpg";
ImageView imageView = (ImageView) findViewById(R.id.imageView);
Glide.with(context)
    .load(url)
    .thumbnail( 0.2f )
    .placeholder(R.drawable.place_image)//图片加载出来前,显示的图片
    .error(R.drawable.error_image)//图片加载失败后,显示的图片
    .into(imageView);
  • placeholder(int resId) 图片加载出来前显示的预显示图片
  • error(int resId) 加载失败后显示的预存图片
    上面这两个参数也支持Drawable 参数
  • thumbnail 是做略图的属性,上面的例子中,如果是0.2f,则将会显示原始图片的20%的大小,如果原图是 1000x1000 的尺寸,那么缩略图将会是 200x200 的尺寸
    缩略图还有另外一种
private void loadImageThumbnailRequest(){
    // setup Glide request without the into() method
    DrawableRequestBuilder<String> thumbnailRequest = Glide.with( context ).load( url );
    // pass the request as a a parameter to the thumbnail request
    Glide.with( context )
        .load( url )
        .thumbnail( thumbnailRequest )
        .into( imageView );
}

与上面第一种方式不同的是,这里的第一个缩略图请求是完全独立于第二个原始请求的。该缩略图可以是不同的资源图片,同时也可以对缩略图做不同的转换,等等...

图片的大小调整

可以调用override来调整图片的大小

Glide
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .override(600, 200) // resizes the image to these dimensions (in pixel). does not respect aspect ratio
    .into(imageViewResize);

transformation(变换)

Glide提供了transformation得功能,在获取到请求的图片之后,能对图片进行一些处理,例如:裁剪、模糊等;而transformation的强大在于可以自定义,这样一来transformation不仅能处理bitmap,同样可以用于处理GIF动画,还有自定义资源类型。

Glide在API上提供的了5个相关的方法可以直接使用

circleCrop()//加载原形图片
centerCrop()//方法是将图片按比例缩放到足矣填充
centerInside()//保留图片原本的长宽比,如果图片长和宽(X和Y)均小于ImageView的长和宽,则不进行放大,直接将图片在ImageView中居中展示;否则对图片的长和宽(X和Y)进行等比例缩小,直到图片至少一边与ImageView重合,并且图片完全显示在ImageView里面,最终将图片在ImageView中居中展示。
fitCenter()//保留图片原本的长宽比,对图片的长和宽(X和Y)进行等比例缩放,直到图片至少一边与ImageView重合,并且图片完全显示在ImageView里面,最终将图片在ImageView中居中展示
optionalFitCenter()//
使用方法如下

GlideApp.with(this)
        .load(url)
        .centerCrop()
        .into(imageView);

可以直接调用以上方法,如果没有直接可以调用的方法,需要通过RequestOptions来设置

RequestOptions options = new RequestOptions().centerCrop();

Glide.with(this)
        .load(url)
        .apply(options)
        .into(imageView);

//静态方法
Glide.with(this)
        .load(url)
        .apply(RequestOptions.centerCropTransform())
        .into(imageView);

针对ImageView可能自身也会设置scaleType的情况,Glide在部分情况会自动应用 FitCenter或CenterCrop,如果 scaleType 是 CENTER_CROP , Glide 将会自动应用 CenterCrop 变换。如果 scaleType 为 FIT_CENTER 或 CENTER_INSIDE ,Glide会自动使用 FitCenter 变换

图片的缓存处理

为了更快的加载图片,我们肯定希望可以直接拿到图片,而不是进行网络请求,所以我们需要缓存。Glide 通过使用默认的内存和磁盘缓存来避免不必要的网络请求,之后我们再详细的去看它的实现。
内存缓存是 Glide 默认帮我们做了的,除非你不需要,可以调用 skipMemoryCache(true) 告诉 Glide 跳过内存缓存。这样 Glide 就不会把这张图片放到内存缓存中,该方法只影响内存缓存。
此外也可以调用用diskCacheStrategy方法自定义缓存策略

  • DiskCacheStrategy.NONE 什么都不缓存
  • DiskCacheStrategy.SOURCE 只缓存全尺寸图
  • DiskCacheStrategy.RESULT 只缓存最终的加载图
  • DiskCacheStrategy.ALL 缓存所有版本图(默认行为)
Glide.with(context)
    .load(url)
    .skipMemoryCache(true)
    .diskCacheStrategy( DiskCacheStrategy.NONE )
    .into(imageView);

图片的优先级

同一时间加载多个图片,App 将难以避免这种情况。如果这个时候我们希望用户的体验更好,往往会选择先加载对于用户更加重要的图片。Glide 可以调用 .priority() 方法配合 Priority 枚举来设置图片加载的优先级。

  • Priority.LOW
  • Priority.NORMAL
  • Priority.HIGH
  • Priority.IMMEDIAT
Glide.with( context )
    .load( highPriorityImageUrl )
    .priority (Priority.HIGH )
    .into( imageView );

图片的动画效果

无论你是否使用占位图,对于UI来说,图片的改变是相当大的一个动作。一个简单的方法可以让这个变化更平滑,更让人眼接受,这就是使用crossfade动画。

Glide
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .placeholder(R.mipmap.ic_launcher) // can also be a drawable
    .error(R.mipmap.future_studio_launcher) // will be displayed if the image cannot be loaded
    .crossFade(300)
    .into(imageViewFade);

crossFade()方法可以携带参数,参数代表着图片的平滑替换时间,默认是300毫秒
如果你想直接跳过这个动画则直接调用.dontAnimate()方法

在图片中

Glide gif和视频的播放

许多图片加载库能处理好图片的加载与显示,但很多并不支持Gif。如果你的app需要支持Gif,Glide的简单会让体验更惊艳。如果你想要播放Gif,你只要使用之前处理图片的类似的方法,

Glide
      .with(context)
      .load(gifUrl)
      .asGif()
      .error( R.drawable.full_cake)
      .into( imageViewGif );

在上面的代码中还有一个asGif的方法,这个方法是为了防止出入的gif图片如果不是gif只是一个普通的图片,会将它通过asGif方法装换成gif,防止上面的方法调用后报错。如果你的app需要显示一组网络URL,可能包括普通的图片或者Gif。在一些情况下,你可能并不在意是否要播放完整的Gif。如果你只是想要显示Gif的第一帧,当URl指向的的确是Gif,你可以调用asBitmap()将其作为常规图片显示。

此外glide还能够播放视频,不过只能播放本地视频

String filePath = "/storrage/emulated/0/Pictures/video.mp4";
Glide.with( context )
    .load( Uri.fromFile( new File( filePath ) ) )
    .into( imageView );

Target

我们所涉及到的代码都是直接加载图片到 ImageView 中。Glide 隐藏做了所有的网络请求和后台的线程处理,图片准备好之后切回到 UI 线程刷新 ImageView。也就是说 ImageView 在我们代码的链式结构中成为了最后一步,但是如果我们需要获取到 Bitmap 本身
的话我们就需要用到 Target 了。Target 其实就是整个图片的加载的生命周期,所以我们就可以通过它在图片加载完成之后获取到 Bitmap。其实对于 Target 可以简单的理解为回调,本身就是一个 interface,Glide本身也为我们提供了很多 Target

private SimpleTarget<Bitmap> mSimpleTarget = new SimpleTarget<Bitmap>(500,500) {
    @Override
    public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> animation) {
        mImageView.setImageBitmap(resource);
    }
};

private void loadImageSimpleTarget() {
    Glide.with( thi s)
        .load( mUrl )
        .asBitmap()
        .into( mSimpleTarget );
}

首先创建了一个 SimpleTarget 的对象并且实现了 onResourceReady() 方法,看方法名能知道是图片加载完之后会调用该方法,参数就有我们需要的 Bitmap 。而使用 SimpleTarget 的对象的时候就像使用 ImageView 一样,作为参数传给 into() 方法就行了,Glide 会内部去处理并返回结果给任何一个对象。这里我们为了防止加载 Gif 、 Video 或者一些位置资源时与 mSimpleTarget 冲突,所以我们调用了 asBitmap() 方法,使其只能返回 Bitmap 对象。

当我们使用 Custom View 时,Glide 并不支持加载图片到自定义 view 中的,使用 ViewTarget 更容易实现。

public void loadImageTarget(Context context){
    CustomView mCustomView = (CustomView) findViewById(R.id.custom_view);

    ViewTarget viewTarget = new ViewTarget<CustomView,GlideDrawable>( mCustomView ) {
        @Override
        public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
            this.view.setImage(resource);
        }
    };

    Glide.with(context)
            .load(mUrl)
            .into(viewTarget);
}

在 target 的 onResourceReady 回调方法中使用自定义 view 自己的方法去设置图片,可以看到在创建 ViewTarget 的时候传入了 CustomView 的对象。

下面是通知栏Target 与 AppWidgetTarget的使用

        NotificationTarget target2=new NotificationTarget(context,
                remoteview,
                R.id.remoteview_notification_icon,
                notification,
                NOTIFICATION_ID);
        Glide  
            .with( context.getApplicationContext() ) 
            .load( url)
            .asBitmap()
            .into( notificationTarget );
public class MYAppWidgetProvider extends AppWidgetProvider {

    private AppWidgetTarget appWidgetTarget;

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
                         int[] appWidgetIds) {

        RemoteViews remoteviews= new RemoteViews(context.getPackageName(), R.layout.my_remoteView);

        appWidgetTarget = new AppWidgetTarget( context, remoteviews, R.id.custom_view_image, appWidgetIds );

        Glide
                .with( context.getApplicationContext() ) // safer!
                .load( GlideExampleActivity.eatFoodyImages[3] )
                .asBitmap()
                .into( appWidgetTarget );

        pushWidgetUpdate(context, remoteviews);
    }

    public static void pushWidgetUpdate(Context context, RemoteViews remoteviews) {
        ComponentName myWidget = new ComponentName(context, FSAppWidgetProvider.class);
        AppWidgetManager manager = AppWidgetManager.getInstance(context);
        manager.updateAppWidget(myWidget, rv);
    }
}

异常处理

Glide不提供直接获取常规请求的日志,但是你可以在请求出错时抓取异常的日志。例如,如果图片不存在,Glide会(静静地)抛出一个异常,并显示出你.erroer()里指定的图片。如果你明确想要知道异常,创建一个listener,然后传递给Glide的.listener()方法。

private RequestListener<String, GlideDrawable> requestListener = new RequestListener<String, GlideDrawable>() {  
    @Override
    public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
        // todo log exception

        // important to return false so the error placeholder can be placed
        return false;
    }

    @Override
    public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
        return false;
    }
};

  Glide  
    .with( context )
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .listener( requestListener )
    .error( R.drawable.cupcake )
    .into( imageViewPlaceholder );

以上是Glide的基本使用

Glide源码解析

从上面的一大串代码中我们可以发现,glide使用的是建造者模式
我们先看看with方法


with.png

with方法有很多的构造函数,可以传入context、Activity等

Glide->with
  @NonNull
  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }

  Glide->getRetriever
  @NonNull
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // Context could be null for other reasons (ie the user passes in null), but in practice it will
    // only occur due to errors with the Fragment lifecycle.
    Preconditions.checkNotNull(
        context,
        "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
            + "returns null (which usually occurs when getActivity() is called before the Fragment "
            + "is attached or after the Fragment is destroyed).");//非空判断
    return Glide.get(context).getRequestManagerRetriever();//获取 RequestManagerRetriever 对象
  }



Glide->get
  /**
   * Get the singleton.
   *
   * @return the singleton
   */
  @NonNull
  public static Glide get(@NonNull Context context) {
    if (glide == null) {
//这句是通过反射获取com.bumptech.glide.GeneratedAppGlideModuleImpl并实例化
      GeneratedAppGlideModule annotationGeneratedModule =
          getAnnotationGeneratedGlideModules(context.getApplicationContext());
      synchronized (Glide.class) {
        if (glide == null) {//单例模式
          checkAndInitializeGlide(context, annotationGeneratedModule);//从名字上可以看出是初始化glide
        }
      }
    }

    return glide;
  }

Glide->initializeGlide
  @GuardedBy("Glide.class")
  private static void initializeGlide(
      @NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
    initializeGlide(context, new GlideBuilder(), generatedAppGlideModule);
  }

Glide->initializeGlide
  private static void initializeGlide(
      @NonNull Context context,
      @NonNull GlideBuilder builder,
      @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
    ...

        Glide glide = builder.build(applicationContext);
    ...
}

GlideBuilder->build
Glide build(@NonNull Context context) {
    if (sourceExecutor == null) {
      sourceExecutor = GlideExecutor.newSourceExecutor();//创建一个默认的线程池,用来执行耗时任务
    }

    if (diskCacheExecutor == null) {
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();//创建一个默认的硬盘缓存线程池
    }

    if (animationExecutor == null) {
      animationExecutor = GlideExecutor.newAnimationExecutor();//创建一个执行动画效果的线程池
    }

    if (memorySizeCalculator == null) {
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();//创建内存缓存控制器,并设置其大小
    }

    if (connectivityMonitorFactory == null) {
      connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();//创建网络监听器
    }

    if (bitmapPool == null) {
      int size = memorySizeCalculator.getBitmapPoolSize();//创建bitmap对象池
      if (size > 0) { // 使用缓存
        bitmapPool = new LruBitmapPool(size);
      } else {// 不使用缓存 
        bitmapPool = new BitmapPoolAdapter();
      }
    }

    if (arrayPool == null) {
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }

    if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }

    if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }

    if (engine == null) {
      engine =
          new Engine(
              memoryCache,
              diskCacheFactory,
              diskCacheExecutor,
              sourceExecutor,
              GlideExecutor.newUnlimitedSourceExecutor(),
              animationExecutor,
              isActiveResourceRetentionAllowed);
    }

    if (defaultRequestListeners == null) {
      defaultRequestListeners = Collections.emptyList();
    } else {
      defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
    }

   
    RequestManagerRetriever requestManagerRetriever =
        new RequestManagerRetriever(requestManagerFactory);//在这里创建了requestManagerRetriever 
最后创建一个请求管理类,将请求工厂对象传入进去,并且会实例化一个主线程的handler对象,用于主-子线程的切换

    return new Glide(
        context,
        engine,
        memoryCache,
        bitmapPool,
        arrayPool,
        requestManagerRetriever,
        connectivityMonitorFactory,
        logLevel,
        defaultRequestOptionsFactory,
        defaultTransitionOptions,
        defaultRequestListeners,
        isLoggingRequestOriginsEnabled,
        isImageDecoderEnabledForBitmaps,
        hardwareBitmapFdLimit,
        minHardwareDimension);
  }
}

RequestManagerRetriever 
  public RequestManagerRetriever(@Nullable RequestManagerFactory factory) {
    this.factory = factory != null ? factory : DEFAULT_FACTORY;
    handler = new Handler(Looper.getMainLooper(), this /* Callback */);
  }

Glide->getRequestManagerRetriever
@NonNull
  public RequestManagerRetriever getRequestManagerRetriever() { //在这里我们看到直接返回了requestManagerRetriever对象
    return requestManagerRetriever;
  }

通过上面的方法,我们可以看到Glide已经初始化完成了
现在已经获取到RequestManagerRetriever对象,接下来我们回到刚才的方法中,它调用了RequestManagerRetriever的get方法


RequestManagerRetriever的get方法.png

从上图可以看到,RequestManagerRetriever中有很多的get方法

RequestManagerRetriever->get
  @NonNull
  public RequestManager get(@NonNull Context context) {
    if (context == null) {
      throw new IllegalArgumentException("You cannot start a load on a null Context");
    } else if (Util.isOnMainThread() && !(context instanceof Application)) {//如果不是application的context就一直迭代这个方法
      if (context instanceof FragmentActivity) {
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        return get((Activity) context);
      } else if (context instanceof ContextWrapper
          // Only unwrap a ContextWrapper if the baseContext has a non-null application context.
          // Context#createPackageContext may return a Context without an Application instance,
          // in which case a ContextWrapper may be used to attach one.
          && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
        return get(((ContextWrapper) context).getBaseContext());
      }
    }
   //一直迭代这个方法,获取到application的context,最后进入到这个方法
    return getApplicationManager(context);
  }

  public RequestManager get(FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else {
      assertNotDestroyed(activity);
      FragmentManager fm = activity.getSupportFragmentManager();
      return supportFragmentGet(activity, fm, null /*parentHint*/);
    }
  }

  public RequestManager get(Fragment fragment) {
    if (Util.isOnBackgroundThread()) {
      return get(fragment.getActivity().getApplicationContext());
    } else {
      FragmentManager fm = fragment.getChildFragmentManager();
      return supportFragmentGet(fragment.getActivity(), fm, fragment);
    }
  }

  public RequestManager get(Activity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else {
      assertNotDestroyed(activity);
      android.app.FragmentManager fm = activity.getFragmentManager();
      return fragmentGet(activity, fm, null /*parentHint*/);
    }
  }

  public RequestManager get(View view) {
    if (Util.isOnBackgroundThread()) {
      return get(view.getContext().getApplicationContext());
    }

    Activity activity = findActivity(view.getContext());

    if (activity == null) {
      return get(view.getContext().getApplicationContext());
    }

    if (activity instanceof FragmentActivity) {
      Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
      if (fragment == null) {
        return get(activity);
      }
      return get(fragment);
    }

    android.app.Fragment fragment = findFragment(view, activity);
    if (fragment == null) {
      return get(activity);
    }
    return get(fragment);
  }

他们会判断Context是Activity的还是Fragment的调用对应的方法,如果传入的FragmentActivity或者Fragment,会调用supportFragmentGet,如果传入的是Activity则调用fragmentGet,如果传入的是view,则根据view中的context来判断
他们的共同点是,如果isOnBackgroundThread是true,则会调用Application的方法

RequestManagerRetriever->getApplicationManager
  @NonNull
  private RequestManager getApplicationManager(@NonNull 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.

          // TODO(b/27524013): Factor out this Glide.get() call.
          Glide glide = Glide.get(context.getApplicationContext());
          //在这里获取RequestManager 对象
          applicationManager =
              factory.build(
                  glide,
                  new ApplicationLifecycle(),
                  new EmptyRequestManagerTreeNode(),
                  context.getApplicationContext());
        }
      }
    }

    return applicationManager;
  }


  @Deprecated
  @NonNull
  private RequestManager fragmentGet(
      @NonNull Context context,
      @NonNull android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

  private RequestManagerFragment getRequestManagerFragment(
      @NonNull final android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = pendingRequestManagerFragments.get(fm);
      if (current == null) {
        current = new RequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          current.getGlideLifecycle().onStart();
        }
        pendingRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

从上面代码上看,传入Activity 或者Fragment的context的最后都调用了getSupportRequestManagerFragment

  @SuppressWarnings("deprecation")
  @NonNull
  private RequestManagerFragment getRequestManagerFragment(
      @NonNull final android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = pendingRequestManagerFragments.get(fm);
      if (current == null) {
        current = new RequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          current.getGlideLifecycle().onStart();
        }
        pendingRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

  @NonNull
  private SupportRequestManagerFragment getSupportRequestManagerFragment(
      @NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
    SupportRequestManagerFragment current =
        (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = pendingSupportRequestManagerFragments.get(fm);
      if (current == null) {
        current = new SupportRequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          current.getGlideLifecycle().onStart();
        }
        pendingSupportRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

SupportRequestManagerFragment的构造函数创建了一个ActivityFragmentLifecycle对象用来管理生命周期,在onStart方法中调用ActivityFragmentLifecycle的onStart方法,同理其他生命周期方法也是。

public class SupportRequestManagerFragment extends Fragment {
  private static final String TAG = "SupportRMFragment";
  private final ActivityFragmentLifecycle lifecycle;
  private final RequestManagerTreeNode requestManagerTreeNode =
      new SupportFragmentRequestManagerTreeNode();
  private final Set<SupportRequestManagerFragment> childRequestManagerFragments = new HashSet<>();

  @Nullable private SupportRequestManagerFragment rootRequestManagerFragment;
  @Nullable private RequestManager requestManager;
  @Nullable private Fragment parentFragmentHint;

  public SupportRequestManagerFragment() {
    this(new ActivityFragmentLifecycle());
  }

  @VisibleForTesting
  @SuppressLint("ValidFragment")
  public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
    this.lifecycle = lifecycle;
  }

  /**
   * Sets the current {@link com.bumptech.glide.RequestManager}.
   *
   * @param requestManager The manager to put.
   */
  public void setRequestManager(@Nullable RequestManager requestManager) {
    this.requestManager = requestManager;
  }
...
  @Override
  public void onDetach() {
    super.onDetach();
    parentFragmentHint = null;
    unregisterFragmentWithRoot();
  }

  @Override
  public void onStart() {
    super.onStart();
    lifecycle.onStart();
  }

  @Override
  public void onStop() {
    super.onStop();
    lifecycle.onStop();
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    lifecycle.onDestroy();
    unregisterFragmentWithRoot();
  }

接下来我们看下RequestManager


RequestManager
public class RequestManager
    implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {
  private static final RequestOptions DECODE_TYPE_BITMAP = decodeTypeOf(Bitmap.class).lock();
  private static final RequestOptions DECODE_TYPE_GIF = decodeTypeOf(GifDrawable.class).lock();
  private static final RequestOptions DOWNLOAD_ONLY_OPTIONS =
      diskCacheStrategyOf(DiskCacheStrategy.DATA).priority(Priority.LOW).skipMemoryCache(true);

  protected final Glide glide;
  protected final Context context;

  @SuppressWarnings("WeakerAccess")
  @Synthetic
  final Lifecycle lifecycle;

  @GuardedBy("this")
  private final RequestTracker requestTracker;

  @GuardedBy("this")
  private final RequestManagerTreeNode treeNode;

  @GuardedBy("this")
  private final TargetTracker targetTracker = new TargetTracker();

  private final Runnable addSelfToLifecycle =
      new Runnable() {
        @Override
        public void run() {
          lifecycle.addListener(RequestManager.this);
        }
      };
  private final Handler mainHandler = new Handler(Looper.getMainLooper());
  private final ConnectivityMonitor connectivityMonitor;
  // Adding default listeners should be much less common than starting new requests. We want
  // some way of making sure that requests don't mutate our listeners without creating a new copy of
  // the list each time a request is started.
  private final CopyOnWriteArrayList<RequestListener<Object>> defaultRequestListeners;

  @GuardedBy("this")
  private RequestOptions requestOptions;

  private boolean pauseAllRequestsOnTrimMemoryModerate;
  public RequestManager(
      @NonNull Glide glide,
      @NonNull Lifecycle lifecycle,
      @NonNull RequestManagerTreeNode treeNode,
      @NonNull Context context) {
    this(
        glide,
        lifecycle,
        treeNode,
        new RequestTracker(),
        glide.getConnectivityMonitorFactory(),
        context);
  }

  // Our usage is safe here.
  @SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
  RequestManager(
      Glide glide,
      Lifecycle lifecycle,
      RequestManagerTreeNode treeNode,
      RequestTracker requestTracker,
      ConnectivityMonitorFactory factory,
      Context context) {
    this.glide = glide;
    this.lifecycle = lifecycle;
    this.treeNode = treeNode;
    this.requestTracker = requestTracker;
    this.context = context;

    connectivityMonitor =
        factory.build(
            context.getApplicationContext(),
            new RequestManagerConnectivityListener(requestTracker));

    // If we're the application level request manager, we may be created on a background thread.
    // In that case we cannot risk synchronously pausing or resuming requests, so we hack around the
    // issue by delaying adding ourselves as a lifecycle listener by posting to the main thread.
    // This should be entirely safe.
    if (Util.isOnBackgroundThread()) {
      mainHandler.post(addSelfToLifecycle);
    } else {
      lifecycle.addListener(this);
    }
    lifecycle.addListener(connectivityMonitor);

    defaultRequestListeners =
        new CopyOnWriteArrayList<>(glide.getGlideContext().getDefaultRequestListeners());
    setRequestOptions(glide.getGlideContext().getDefaultRequestOptions());

    glide.registerRequestManager(this);
  }
...
  @Override
  public synchronized void onStart() {
    resumeRequests();
    targetTracker.onStart();
  }

  /**
   * Lifecycle callback that unregisters for connectivity events (if the
   * android.permission.ACCESS_NETWORK_STATE permission is present) and pauses in progress loads.
   */
  @Override
  public synchronized void onStop() {
    pauseRequests();
    targetTracker.onStop();
  }

  /**
   * Lifecycle callback that cancels all in progress requests and clears and recycles resources for
   * all completed requests.
   */
  @Override
  public synchronized void onDestroy() {
    targetTracker.onDestroy();
    for (Target<?> target : targetTracker.getAll()) {
      clear(target);
    }
    targetTracker.clear();
    requestTracker.clearRequests();
    lifecycle.removeListener(this);
    lifecycle.removeListener(connectivityMonitor);
    mainHandler.removeCallbacks(addSelfToLifecycle);
    glide.unregisterRequestManager(this);
  }
...

在RequestManager实现了LifecycleListener接口,并且调用lifecycle.addListener(this)添加监听器。接口生命周期函数onStart()启动请求,onStop暂停请求,在ActivtyFragmentLifecycle中如果添加了监听器,在onStart调用监听器的onStart方法,也就是调用RequsetManager中的onStart,同理onPause也是。

class ActivityFragmentLifecycle implements Lifecycle {
  private final Set<LifecycleListener> lifecycleListeners =
      Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
  private boolean isStarted;
  private boolean isDestroyed;

  /**
   * Adds the given listener to the list of listeners to be notified on each lifecycle event.
   *
   * <p>The latest lifecycle event will be called on the given listener synchronously in this
   * method. If the activity or fragment is stopped, {@link LifecycleListener#onStop()}} will be
   * called, and same for onStart and onDestroy.
   *
   * <p>Note - {@link com.bumptech.glide.manager.LifecycleListener}s that are added more than once
   * will have their lifecycle methods called more than once. It is the caller's responsibility to
   * avoid adding listeners multiple times.
   */
  @Override
  public void addListener(@NonNull LifecycleListener listener) {
    lifecycleListeners.add(listener);

    if (isDestroyed) {
      listener.onDestroy();
    } else if (isStarted) {
      listener.onStart();
    } else {
      listener.onStop();
    }
  }

  @Override
  public void removeListener(@NonNull LifecycleListener listener) {
    lifecycleListeners.remove(listener);
  }

  void onStart() {
    isStarted = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onStart();
    }
  }

  void onStop() {
    isStarted = false;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onStop();
    }
  }

  void onDestroy() {
    isDestroyed = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onDestroy();
    }
  }
}
  • 经过上面的分析发现整个过程就理顺了,先创建一个Fragment对象并在构造函数中创建Lifecycle对象
    ,在创建RequestManager的时候会把Lifecycle对象传递进去。

  • RequestManger则在构造函数处为接收的Lifecycle添加监听器,而RequestManager自身实现了LifecycleListener,在实现的接口方法onStop和onPause分别启动和暂停请求。

  • Lifecycle对象在添加监听器后,会在生命周期方法onStart和onPauser分别调用监听器对应的方法。

  • 因此当Fragment会在生命周期方法中调用Lifecycle对应的生命周期方法,最终也就是在调用RequestManger中的生命周期方法,因此实现了请求和生命周期的绑定,从而可以根据生命周期来启动和暂停请求。

  • 总结一下,with()方法主要初始化Glide和创建了RequestManger绑定SupportRequestManagerFragment的生命周期函数,最后返回RequestManager。

load方法

load.png

我们看到load方法有很多重载方法,load方法都实现自ModelTypes,经过with方法,我们知道with方法返回的是requestManager对象,因此load方法在RequestManager方法中查找

RequestManager implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {
...
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
    return asDrawable().load(bitmap);
  }

  /**
   * Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(Drawable)}.
   *
   * @return A new request builder for loading a {@link Drawable} using the given model.
   */
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
    return asDrawable().load(drawable);
  }

  /**
   * Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(String)}.
   *
   * @return A new request builder for loading a {@link Drawable} using the given model.
   */
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }

  /**
   * Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(Uri)}.
   *
   * @return A new request builder for loading a {@link Drawable} using the given model.
   */
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Uri uri) {
    return asDrawable().load(uri);
  }

  /**
   * Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(File)}.
   *
   * @return A new request builder for loading a {@link Drawable} using the given model.
   */
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable File file) {
    return asDrawable().load(file);
  }

  /**
   * Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(Integer)}.
   *
   * @return A new request builder for loading a {@link Drawable} using the given model.
   */
  @SuppressWarnings("deprecation")
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
    return asDrawable().load(resourceId);
  }

  /**
   * Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(URL)}.
   *
   * @return A new request builder for loading a {@link Drawable} using the given model.
   */
  @SuppressWarnings("deprecation")
  @CheckResult
  @Override
  @Deprecated
  public RequestBuilder<Drawable> load(@Nullable URL url) {
    return asDrawable().load(url);
  }

  /**
   * Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(byte[])}.
   *
   * @return A new request builder for loading a {@link Drawable} using the given model.
   */
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable byte[] model) {
    return asDrawable().load(model);
  }

  /**
   * A helper method equivalent to calling {@link #asDrawable()} and then {@link
   * RequestBuilder#load(Object)} with the given model.
   *
   * @return A new request builder for loading a {@link Drawable} using the given model.
   */
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Object model) {
    return asDrawable().load(model);
  }
...

我们可以发现无论哪个load方法都是调用了asDrawable()方法

  @NonNull
  @CheckResult
  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }

  @NonNull
  @CheckResult
  public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }

 public RequestBuilder<File> asFile() {
    return as(File.class).apply(skipMemoryCacheOf(true));
  }
  public RequestBuilder<GifDrawable> asGif() {
    return as(GifDrawable.class).apply(DECODE_TYPE_GIF);
  }
  public RequestBuilder<Bitmap> asBitmap() {
    return as(Bitmap.class).apply(DECODE_TYPE_BITMAP);
  }

我们可以看到,调用AS方法后里面实际上是初始化一个RequestBuilder对象

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {

...
  @SuppressLint("CheckResult")
  @SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
  protected RequestBuilder(
      @NonNull Glide glide,
      RequestManager requestManager,
      Class<TranscodeType> transcodeClass,
      Context context) {
    this.glide = glide;
    this.requestManager = requestManager;
    this.transcodeClass = transcodeClass;
    this.context = context;
    this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
    this.glideContext = glide.getGlideContext();

    initRequestListeners(requestManager.getDefaultRequestListeners());
    apply(requestManager.getDefaultRequestOptions());
  }
...

构造了一个RequestBuilder实例,传入的参数赋值到RequestBuilder的成员变量,这里成员transcodeClass也就是刚才的Drawable.class,记住了,初始化了一个requestOptions,这2个成员记住了,后面会有用的,接着回到之前的一段代码,查看load方法

RequestBuilder->load
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
    return loadGeneric(bitmap).apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
  }

 public RequestBuilder<TranscodeType> load(@Nullable Drawable drawable) {
    return loadGeneric(drawable)
        .apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
  }
 public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
    return loadGeneric(bitmap)
        .apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
  }
  public RequestBuilder<TranscodeType> load(@Nullable Uri uri) {
    return loadGeneric(uri);
  }
public RequestBuilder<TranscodeType> load(@Nullable File file) {
    return loadGeneric(file);
  }
public RequestBuilder<TranscodeType> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
    return loadGeneric(resourceId).apply(signatureOf(ApplicationVersionSignature.obtain(context)));
  }
public RequestBuilder<TranscodeType> load(@Nullable URL url) {
    return loadGeneric(url);
  }
 public RequestBuilder<TranscodeType> load(@Nullable byte[] model) {
    RequestBuilder<TranscodeType> result = loadGeneric(model);
    if (!result.requestOptions.isDiskCacheStrategySet()) {
        result = result.apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
    }
    if (!result.requestOptions.isSkipMemoryCacheSet()) {
      result = result.apply(skipMemoryCacheOf(true /*skipMemoryCache*/));
    }
    return result;
  }

  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;//这个model就是url String 的实例
    //数据来源是否已经设置的标志位 isModelSet 设置为true,意味着我们在调用 Glide.with(context).load(url) 之后数据来源已经设置成功了。
    isModelSet = true;
    return this;
  }

到这里其实load方法就已经完了,还有一些其他的方法

 public RequestBuilder<TranscodeType> apply(@NonNull RequestOptions requestOptions) {
    Preconditions.checkNotNull(requestOptions);
    this.requestOptions = getMutableOptions().apply(requestOptions);
    return this;
  }
 public RequestBuilder<TranscodeType> transition(
      @NonNull TransitionOptions<?, ? super TranscodeType> transitionOptions) {
    this.transitionOptions = Preconditions.checkNotNull(transitionOptions);
    isDefaultTransitionOptionsSet = false;
    return this;
  }
  public RequestBuilder<TranscodeType> listener(
      @Nullable RequestListener<TranscodeType> requestListener) {
    this.requestListeners = null;
    return addListener(requestListener);
  }
public RequestBuilder<TranscodeType> addListener(
      @Nullable RequestListener<TranscodeType> requestListener) {
    if (requestListener != null) {
      if (this.requestListeners == null) {
        this.requestListeners = new ArrayList<>();
      }
      this.requestListeners.add(requestListener);
    }
    return this;
  }
public RequestBuilder<TranscodeType> error(@Nullable RequestBuilder<TranscodeType> errorBuilder) {
    this.errorBuilder = errorBuilder;
    return this;
  }

info(ImageView view)方法

  @NonNull
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);

    BaseRequestOptions<?> requestOptions = this;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      // Clone in this method so that if we use this RequestBuilder to load into a View and then
      // into a different target, we don't retain the transformation applied based on the previous
      // View's scale type.
      switch (view.getScaleType()) {//这里获取imageView的ScaleType属性,glide会将ScaleType转化成requestOptions 
        case CENTER_CROP:
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
        case FIT_XY:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }

    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

RequestBuilder->info
  private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,
      Executor callbackExecutor) {
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }

    Request request = buildRequest(target, targetListener, options, callbackExecutor);

    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      // If the request is completed, beginning again will ensure the result is re-delivered,
      // triggering RequestListeners and Targets. If the request is failed, beginning again will
      // restart the request, giving it another chance to complete. If the request is already
      // running, we can let it continue running without interruption.
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        // Use the previous request rather than the new one to allow for optimizations like skipping
        // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
        // that are done in the individual Request.
        previous.begin();
      }
      return target;
    }

    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);

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

推荐阅读更多精彩内容