Glide源码分析之Glide和RequestManager构建过程

Glide源码分析之Glide和RequestManager构建过程
Glide源码分析之自定义模块及Glide如何触发网络请求(未发布)
Glide源码分析之load(url)和into(iv)(未发布)
Glide源码分析之缓存机制(未发布)

基本用法

多数情况下,使用Glide加载图片非常简单,一行代码足矣:

Glide.with(fragment).load(myUrl).into(imageView)

取消加载:

Glide.with(fragment).clear(imageView);

实际上,Glide.with() 中传入的 Activity 或 Fragment 实例销毁时,Glide 会自动观察 Activity 或 Fragment 的生命周期取消加载并回收资源,实际上Glide为了监听Activity 或 Fragment的声明周期会在内部添加一个fragment就是为监听生命周期。

看一下Glide.with()的源码:

 /**
 * with方法主要的逻辑:
 * 1、在1步骤中通过getRetriever获取到在初始化Glide时创建的RequestManagerRetriever实例;
 * 2、在2步骤中通过RequestManagerRetriever的get方法获取RequestManager实例。
 *
 * <p>
 * RequestManagerRetriever用于创建新的RequestManagers或从activity和fragment中检索现有的RequestManagers
 *
 * @param context
 * @return
 */
@NonNull
public static RequestManager with(@NonNull Context context) {
    // 调用getRetriever获取RequestManagerRetriever
    RequestManagerRetriever requestManagerRetriever = getRetriever(context);// 1
    // 调用RequestManagerRetriever的get方法,获取RequestManager(图片请求加载管理者)
    RequestManager requestManager = requestManagerRetriever.get(context); // 2
    return requestManager;
}


/**
 * 请求资源使用Activityh生命周期同步
 *
 * @param activity
 * @return
 */
@NonNull
public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
}

/**
 * 请求资源使用FragmentActivity生命周期同步
 */
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
}

/**
 * 请求资源使用Fragment生命周期同步
 */
@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
}

/**
 * 请求资源使用android.app.Fragment生命周期同步
 */
@SuppressWarnings("deprecation")
@Deprecated
@NonNull
public static RequestManager with(@NonNull android.app.Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
}

/**
 * 通过View去搜索其依附的Activity或者Fragment并使用它们作为请求资源使用生命周期同步
 */
@NonNull
public static RequestManager with(@NonNull View view) {
    return getRetriever(view.getContext()).get(view);
}

可以看到Glide.with(context)有几个重载的方法,Glide.with(context)方法主要的逻辑:
1、在1步骤中通过getRetriever获取到在初始化Glide时创建的RequestManagerRetriever实例;
2、在2步骤中通过RequestManagerRetriever的get方法获取RequestManager实例。 RequestManagerRetriever用于创建新的RequestManagers或从activity和fragment中检索现有的RequestManagers

Glide.with(context)最终调用getRetriever(context),然后再调用RequestManagerRetriever的实例get()方法,去获取RequestManager对象:

  /**
 * getRetriever方法主要逻辑:
 * 1、在1步骤,会创建Glide实例并且初始化Glide实例,还会创建RequestManagerRetriever实例对象;
 * 2、在2步骤直接返回RequestManagerRetriever实例
 *
 * @param context
 * @return
 */
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) { // 1.2
    // Glide.get(context) 获取Glide对象,包括初始化之类的配置
    Glide glide = Glide.get(context);//1
    // 通过Glide获取RequestManagerRetriever
    // 可能你会惊讶在哪 初始化RequestManagerRetriever的,其实是在GlideBuilder的build方法中
    RequestManagerRetriever requestManagerRetriever = glide.getRequestManagerRetriever();// 2
    return requestManagerRetriever;
}

1、 Glide.get(context) 获取Glide对象包括初始化之类的配置,初始化后面会讲GlideBiulder;
2、通过Glide对象获取RequestManagerRetriever, 可能你会惊讶在哪 初始化RequestManagerRetriever的,其实是在GlideBuilder的build方法中。

