Android Fragment 实现懒加载

Fragment 懒加载使用场景

当使用viewpager+adapter作为应用大的布局时,viewpager会通过setOffscreenPageLimit来设置预加载的项目,不设置setOffscreenPageLimit,则默认为1(设置0无效,可以查看该方法源码知道),也就是当我们打开应用看到的时候fragmentOne时,实际上其他fragment(例如fragmentSecond)也进行了加载,只不过没有显示出来罢了,但是这样就造成了不必要的资源浪费(例如,fragmentSecond没有显示,但是却进行了大量的网络加载操作)。

以下场景可以使用懒加载:

  1. 预加载Fragment时,也就是加载不可见的Fragment时,该不可见的Fragment初始化数据和页面可能占用了大量的资源;

  2. 如果当页面内有动画或者是存在其它持续性的操作,fragment的状态切换时操作的开关问题;

  3. 初始化时代码量大的问题, inflater.inflate(R.layout.xxx,Container,false)都是多余重复的操作;

Fragment的生命周期回顾

这里写图片描述

onAttach()

Fragment已经关联到Activity,此时Activity已经传递给Fragment, 但此时Activity不一定创建完成,所以此时不一定可以相互通信,但是只会回调一次., 完全与Activity相互通信可以在onActivityCreated()中进行

/**
 * Called when a fragment is first attached to its context.
 * {@link #onCreate(Bundle)} will be called after this.
 */
@CallSuper
public void onAttach(Context context) {
    mCalled = true;
    final Activity hostActivity = mHost == null ? null : mHost.getActivity();
    if (hostActivity != null) {
        mCalled = false;
        onAttach(hostActivity);
    }
}

onCreate()

系统创建fragment的时候回调他,在他里面实例化一些变量,这些个变量主要是:当你 暂停 停止的时候 你想保持的数据 ,如果我们要为fragment启动一个后台线程,可以考虑将代码放于此处。

参数是:Bundle savedInstance, 用于保存 Fragment 参数, Fragement 也可以 重写 onSaveInstanceState(BundleoutState) 方法, 保存Fragement状态;
可以用于 文件保护

这个方法只会调用一次

  /**
     * Called to do initial creation of a fragment.  This is called after
     * {@link #onAttach(Activity)} and before
     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}, but is not called if the fragment
     * instance is retained across Activity re-creation (see {@link #setRetainInstance(boolean)}).
     *
     * <p>Note that this can be called while the fragment's activity is
     * still in the process of being created.  As such, you can not rely
     * on things like the activity's content view hierarchy being initialized
     * at this point.  If you want to do work once the activity itself is
     * created, see {@link #onActivityCreated(Bundle)}.
     *
     * <p>If your app's <code>targetSdkVersion</code> is 23 or lower, child fragments
     * being restored from the savedInstanceState are restored after <code>onCreate</code>
     * returns. When targeting N or above and running on an N or newer platform version
     * they are restored by <code>Fragment.onCreate</code>.</p>
     *
     * @param savedInstanceState If the fragment is being re-created from
     * a previous saved state, this is the state.
     */

onCreateView()

第一次使用的时候 fragment会在这上面画一个layout出来,为了可以画控件 要返回一个 布局的view,也可以返回null.

当系统用到fragment的时候 fragment就要返回他的view,越快越好,所以尽量在这里不要做耗时操作,比如从数据库加载大量数据显示listview,当然子线程还是可以的。

给当前的fragment绘制ui布局,可以使用线程更新UI,说白了就是加载fragment的布局的。

这里一般都先判断是否为null
如果在onCreateView里面初始化控件会慢很多,比如Listview等.

// 判断View是否存在,避免每次都要加载,节省资源
if(text==null){
    Bundle args=getArguments();
    text=args.getString("text");
}
if (view == null) {
    view = inflater.inflate(R.layout.hello, null);
}

onActivityCreated()

当Activity中的onCreate方法执行完后调用。

