Android - 懒加载

今天我们来聊一聊ViewPager+Fragment的懒加载。
参考资料:https://juejin.im/post/5adcb0e36fb9a07aa7673fbc

1.什么是懒加载,为什么要用懒加载?

如果我们的项目中使用了ViewPager+Framgment实现底部Tab可点可滑,那么我们都知道ViewPager有预加载功能,通过viewpager.setOffscreenPageLimit();来设置,不设置默认加载上一个和下一个Fragment页面,带上本身也就是三个页面(当然如果你刚进入就是首页,那么它会加载首页和下一个页面,因为首页上面没有页面呀)。预加载功能会暴露一个问题,比如我刚进入加载首页的数据,但是因为有预加载功能,那么就会执行下一个Tab对应的Fragmeng的生命周期,如果我下一个Tab页数据量小还好,如果我有比较耗时的操作或者网络请求,势必会影响程序的性能,影响用户的体验。那么我们要做的就是禁止ViewPager预加载或者提供一个只在Fragemnt可见的情况下,才去进行耗时操作的方法,只要Fragmeng可见我们就执行该方法。

2.懒加载解决方式

2.1 尝试设置setOffscreenPageLimit(失败)

之前想既然setOffscreenPageLimit可以设置,那我就将其设置为0,结果“然并卵”,查看源码如下:

image.png

可以看到如果我们设置的值,小于DEFAULT_OFFSCREEN_PAGES这个常量值,那么就会被赋值为DEFAULT_OFFSCREEN_PAGES,那么我们看看DEFAULT_OFFSCREEN_PAGES值是多少:
image.png

也就是你设置的setOffscreenPageLimit要大于1才可以生效

2.2 试试懒加载

我们先来看看我们的页面:


image.png

就是正常的viewpager+fragment,每个页面有一个TextView,可点可划。三个Fragment依次是:HomeFragment,InvestFragment,UserFragment,

  • 本次要用到的生命周期的方法是:
    onCreatedView + onResume + onPause + onDestroyView

  • 本次要用到的非生命周期的方法是:setUserVisibleHint
    简单介绍一下此方法:当fragment被用户可见时,setUserVisibleHint()会调用且传入true值,当fragment不被用户可见时,setUserVisibleHint()则得到false值,此方法先于生命周期方法执行

  • Fragment 主要的三个状态:第一次可见,每次可见,每次不可见

  • 对于ViewPager+Fragment使用过程中的三种情况
    (1) 使用 FragmentPagerAdapter ,FragmentPagerStateAdapter不设置 setOffscreenPageLimit数,采用默认方式
    (2)使用 FragmentPagerAdapter ,FragmentPagerStateAdapter设置 setOffscreenPageLimit数,设置为底部Tab总数
    (3)使用 FragmentPagerAdapter ,FragmentPagerStateAdapter进入到其他页面或者点击Home键,返回到桌面。

其他注意的是:使用FragmentPagerAdapter 和FragmentPagerStateAdapter的区别在于FragmentPagerStateAdapter会在Fragment不可见的时候走 detach,而FragmentPagerAdapter不会。

当然我测试用的是FragmentPagerAdapter,我们先看一看正常滑动,不设置setOffscreenPageLimit(),Fragment生命周期是怎么走的,先写一个BaseLazyLoadFragment类继承自Fragment.重写我们刚才说的生命周期的方法,首页等Fragment继承BaseLazyLoadFragment打印生命周期(默认进来先加载首页):
BaseLazyLoadFragment部分代码如下

image.png

Fragment代码如下
image.png

不设置setOffscreenPageLimit(),FragmentPagerAdapter 结果如下


image.png

不设置setOffscreenPageLimit(),FragmentPagerStateAdapter 结果如下:
这个结果我之前有过测试,直接告诉你们把区别是:FragmentPagerStateAdapter 会走一下两个生命周期方法,包括从切到Home页面在返回这是一下走两个生命周期方法。

设置setOffscreenPageLimit()个数为总Tab数,FragmentPagerAdapter 和FragmentPagerStateAdapter结果一致,如下:

设置setOffscreenPageLimit为总数之后,一开始就加载了所有的Fragment
com.hxzk_bj_demo E/HomeFragment: isVisibleToUser------>false
com.hxzk_bj_demo E/InvestFragment:isVisibleToUser------>false
com.hxzk_bj_demo E/UserFragment: isVisibleToUser------>false
com.hxzk_bj_demo E/HomeFragment: isVisibleToUser------>true
com.hxzk_bj_demo E/HomeFragment: ------>onCreateView
com.hxzk_bj_demo E/HomeFragment: ------>onStart
com.hxzk_bj_demo E/HomeFragment: ------>onResume
com.hxzk_bj_demo E/InvestFragment: ------>onCreateView
com.hxzk_bj_demo E/UserFragment: ------>onCreateView
com.hxzk_bj_demo E/InvestFragment: ------>onStart
com.hxzk_bj_demo E/InvestFragment: ------>onResume
com.hxzk_bj_demo E/UserFragment: ------>onStart
com.hxzk_bj_demo E/UserFragment: ------>onResume
//第一个Fragment切换到第二个Fragment
com.hxzk_bj_demo E/HomeFragment: isVisibleToUser------>false
com.hxzk_bj_demo E/InvestFragment: isVisibleToUser------>true
//第二个Fragment切换到第三个Fragment
com.hxzk_bj_demo E/InvestFragment:isVisibleToUser------>false
com.hxzk_bj_demo E/UserFragment: isVisibleToUser------>true
//点击Home
com.hxzk_bj_demo E/HomeFragment: ------>onPause
com.hxzk_bj_demo E/InvestFragment: ------>onPause
com.hxzk_bj_demo E/UserFragment: ------>onPause
//点击Home后返回
com.hxzk_bj_demo E/HomeFragment: ------>onStart
com.hxzk_bj_demo E/InvestFragment: ------>onStart
com.hxzk_bj_demo E/UserFragment: ------>onStart
com.hxzk_bj_demo E/HomeFragment: ------>onResume
com.hxzk_bj_demo E/InvestFragment: ------>onResume
com.hxzk_bj_demo E/UserFragment: ------>onResume

没啥的。

针对不设置setOffscreenPageLimit(),FragmentPagerAdapter 结果我们分析一下:可以看到进入到第一个Fragment的时候,也执行了下一个Fragment的生命周期,执行了不必要的操作。那大家有没有发现,如果那个Fragment的状态为可见其setUserVisibleHint的值就为true,其余Fragment的值为false,那我们只需要判断,如果setUserVisibleHint的值就为true即改Fragment为可见状态,我们就执行耗时操作,其他Fragment为false,就不执行网络请求的操作呗。那我们写一个公共的方法,注意此方法执行,要放到onActivityCreate()之后,否则我请求回来的数据载体控件的Activity都没有创建,所以我要定义几个变量来查看Fragment的状态,我们之前也说了Fragement有首次可见,可见和不可见三种状态,代码如下:


    View rootView;

    /**当前Fragment是否首次可见,默认是首次可见**/
    private boolean mIsFirstVisible = true;
    /**当前Fragment的View是否已经创建**/
    private boolean isViewCreated = false;
    /**当前Fragment的可见状态,一种当前可见,一种当前不可见**/
    private boolean currentVisibleState = false;


    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        LogUtil.e(getClass().getSimpleName(),"-----> onCreateView");

        if(rootView == null){
            rootView = inflater.inflate(getLayoutId(), container, false);
            initView(rootView);
        }
        isViewCreated=true;//在onCreateView执行完毕,将isViewCreated改为true;
        return rootView;
    }