接下来看一下调用RequestManagerRetriever 的get方法,也是有几个重载;

 /**
 * <p>
 * 上面getRetriever(context)方法,主要是构建Glide和RequestManagerRetriever
 * <p>
 * RequestManagerRetriever:一组静态方法,用于创建新的RequestManagers或从活动和片段中检索现有的RequestManagers。
 * <p>
 * 这一步就是构建RequestManager:描述图片加载请求管理者
 * <p>
 * <p>
 * 作用:主要是通过传入的context构建RequestManager实例,当然你这里可会从activity和fragment中获取已经存在的RequestManager
 * 为什么需要context?主要是构建的RequestManager即请求管理,需要感知各组件的生命周期,例如:application、activity和fragment
 * <p>
 * 主要的逻辑:
 * 1、在1步骤中判断是否是在主线程和是不是Application,如果我们是在非主线程当中使用的Glide,那么不管你是传入的Activity还是Fragment,都会被强制当成Application来处理
 * 2、在2步骤中直接当成Application处理;
 * 3、最后会通过RequestManager工厂(RequestManagerFactory)创建RequestManager实例
 * <p>
 * 最后:
 * 虽然get方法有多个重载其实逻辑的处理只有两种:
 * 1、传入的context是非Application参数的情况:
 * 不管是传入的context是Activity、FragmentActivity、v4包下的Fragment、还是app包下的Fragment,
 * 最终的逻辑是一样的,会向当前的Activity当中添加一个隐藏的Fragment。因为Glide需要感知加载的生命周期
 * 2、传入的context是Application参数的情况,或者是在后台线程:
 * 如果传入的context是Application或者是在后台线程,直接就调用getApplicationManager()方法来获取一个RequestManager对象
 * <p>
 * <p>
 *
 * <p>
 * 注意这里的get方法都是单例的
 *
 * @param context
 * @return
 */
@NonNull
public RequestManager get(@NonNull Context context) {
    if (context == null) {
        throw new IllegalArgumentException("You cannot start a load on a null Context");
        //如果我们是在非主线程当中使用的Glide,那么不管你是传入的Activity还是Fragment,都会被强制当成Application来处理
    } else if (Util.isOnMainThread() && !(context instanceof Application)) {//1
        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);// 2
}

@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
        return get(activity.getApplicationContext());
    } else {

        assertNotDestroyed(activity);
        // 获取当前activity的FragmentManager,因为你需要为当前activity添加隐藏的fragment
        FragmentManager fm = activity.getSupportFragmentManager();
        return supportFragmentGet(activity, fm, null, isActivityVisible(activity));
    }
}

 @NonNull
public RequestManager get(@NonNull Fragment fragment) {
    Preconditions.checkNotNull(fragment.getActivity(),
            "You cannot start a load on a fragment before it is attached or after it is destroyed");
    if (Util.isOnBackgroundThread()) {
        return get(fragment.getActivity().getApplicationContext());
    } else {
        FragmentManager fm = fragment.getChildFragmentManager();
        return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
    }
}


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

@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull View view) {
    if (Util.isOnBackgroundThread()) {
        return get(view.getContext().getApplicationContext());
    }

    Preconditions.checkNotNull(view);
    Preconditions.checkNotNull(view.getContext(),
            "Unable to obtain a request manager for a view without a Context");
    Activity activity = findActivity(view.getContext());
    // The view might be somewhere else, like a service.
    if (activity == null) {
        return get(view.getContext().getApplicationContext());
    }

    // Support Fragments.
    // Although the user might have non-support Fragments attached to FragmentActivity, searching
    // for non-support Fragments is so expensive pre O and that should be rare enough that we
    // prefer to just fall back to the Activity directly.
    if (activity instanceof FragmentActivity) {
        Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
        return fragment != null ? get(fragment) : get(activity);
    }

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

1、上面getRetriever(context)方法,主要是构建Glide和RequestManagerRetriever
2、RequestManagerRetriever:一组静态方法,用于创建新的RequestManagers或从活动和片段中检索现有的RequestManagers。
3、这一步就是构建RequestManager:描述图片加载请求管理者
4、 作用:主要是通过传入的context构建RequestManager实例,当然你这里可会从activity和fragment中获取已经存在的RequestManager
5、为什么需要context?主要是构建的RequestManager即请求管理,需要感知各组件的生命周期,例如:application、activity和fragment
6、主要的逻辑:

  • 在1步骤中判断是否是在主线程和是不是Application,如果我们是在非主线程当中使用的Glide,那么不管你是传入的Activity还是Fragment,都会被强制当成Application来处理
    • 在2步骤中直接当成Application处理;
    • 最后会通过RequestManager工厂(RequestManagerFactory)创建RequestManager实例
      最后:虽然get方法有多个重载其实逻辑的处理只有两种:传入的context是非Application参数的情况:不管是传入的context是Activity、FragmentActivity、v4包下的Fragment、还是app包下的Fragment,最终的逻辑是一样的,会向当前的Activity当中添加一个隐藏的Fragment。因为Glide需要感知加载的生命周期,
      传入的context是Application参数的情况,或者是在后台线程:
      如果传入的context是Application或者是在后台线程,直接就调用getApplicationManager()方法来获取一个RequestManager对象
      注意这里的get方法都是单例的

可以看到在get方法中通过supportFragmentGet方法和getSupportRequestManagerFragment构建隐藏的fragment:

 /**
 * 主要逻辑:
 * 1、搜索是否已经添加了隐藏的Fragment到当前的Activity中,如果已经添加过了是由缓存的。
 * 2、如果在当前activity,已经添加了隐藏的Fragment,就尝试从隐藏的Fragment中搜索RequestManager,
 * 如果没有找到,就调用RequestManager工厂创建一个RequestManager,并添加到隐藏的Fragment中,然后返回RequestManager实例。
 * <p>
 * 注意:其实一个Activity就存在一个RequestManager,它们的关系是activity-SupportRequestManagerFragment-RequestManager一一对应
 *
 * @param context
 * @param fm
 * @param parentHint
 * @param isParentVisible
 * @return
 */
@NonNull
private RequestManager supportFragmentGet(@NonNull Context context, @NonNull FragmentManager fm,
                                          @Nullable Fragment parentHint, boolean isParentVisible) {
    // 搜索隐藏的Fragment
    SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
    //从隐藏的Fragment搜索RequestManager
    RequestManager requestManager = current.getRequestManager();

    // 如果不存在就直接创建RequestManager并添加到隐藏的Fragment中缓存
    if (requestManager == null) {
        Glide glide = Glide.get(context);
        requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
        current.setRequestManager(requestManager);
    }
    return requestManager;
}

@NonNull
private SupportRequestManagerFragment getSupportRequestManagerFragment(@NonNull final FragmentManager fm,
                                                                       @Nullable Fragment parentHint, boolean isParentVisible) {
    // 搜索是否已经添加了隐藏的Fragment
    SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);//1
    if (current == null) {
        // 从缓存中找
        current = pendingSupportRequestManagerFragments.get(fm);// 2

        // 还是没有找到,就直接创建一个Fragment
        if (current == null) {
            current = new SupportRequestManagerFragment();
            current.setParentFragmentHint(parentHint);

            // 如果当前activity正在显示
            if (isParentVisible) {
                current.getGlideLifecycle().onStart();
            }

            // 加入缓存
            pendingSupportRequestManagerFragments.put(fm, current);
            // 开始fragment事务,标志设置为FRAGMENT_TAG
            fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
            handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
        }
    }
    return current;
}