从这句官方的话可以看出:当执行onActivityCreated()的时候 activity的onCreate才刚完成。所以在onActivityCreated()调用之前 activity的onCreate可能还没有完成,所以不能再onCreateView()中进行 与activity有交互的UI操作,UI交互操作可以砸onActivityCreated()里面进行。

所以呢:这个方法主要是初始化那些你需要你的父Activity或者Fragment的UI已经被完整初始化才能初始化的元素。

onStart()

和activity一致 启动, Fragement 启动时回调, 此时Fragement可见;

onResume()

和activity一致 在activity中运行是可见的激活, Fragement 进入前台, 可获取焦点时激活;

onPause()

和activity一致 其他的activity获得焦点,这个仍然可见 第一次调用的时候,指的是用户离开这个fragment(并不是被销毁)通常用于用户的提交(可能用户离开后不会回来了)

onStop()

和activity一致fragment不可见的, 可能情况:activity被stopped了 或者 fragment被移除但被加入到回退栈中,一个stopped的fragment仍然是活着的如果长时间不用也会被移除

onDestoryView()

Fragment中的布局被移除时调用.表示fragemnt销毁相关联的UI布局,清除所有跟视图相关的资源

以前以为这里没什么用处其实 大有文章可做,
相信大家都用过ViewPager+Fragment,由于ViewPager的缓存机制,每次都会加载3页。

例如:有四个 fragment 当滑动到第四页的时候 第一页执行onDestroyView(),但没有执行onDestroy。他依然和activity关联。当在滑动到第一页的时候又执行了 onCreateView()。 生命周期可以自己试一下。
那么问题来了。会出现重复加载view的局面,所以这么做(下面是先人的代码)

@Override
public void onDestroyView() {
    Log.i("onDestroyView_Fragment");
    if(view!=null){
                    ((ViewGroup)view.getParent()).removeView(view);
    }
    super.onDestroyView();
}

onDestory()

同Actiivty

onDetach()

与Activity脱离

使用setUserVisibleHint()实现懒加载

Set a hint to the system about whether this fragment's UI is currently visible to the user. 
An app may set this to false to indicate that the fragment's UI is scrolled out of visibility 
or is otherwise not directly visible to the user. 
This may be used by the system to prioritize operations     such as fragment lifecycle updates 
or loader ordering behavior.

这个SetUserVisibleHint()的方法就是判断这个fragment对用户是否可见的

唯一需要值得注意的是,setUserVisibleHint是在onCreateView周期前调用,此时布局中各View还未初始化,所以只能在setUserVisibleHint中进行纯数据的加载。那么牵涉到ui的操作(比如为某个view设置数据)该放在那里呢?

代码实例

public abstract class BaseFragment extends Fragment {

    private static final String TAG = "tianrui";

    protected boolean mIsVisible;
    protected boolean mHasLoaded;
    protected boolean mHasPrepare;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate: ");
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        Log.d("tianrui", "onViewCreated: ");
        super.onViewCreated(view, savedInstanceState);
        ButterKnife.bind(this, view);
        if (mIsVisible) {
            Log.d(TAG, "onViewCreated: load data in #onViewCreated ");
            initData();
            mHasLoaded = true;
        }
        mHasPrepare = true;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        Log.d("tianrui", "setUserVisibleHint: " + isVisibleToUser);
        mIsVisible = getUserVisibleHint();
        lazyLoad();
    }

    @Override
    public void onDestroyView() {
        Log.d(TAG, "onDestroyView: ");
        super.onDestroyView();
        mHasLoaded = false;
        mHasPrepare = false;
    }


    protected void lazyLoad() {
        Log.d(TAG, "lazyLoad: mIsVisible " + mIsVisible + " mHasLoaded " + mHasLoaded + " mHasPrepare " + mHasPrepare);
        if (!mIsVisible || mHasLoaded || !mHasPrepare) {
            return;
        }
        Log.d(TAG, "lazyLoad: load data in lazyLoad ");
        initData();
    }

    abstract protected void initData();
}