不同生命周期变量值的变更及涉及的相关代码:

  • onStart
 @Override
    public void onStart() {
        super.onStart();
        LogUtil.e(getClass().getSimpleName(),"-----> onStart");
        //isHidden()是Fragment是否处于隐藏状态和isVisible()有区别
        //getUserVisibleHint(),Fragement是否可见
        if(!isHidden()&& getUserVisibleHint()){//如果Fragment没有隐藏且可见
         //执行分发的方法,三种结果对应自Fragment的三个回调,对应的操作,Fragment首次加载,可见,不可见
            disPatchFragment(true);
        }

    }
  • onResume
  @Override
    public void onResume() {
        super.onResume();
        LogUtil.e(getClass().getSimpleName(),"-----> onResume");
        if(!mIsFirstVisible){
//表示点击home键又返回操作,设置可见状态为ture
            if(!isHidden()&& !getUserVisibleHint() && currentVisibleState){
                disPatchFragment(true);
            }
        }

    }
  • onPause
   @Override
    public void onPause() {
        super.onPause();
        //表示点击home键,原来可见的Fragment要走该方法,更改Fragment的状态为不可见
        if(!isHidden()&& getUserVisibleHint()){
            disPatchFragment(false);
        }
    }
  • onDestroyView
   @Override
    public void onDestroyView() {
        super.onDestroyView();
        LogUtil.e(getClass().getSimpleName(),"-----> onStart");
        //当 View 被销毁的时候我们需要重新设置 isViewCreated mIsFirstVisible 的状态
        isViewCreated = false;
        mIsFirstVisible = true;
    }

  • Fragment不同状态对应的回调方法
 /**
     *
     * @param visible Fragment当前是否可见,然后调用相关方法
     */
    public  void   disPatchFragment(boolean visible){
        currentVisibleState=visible;
        if(visible){//Fragment可见
            if(mIsFirstVisible){//可见又是第一次
                mIsFirstVisible=false;//改变首次可见的状态
                onFragmentFirst();
            }else{//可见但不是第一次
                LogUtil.e(getClass().getSimpleName(),"可见");
            }
        }else {//不可见
            LogUtil.e(getClass().getSimpleName(),"不可见");
        }
    };

    //Fragemnet首次可见的方法
    public void onFragmentFirst(){
        LogUtil.e(getClass().getSimpleName(),"首次可见");
    };
    //Fragemnet可见的方法
    public void onFragmentVisble(){//子Fragment调用次方法,执行可见操作
        LogUtil.e(getClass().getSimpleName(),"可见");
    };
    //Fragemnet不可见的方法
    public void onFragmentInVisible(){
        LogUtil.e(getClass().getSimpleName(),"不可见");
    };

最后来一个总的代码:
public abstract class BaseLazyLoadFragment extends android.support.v4.app.Fragment {

View rootView;

/**当前Fragment是否首次可见,默认是首次可见**/
private boolean mIsFirstVisible = true;
/**当前Fragment的View是否已经创建**/
private boolean isViewCreated = false;
/**当前Fragment的可见状态,一种当前可见,一种当前不可见**/
private boolean currentVisibleState = false;


@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    LogUtil.e(getClass().getSimpleName(),"-----> onCreateView");

    if(rootView == null){
        rootView = inflater.inflate(getLayoutId(), container, false);
        initView(rootView);
    }
    isViewCreated=true;//在onCreateView执行完毕,将isViewCreated改为true;
    return rootView;
}


@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    LogUtil.e(getClass().getSimpleName(),"----->"+isVisibleToUser);
    if (isViewCreated) {
        if (isVisibleToUser && !currentVisibleState) {//Fragment可见且状态不是可见(从一个Fragment切换到另外一个Fragment,后一个设置状态为可见)
            disPatchFragment(true);
        } else if (!isVisibleToUser && currentVisibleState) {//Fragment不可见且状态是可见(从一个Fragment切换到另外一个Fragment,前一个更改状态为不可见)
            disPatchFragment(false);
        }
    }
}



/**返回子Fragment的布局id**/
public abstract  int getLayoutId();

/**初始化View的方法**/
public abstract  void initView(View rootView);




@Override
public void onStart() {
    super.onStart();
    LogUtil.e(getClass().getSimpleName(),"-----> onStart");
    //isHidden()是Fragment是否处于隐藏状态和isVisible()有区别
    //getUserVisibleHint(),Fragement是否可见
    if(!isHidden()&& getUserVisibleHint()){//如果Fragment没有隐藏且可见
     //执行分发的方法,三种结果对应自Fragment的三个回调,对应的操作,Fragment首次加载,可见,不可见
        disPatchFragment(true);
    }

}