1、搜索是否已经添加了隐藏的Fragment到当前的Activity中,如果已经添加过了是由缓存的。
2、如果在当前activity,已经添加了隐藏的Fragment,就尝试从隐藏的Fragment中搜索RequestManager, 如果没有找到,就调用RequestManager工厂创建一个RequestManager,并添加到隐藏的Fragment中,然后返回RequestManager实例。 注意:其实一个Activity就存在一个RequestManager,它们的关系是activity-SupportRequestManagerFragment-RequestManager一一对应。

为什么需要使用pendingRequestManagerFragments?

为什么需要使用pendingRequestManagerFragments这样一个集合来临时存储一下fragment,然后又马上通过handler发送消息移除?这其实是跟主线程的Looper机制和Fragment的事务机制有关的,Fragment事务也是通过Handler处理的。我们知道,android中的主线程是一个闭环,通过Handler发送消息到MessageQueue,然后通过Looper轮询获取消息并交给Handler处理,我们来看如下常见场景,这里所说的是在线程中,如果是子线程,那么不会有添加Fragment这一步,也就是放弃监听生命周期。

Glide.with(this).load(url_1).into(mImageView_1);//1
Glide.with(this).load(url_2).into(mImageView_2);//2

看看这两行代码的Handler消息处理图


handler.png

当我们执行注释1代码时:fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
时对应m1,执行handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();以此类推,但是Handler中MessageQueue是同步取出Message处理,它一般会先执行m1-->r1 -->m2 -->r2,所以Glide为了保证RootFragment的唯一性,加上临时缓存,上面两行代码时同步执行,当第一行执行完那么缓存中必然存在rootfragment。

到此为止就已经创建你好RequestManager了,也就是Glide的整个构建流程完毕,有的读者可能会发现Glide的构建怎么没有,下面就开始。

大家还记得 getRetriever(context)方法中有这么一行

 Glide glide = Glide.get(context);

我们来看看Glide get(@NonNull Context context)源码

private static volatile Glide glide;
/**
 * Get the singleton.
* 单例双重校验
 */
@NonNull
public static Glide get(@NonNull Context context) {
    if (glide == null) {
        synchronized (Glide.class) {
            if (glide == null) {
                checkAndInitializeGlide(context);
            }
        }
    }
    return glide;
}

 private static void checkAndInitializeGlide(@NonNull Context context) {
    if (isInitializing) {
        throw new IllegalStateException("You cannot call Glide.get() in registerComponents(),"
                + " use the provided Glide instance instead");
    }
    isInitializing = true;
    initializeGlide(context);
    isInitializing = false;
}

这个方法首先会检查是否已经初始化了,因为在初始化Glide的线程中,一个或多个类可以多次调用Glide.get(context)。
如果没有这个检查,那些调用可能会触发无限递归。就是避免重复初始化Glide对象