子类实现initData()绑定数据即可


public class FeedFragment extends BaseFragment implements FeedContract.View {

    private static final String TAG = "feed_frag";

    @BindView(R.id.id_rv_list)
    RecyclerView mRvList;

    @BindView(R.id.id_srl_refresh)
    SwipeRefreshLayout mSrlRefresh;

    @BindView(R.id.id_progress_bar)
    ProgressBar mProgressBar;

    @BindView(R.id.fab)
    FloatingActionButton mFab;

    private FeedContract.Presenter mPresenter;
    private String mChannel;

    private interface ExtraKey {
        String CHANNEL_KEY = "channel_key";

    }

    private interface ViewStatus {
        int REFRESHING = 0x01;
        int REFRESH_SUCCESS = 0x02;
    }


    public FeedFragment() {

    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final Bundle bundle = getArguments();
        if (bundle != null) {
            mChannel = bundle.getString(ExtraKey.CHANNEL_KEY);
        }
        mPresenter = new FeedPresenter(new RemoteFeedDataSource(mChannel), this);
    }

    public static FeedFragment newInstance(String channel) {
        final FeedFragment feedFragment = new FeedFragment();
        final Bundle bundle = new Bundle();
        bundle.putString(ExtraKey.CHANNEL_KEY, channel);
        feedFragment.setArguments(bundle);
        return feedFragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_feed, container, false);
    }

    @Override
    // initData绑定数据, 懒加载节省的是这部分资源.
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }

    @Override
    protected void initData() {
        mPresenter.start();
        mSrlRefresh.setColorSchemeColors(getResources().getColor(R.color.colorAccent));
        mSrlRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                mPresenter.refresh();
            }
        });
    }


    @Override
    public void setPresenter(FeedContract.Presenter presenter) {
        mPresenter = presenter;
    }

    @Override
    public void showFeedList(List<Feed.FeedItem> feedItems) {
        Log.d(TAG, "showFeedList: " + feedItems);
        if (feedItems.isEmpty()) {
            return;
        }
        mRvList.setAdapter(new FeedAdapter(feedItems));
        mRvList.addItemDecoration(new RecyclerView.ItemDecoration() {
            @Override
            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
                outRect.top = 10;
            }
        });
        adjustStatus(ViewStatus.REFRESH_SUCCESS);
    }

    @OnClick(R.id.fab)
    void onFabClick(View view) {
        mPresenter.refresh();
    }

    @Override
    public void showErrorPage() {
        Toast.makeText(getContext(), "网络错误", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showLoading() {
        mSrlRefresh.setRefreshing(true);
        // rotate fab.
        final AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this.getContext(), R.animator.fab_click);
        set.setInterpolator(new AccelerateInterpolator());
        set.setTarget(mFab);
        set.start();
    }

    @Override
    public void refresh(List<Feed.FeedItem> feedItems) {
        if (feedItems == null || feedItems.isEmpty()) {
            return;
        }
        final RecyclerView.Adapter adapter = mRvList.getAdapter();
        if (adapter == null || !(adapter instanceof FeedAdapter)) {
            return;
        }

        ((FeedAdapter) adapter).refreshData(feedItems);

        App.getHandler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mSrlRefresh.setRefreshing(false);
            }
        }, 2000);
        adjustStatus(ViewStatus.REFRESH_SUCCESS);
    }

    private void adjustStatus(int status) {
        switch (status) {
            case ViewStatus.REFRESH_SUCCESS:
                mSrlRefresh.setVisibility(View.VISIBLE);
                mProgressBar.setVisibility(View.GONE);
                break;
            default:
            case ViewStatus.REFRESHING:
                mSrlRefresh.setVisibility(View.GONE);
                mProgressBar.setVisibility(View.VISIBLE);
                break;
        }
    }
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值