@Override
public void onResume() {
    super.onResume();
    LogUtil.e(getClass().getSimpleName(),"-----> onResume");
    if(!mIsFirstVisible){
        //表示点击home键又返回操作,设置可见状态为ture
        if(!isHidden()&& !getUserVisibleHint() && currentVisibleState){
            disPatchFragment(true);
        }
    }

}


@Override
public void onPause() {
    super.onPause();
    //表示点击home键,原来可见的Fragment要走该方法,更改Fragment的状态为不可见
    if(!isHidden()&& getUserVisibleHint()){
        disPatchFragment(false);
    }
}


@Override
public void onDestroyView() {
    super.onDestroyView();
    LogUtil.e(getClass().getSimpleName(),"-----> onStart");
    //当 View 被销毁的时候我们需要重新设置 isViewCreated mIsFirstVisible 的状态
    isViewCreated = false;
    mIsFirstVisible = true;
}


/**
 *
 * @param visible Fragment当前是否可见,然后调用相关方法
 */
public  void   disPatchFragment(boolean visible){
    currentVisibleState=visible;
    if(visible){//Fragment可见
        if(mIsFirstVisible){//可见又是第一次
            mIsFirstVisible=false;//改变首次可见的状态
            onFragmentFirst();
        }else{//可见但不是第一次
            LogUtil.e(getClass().getSimpleName(),"可见");
        }
    }else {//不可见
        LogUtil.e(getClass().getSimpleName(),"不可见");
    }
};

//Fragemnet首次可见的方法
public void onFragmentFirst(){
    LogUtil.e(getClass().getSimpleName(),"首次可见");
};
//Fragemnet可见的方法
public void onFragmentVisble(){//子Fragment调用次方法,执行可见操作
    LogUtil.e(getClass().getSimpleName(),"可见");
};
//Fragemnet不可见的方法
public void onFragmentInVisible(){
    LogUtil.e(getClass().getSimpleName(),"不可见");
};

}

我们的Fragment只需要继承BaseLazyLoadFragment,然后对应调用首次可见方法,再次可见方法,不可见方法做相应的操作就可以了。

懒加载进阶

我们上面说的是一层的ViewPager加Fragment,但大家也一定遇到过Fragemgt中又来了一层ViewPager+Fragment,如图:


那这种的怎么办呢?之前的方法还管用不,别急,我们先看看其生命周期打印:

纳尼,InvestFragment(投资)内部第一个Tab,Invest_MineFragment竟然执行了首次可见的方法,要知道此时的InvestFragmetn都没有显示。
针对此问题,我的解决方法是,先判断父Fragment如果没有显示,那么不触发我们定义的方法,代码如下:

  /**
     *判断多层嵌套的父Fragment是否显示
     */
    private boolean isParentFragmentInvisible() {
        //获取父Fragment的可见状态
        BaseLazyLoadFragment fragment = (BaseLazyLoadFragment) getParentFragment();
   //如果两个都满足即父Fragment不为空且父Fragment不可见,返回true
        return fragment != null && !fragment.getCurrentVisibleState();
    }
    
    private boolean getCurrentVisibleState() {
        return currentVisibleState;
    }

ok,我们在运行一下,打印如下:

image.png

感觉没啥问题?
别急,还有剩余部分打印信息:
image.png