下面就是Glide初始化真正的逻辑:

 /**
 * 首先找到@GlideModel注解生成的类GeneratedAppGlideModuleImpl
 * 
 * @param context
 * @param builder
 */
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    //获取 applicationContext
    Context applicationContext = context.getApplicationContext();
    // 找到@GlideModel注解生成的类如GeneratedAppGlideModuleImpl
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
    /**
     * 
     * 
     * 会触发每个Module包括我们自定义的Module中的applyOptions
     *
     * 在GlideBuilder中的biuld方法,每一个默认配置都是判断null,所以我们可以在我们的自定义Module中修改Glide的配置
     *
     */
    if (annotationGeneratedModule != null) {
        annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
    // 构建Glide
    Glide glide = builder.build(applicationContext);

    // 注册组件,例如:替换默认HttpUrlConnection为OkHttp以及其他组件,当然这些是可以通过注解来完成的,详细请看官方的demo
    if (annotationGeneratedModule != null) {
        annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
    }
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
}

首先找到@GlideModel注解生成的类GeneratedAppGlideModuleImpl,并且会触发每个Module包括我们自定义的Module中的applyOptions,以便我们修改Glide的默认配置。也就是说当你调用with方法时,就已经初始化Glide

再看看initializeGlide方法中初始化相关的配置GlideBiulder,这里使用构建模式

 // 构建Glide
 Glide glide = builder.build(applicationContext);

GlideBuilder:

private final Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions = new ArrayMap<>();
//  负责启动负载并管理活动和缓存资源。
private Engine engine;
// Bitmap 复用池(享元模式)
private BitmapPool bitmapPool;

private ArrayPool arrayPool;

// 内存管理计算器或者策略
private MemorySizeCalculator memorySizeCalculator;
private MemoryCache memoryCache;

// Glide 资源线程池
private GlideExecutor sourceExecutor;

// 磁盘缓存线程池
private GlideExecutor diskCacheExecutor;
// 磁盘缓存工厂
private DiskCache.Factory diskCacheFactory;

// 网络状态监视器工厂
private ConnectivityMonitorFactory connectivityMonitorFactory;
private int logLevel = Log.INFO;
// 默认请求项
private RequestOptions defaultRequestOptions = new RequestOptions();
@Nullable
private RequestManagerFactory requestManagerFactory;
//执行动画的线程池
private GlideExecutor animationExecutor;
private boolean isActiveResourceRetentionAllowed;
@Nullable
private List<RequestListener<Object>> defaultRequestListeners;
private boolean isLoggingRequestOriginsEnabled;

出了这些成员变量之外还有就是这些变量的setxx方法,我就不一一列出来,重点看看biuld方法。

 /**
 * 其实每次判断是否为null就是不应该重复初始化,这也给我们在自定义Model修改Glide的默认配置
 *
 * @param context
 * @return
 */
@NonNull
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();
    }

    // bitmap 复用池
    if (bitmapPool == null) {
        int size = memorySizeCalculator.getBitmapPoolSize();
        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);
    }

    //一组静态方法,用于创建新的RequestManagers或从活动和片段中(Retriever)检索现有的RequestManager。
    // RequestManager 描述图片加载请求管理者,例如:感知声明周期取消请求
    RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory);

    // 创建GLide对象
    return new Glide(
            context,
            engine,
            memoryCache,
            bitmapPool,
            arrayPool,
            requestManagerRetriever,
            connectivityMonitorFactory,
            logLevel,
            defaultRequestOptions.lock(),
            defaultTransitionOptions,
            defaultRequestListeners,
            isLoggingRequestOriginsEnabled);
}

其实Glide的构建是相当复杂的,包括各种线程池的配置以及各种复用池的构建,通过GlideBuilder构建Glide,RequestManager见名思意就是检索RequestManager即在activity或者Fragment中检索是否存在,否则直接创建。

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

推荐阅读更多精彩内容

  • 一、简介 在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫Glide的图片加载库,作者是bumptech。这...
    天天大保建阅读 7,484评论 2 28
  • 7.1 压缩图片 一、基础知识 1、图片的格式 jpg:最常见的图片格式。色彩还原度比较好,可以支持适当压缩后保持...
    AndroidMaster阅读 2,519评论 0 13
  • 在一个 Gradle 项目中在你的 build.gradle中添加下面这行代码: 从一个 URL 中加载图片就像 ...
    A_Coder阅读 1,985评论 0 4
  • Glide4源码解析系列 [Glide4源码解析系列]--1.Glide初始化 [Glide4源码解析系列]--...
    开发的猫阅读 5,238评论 4 33
  • 小丸子喜欢花伦 阿狸喜欢桃子 海绵宝宝喜欢派大星 蜡笔小新喜欢本宫娜娜子 工藤新一喜欢毛利兰 大雄喜欢静香 鸣人喜...
    青灰摇阅读 268评论 0 1