也就是我们还需要一个第一个子Fragment的状态信息:解决思路如下:
由于父Fragment的执行在子Fragment之前,所以,当我们在父 Fragment 分发完成自己的可见事件后,让子 Fragment 再次调用自己的可见事件分发方法,这次我们让 isParentFragmentVsible() 返回 false ,可见状态将会正确分发了,有点类似于父类完成后,又调用方法刷新子类

 /**
     *
     * @param visible Fragment当前是否可见,然后调用相关方法
     */
    public  void   disPatchFragment(boolean visible){
        String aa =getClass().getSimpleName();
        //如果父Fragment不可见,则不向下分发给子Fragment
        if(visible && isParentFragmentVsible())return;
        currentVisibleState=visible;
        if(visible){//Fragment可见
            if(mIsFirstVisible){//可见又是第一次
                mIsFirstVisible=false;//改变首次可见的状态
                onFragmentFirst();
            }//可见但不是第一次
                onFragmentVisble();
            //可见状态的时候内层 fragment 生命周期晚于外层 所以在 onFragmentResume 后分发
            dispatchChildFragmentVisibleState(true);
        }else {//不可见
            onFragmentInVisible();
            dispatchChildFragmentVisibleState(false);
        }
    };


    /**
     * 父Fragment分发完成之后再次调用,重新分发给子Fragment
     * @param visible
     */
    private void dispatchChildFragmentVisibleState(boolean visible) {
        FragmentManager childFragmentManager = getChildFragmentManager();
        @SuppressLint("RestrictedApi") List<Fragment> fragments = childFragmentManager.getFragments();
        if(fragments != null){
            if (!fragments.isEmpty()) {
                for (Fragment child : fragments) {
                    if (child instanceof BaseLazyLoadFragment && !child.isHidden() && child.getUserVisibleHint()) {
                        ((BaseLazyLoadFragment) child).disPatchFragment(visible);
                    }
                }
            }
        }

    }

这样就ok了,然后我们点击home键:


这又是什么问题,Invest_YourFragment为什么走了两边?
原因处在顺序调用上,我刚才说了:父 Fragment总是优先于子 Fragment,而对于不可见事件,内部的 Fragment 生命周期总是先于外层 Fragment。回到我们代码里:父Fragment调用自身的 disPatchFragment方法分发了不可见事件,又会再次调用 dispatchChildFragmentVisibleState ,导致子 Fragment 再次调用自己的 disPatchFragment再次调用了一次 不可见事件onFragmentInVisible,故产生了两次。

解决办法就是我们之前定义的变量:currentVisibleState,如果当前的 Fragment 要分发的状态与 currentVisibleState 相同我们就没有必要去做分发了。
代码及添加位置如下:
``


image.png

``
最后附上总代码,编写Fragment时,只需要继承该类,然后调用可见的方法就好了。

public abstract class BaseLazyLoadFragment extends Fragment {
    
    View rootView;

    /**当前Fragment是否首次可见,默认是首次可见**/
    private boolean mIsFirstVisible = true;
    /**当前Fragment的View是否已经创建**/
    private boolean isViewCreated = false;
    /**当前Fragment的可见状态,一种当前可见,一种当前不可见**/
    private boolean currentVisibleState = false;


    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        LogUtil.e(getClass().getSimpleName(),"-----> onCreateView");

        if(rootView == null){
            rootView = inflater.inflate(getLayoutId(), container, false);
            initView(rootView);
        }
        isViewCreated=true;//在onCreateView执行完毕,将isViewCreated改为true;
        return rootView;
    }



    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isViewCreated) {
            if (isVisibleToUser && !currentVisibleState) {//Fragment可见且状态不是可见(从一个Fragment切换到另外一个Fragment,后一个设置状态为可见)
                disPatchFragment(true);
            } else if (!isVisibleToUser && currentVisibleState) {//Fragment不可见且状态是可见(从一个Fragment切换到另外一个Fragment,前一个更改状态为不可见)
                disPatchFragment(false);
            }
        }
    }



    /**返回子Fragment的布局id**/
    public abstract  int getLayoutId();

    /**初始化View的方法**/
    public abstract  void initView(View rootView);




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

        //isHidden()是Fragment是否处于隐藏状态和isVisible()有区别
        //getUserVisibleHint(),Fragement是否可见
        if(!isHidden()&& getUserVisibleHint()){//如果Fragment没有隐藏且可见
         //执行分发的方法,三种结果对应自Fragment的三个回调,对应的操作,Fragment首次加载,可见,不可见
            disPatchFragment(true);
        }

    }


    @Override
    public void onResume() {
        super.onResume();
        if(!mIsFirstVisible){
            //表示点击home键又返回操作,设置可见状态为ture
            if(!isHidden()&& !getUserVisibleHint() && currentVisibleState){
                disPatchFragment(true);
            }
        }

    }


    @Override
    public void onPause() {
        super.onPause();
        //表示点击home键,原来可见的Fragment要走该方法,更改Fragment的状态为不可见
        if(!isHidden()&& getUserVisibleHint()){
            disPatchFragment(false);
        }
    }


    @Override
    public void onDestroyView() {
        super.onDestroyView();
        //当 View 被销毁的时候我们需要重新设置 isViewCreated mIsFirstVisible 的状态
        isViewCreated = false;
        mIsFirstVisible = true;
    }


    /**
     *
     * @param visible Fragment当前是否可见,然后调用相关方法
     */
    public  void   disPatchFragment(boolean visible){
        String aa =getClass().getSimpleName();
        //如果父Fragment不可见,则不向下分发给子Fragment
        if(visible && isParentFragmentVsible())return;

        // 如果当前的 Fragment 要分发的状态与 currentVisibleState 相同(都为false)我们就没有必要去做分发了。
        if (currentVisibleState == visible) {
            return;
        }

        currentVisibleState=visible;
        if(visible){//Fragment可见
            if(mIsFirstVisible){//可见又是第一次
                mIsFirstVisible=false;//改变首次可见的状态
                onFragmentFirst();
            }//可见但不是第一次
                onFragmentVisble();
            //可见状态的时候内层 fragment 生命周期晚于外层 所以在 onFragmentResume 后分发
            dispatchChildFragmentVisibleState(true);
        }else {//不可见
            onFragmentInVisible();
            dispatchChildFragmentVisibleState(false);
        }
    };


    /**
     * 重新分发给子Fragment
     * @param visible
     */
    private void dispatchChildFragmentVisibleState(boolean visible) {
        FragmentManager childFragmentManager = getChildFragmentManager();
        @SuppressLint("RestrictedApi") List<Fragment> fragments = childFragmentManager.getFragments();
        if(fragments != null){
            if (!fragments.isEmpty()) {
                for (Fragment child : fragments) {
                    if (child instanceof BaseLazyLoadFragment && !child.isHidden() && child.getUserVisibleHint()) {
                        ((BaseLazyLoadFragment) child).disPatchFragment(visible);
                    }
                }
            }
        }

    }

    //Fragemnet首次可见的方法
    public void onFragmentFirst(){
        LogUtil.e(getClass().getSimpleName(),"首次可见");
    };
    //Fragemnet可见的方法
    public void onFragmentVisble(){//子Fragment调用次方法,执行可见操作
        LogUtil.e(getClass().getSimpleName(),"可见");
    };
    //Fragemnet不可见的方法
    public void onFragmentInVisible(){
        LogUtil.e(getClass().getSimpleName(),"不可见");
    };
    

    /**
     *判断多层嵌套的父Fragment是否显示
     */
    private boolean isParentFragmentVsible() {
        BaseLazyLoadFragment fragment = (BaseLazyLoadFragment) getParentFragment();
        return fragment != null && !fragment.getCurrentVisibleState();
    }

    private boolean getCurrentVisibleState() {
        return currentVisibleState;
    }
}

完毕!

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

推荐阅读更多精彩内容

  • 目录介绍 1.什么是懒加载1.1 什么是预加载1.2 懒加载介绍1.3 懒加载概括 2.实际应用中有哪些懒加载案例...
    杨充211阅读 9,669评论 4 15
  • 多层嵌套后的 Fragment 懒加载 印象中从 Feed 流应用流行开始,Fragment 懒加载变成了一个大家...
    醒着的码者阅读 3,822评论 3 60
  • ���最近在重构公司公司的主页,其中用到了懒加载,所以找了网上的懒加载的介绍的博客,读了好几篇关于懒加载的文件,发...
    Jinwong阅读 8,940评论 1 37
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,077评论 25 707
  • 饭后,躺在床上,无聊的翻看着手机,其实也没啥好看的,只是觉得躺着如果不拿个手机,就失去了躺下的意义。不知道从什么时...
    楚心羽阅读 578评论 0 3