ViewPage Fragment 懒加载

来源:互联网 发布:淘宝评价修改步骤 编辑:程序博客网 时间:2024/06/05 00:59

最近遇到这么一个问题,ViewPage+Fragment的传统布局,ABC三个Fragment,其中BC都是内嵌一个RecyclerView,异步加载item,经常遇到一个现象就是点击b的时候,a,c也请求加载了,log如下:

08-08 17:57:39.601 26333-26333/com.nuctech.tr.trapp I/SetupHardwareParameterFragment: onAttach: 08-08 17:57:39.601 26333-26333/com.nuctech.tr.trapp I/SetupHardwareParameterFragment: onCreateView: 08-08 17:57:39.601 26333-26333/com.nuctech.tr.trapp I/SetupHardwareParameterFragment: onViewCreated: 08-08 17:57:39.601 26333-26333/com.nuctech.tr.trapp I/SetupHardwareParameterFragment: onActivityCreated: 08-08 17:57:39.601 26333-26333/com.nuctech.tr.trapp I/SetupHardwareParameterFragment: onResume: 
08-08 18:00:06.201 28740-28740/com.nuctech.tr.trapp I/SetupAlgorithmparameterFragment: onAttach: 08-08 18:00:06.201 28740-28740/com.nuctech.tr.trapp I/SetupAlgorithmparameterFragment: onCreateView: 08-08 18:00:06.201 28740-28740/com.nuctech.tr.trapp I/SetupAlgorithmparameterFragment: onViewCreated: 08-08 18:00:06.201 28740-28740/com.nuctech.tr.trapp I/SetupAlgorithmparameterFragment: onActivityCreated: 08-08 18:00:06.201 28740-28740/com.nuctech.tr.trapp W/FragmentManager: moveToState: Fragment state for SetupAlgorithmparameterFragment{20f1bd68 #4 id=0x7f0f012e android:switcher:2131689774:2} not updated inline; expected state 3 found 208-08 18:00:06.201 28740-28740/com.nuctech.tr.trapp I/SetupAlgorithmparameterFragment: onResume: 
A不贴了,这样的后果就是点击B的时候AC生命周期都走完了,再点击C的时候,生命周期没有变化。。。

1.于是乎我放在C生命周期里的请求在点击B的时候就执行完了,对于移动端的性能还是有影响的。

2.更可气的是我的RecyclerView有一定几率不出现数据(后面有解决办法)。

于是乎懒加载就得提几句了。

下面引用:http://www.jianshu.com/p/ce429d72228f

懒加载的技术关键点是什么?

根据定义:

所谓懒加载,即Fragment 的 UI 对用户可见时才加载数据。

需要判断何时 Fragment 的 UI 才对用户可见

如何判断 Fragment 的 UI 是否对用户可见?

Fragment 提供了一个方法 public void setUserVisibleHint(boolean isVisibleToUser),API 的注释如下

Set a hint to the system about whether this fragment's UI is currently visible to the user. This hint defaults to true and is persistent across fragment instance state save and restore.

所以,只需要判断参数 isVisibleToUser 是否为 True 即可知道该 Fragment 的 UI 是否对用户可见。

setUserVisibleHint 在什么时候调用?

对于单个 Fragment,setUserVisibleHint 是不会被调用的,只有该 Fragment 在 ViewPager 里才会被调用。所以,我写了一个 ViewPager + Fragment 的 Demo,打印了一下 Log。

D/Owen_TestFragment: setUserVisibleHint: isVisibleToUser = falseD/Owen_TestFragment: onAttachD/Owen_TestFragment: onCreateD/Owen_TestFragment: setUserVisibleHint: isVisibleToUser = trueD/Owen_TestFragment: onCreateViewD/Owen_TestFragment: onActivityCreatedD/Owen_TestFragment: onStartD/Owen_TestFragment: onResumeD/Owen_TestFragment: onPauseD/Owen_TestFragment: onPause D/Owen_TestFragment: onStopD/Owen_TestFragment: onDestroyViewD/Owen_TestFragment: onDestroyD/Owen_TestFragment: onDetach

可以看到 setUserVisibleHint 的执行顺序是

setUserVisibleHint(false) -> onAttach -> onCreate -> setUserVisibleHint(true)  -> onCreateView -> onActivityCreated ->.... -> onDetach

代码

为了方便,封装一个基类 LazyLoadFragment,提供一个 loadData() 方法供调用去加载数据

public abstract class LazyLoadFragment extends Fragment {    /**     * 控件是否初始化完成     */    private boolean isViewCreated;    /**     * 数据是否已加载完毕     */    private boolean isLoadDataCompleted;    @Nullable    @Override    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {        View view = inflater.inflate(getLayout(), container, false);        initViews(view);        isViewCreated = true;        return view;    }    public abstract int getLayout();    public abstract void initViews(View view);    @Override    public void setUserVisibleHint(boolean isVisibleToUser) {        super.setUserVisibleHint(isVisibleToUser);        if (isVisibleToUser && isViewCreated && !isLoadDataCompleted) {            loadData();        }    }    @Override    public void onActivityCreated(@Nullable Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        if (getUserVisibleHint()) {            loadData();        }    }    public void loadData() {        isLoadDataCompleted = true;    }}

等等,为什么 loadData() 会在两个地方执行?在 setUserVisibleHint 方法里执行我还能理解,为什么 onActivityCreated 也要执行呢?

因为,ViewPager 默认显示第一页,第一页肯定要先加载数据啊,而且 setUserVisibleHint 的执行顺序又是在 onCreatView 之前,同时 onCreatView 需要初始化界面和修改 isViewCreated 的值。所以就需要在 onActivityCreated 里执行一次咯。

等等

文章写到这里,我听到了一个不同的声音

ViewPager 不是有 setOffscreenPageLimit(int limit) 方法吗?我调用 viewPager.setOffscreenPageLimit(0) 不就行了吗?

我想说:思路是对的,但是这样做没效果。为什么?看一下 setOffscreenPageLimit 的方法实现就知道了

private static final int DEFAULT_OFFSCREEN_PAGES = 1;public void setOffscreenPageLimit(int limit) {    if (limit < DEFAULT_OFFSCREEN_PAGES) {        Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +                DEFAULT_OFFSCREEN_PAGES);        limit = DEFAULT_OFFSCREEN_PAGES;    }    if (limit != mOffscreenPageLimit) {        mOffscreenPageLimit = limit;        populate();    }}

limit 默认为 1 ,就算传一个 0 也无济于事啊。

总结

  • 懒加载的技术关键点
  • setUserVisibleHint 的执行顺序
  • 为什么 ViewPager.setOffscreenPageLimit(0) 无效?


至于我RecylerView的有时不出数据,滑动一下才出来我就让他加载的时候划一下被。。。上其中一个fragment的所有源码。
package com.nuctech.tr.trapp.fragments;import android.content.Context;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v4.app.Fragment;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.DefaultItemAnimator;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import com.nuctech.tr.trapp.R;import com.nuctech.tr.trapp.adapter.recycleview.DividerItemDecoration;import com.nuctech.tr.trapp.plugin.recycler_plugin.adapter.LoadMoreAdapter.OnLoadMoreListener;import com.nuctech.tr.trapp.plugin.recycler_plugin.utils.RecyclerPlugin;import com.nuctech.tr.trapp.present.SetupDeviceparaPresenter;import com.nuctech.tr.trapp.view.IParaView;import java.lang.ref.WeakReference;import butterknife.Bind;import butterknife.ButterKnife;/** * A simple {@link Fragment} subclass. * Activities that contain this fragment must implement the * {@link SetupHardwareParameterFragment.OnFragmentInteractionListener} interface * to handle interaction events. * Use the {@link SetupHardwareParameterFragment#newInstance} factory method to * create an instance of this fragment. */public class SetupHardwareParameterFragment extends Fragment implements OnLoadMoreListener, IParaView {    private static final String TAG = "SetupHardwareParameterFragment";    @Bind(R.id.id_recyclerview)    RecyclerView idRecyclerview;    //懒加载    /**     * 控件是否完成初始化     */    private boolean isViewCreated;    /**     * 数据是否已经加载完毕     */    private boolean IsLoadDataCompleted;    public SetupDeviceparaPresenter getSetupDeviceparaPresenter() {        return setupDeviceparaPresenter;    }    public void setSetupDeviceparaPresenter(SetupDeviceparaPresenter setupDeviceparaPresenter) {        this.setupDeviceparaPresenter = setupDeviceparaPresenter;    }    private SetupDeviceparaPresenter setupDeviceparaPresenter;    private Context mContext;    private RecyclerPlugin plugin;    public SetupHardwareParameterFragment() {        // Required empty public constructor    }    @Override    public void onAttach(Context context) {        super.onAttach(context);        Log.i(TAG, "onAttach: ");        this.mContext = context;        if (setupDeviceparaPresenter == null)            setupDeviceparaPresenter = new SetupDeviceparaPresenter(mContext);        setupDeviceparaPresenter.attach((IParaView) this);    }    @Override    public void onDetach() {        super.onDetach();        Log.i(TAG, "onDetach: ");        this.mContext = null;        setupDeviceparaPresenter.dettach();    }    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,                             Bundle savedInstanceState) {        Log.i(TAG, "onCreateView: ");        // Inflate the layout for this fragment        View view = inflater.inflate(R.layout.fragment_setup_hardware_parameter, container, false);        ButterKnife.bind(this, view);        idRecyclerview.addItemDecoration(new DividerItemDecoration(mContext, DividerItemDecoration.VERTICAL_LIST));        LinearLayoutManager manager = new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false);        idRecyclerview.setLayoutManager(manager);        idRecyclerview.setItemAnimator(new DefaultItemAnimator());        plugin = new RecyclerPlugin(mContext, idRecyclerview, setupDeviceparaPresenter.getDeviceParaAdapter());        plugin.createAddMore(getActivity().getLayoutInflater(), false, this);        idRecyclerview.setAdapter(plugin.getLastAdapter());        isViewCreated = true;        return view;    }    @Override    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {        super.onViewCreated(view, savedInstanceState);        Log.i(TAG, "onViewCreated: ");//        setupDeviceparaPresenter.bindData();    }    @Override    public void onActivityCreated(@Nullable Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        Log.i(TAG, "onActivityCreated: ");        if (getUserVisibleHint())            setupDeviceparaPresenter.bindData();    }    @Override    public void onResume() {        super.onResume();        Log.i(TAG, "onResume: ");    }    @Override    public void onDestroyView() {        super.onDestroyView();        Log.i(TAG, "onDestroyView: ");        ButterKnife.unbind(this);    }    @Override    public void setUserVisibleHint(boolean isVisibleToUser) {        super.setUserVisibleHint(isVisibleToUser);        if (isVisibleToUser && isViewCreated && !IsLoadDataCompleted) {            setupDeviceparaPresenter.bindData();            Log.i(TAG, "setUserVisibleHint: ");        }    }    @Override    public void onLoadMoreRequested() {    }    @Override    public void updateData() {        ((AppCompatActivity) mContext).runOnUiThread(new MyRunnable(this));        IsLoadDataCompleted = true;    }    public static class MyRunnable implements Runnable {        WeakReference<SetupHardwareParameterFragment> weakReference;        public MyRunnable(SetupHardwareParameterFragment setupAlgorithmparaPresenter) {            weakReference = new WeakReference<SetupHardwareParameterFragment>(setupAlgorithmparaPresenter);        }        @Override        public void run() {            if (weakReference.get() != null) {                weakReference.get().idRecyclerview.getAdapter().notifyDataSetChanged();                if (weakReference.get().idRecyclerview.getAdapter().getItemCount() > 0)                    weakReference.get().idRecyclerview.scrollToPosition(0);            }        }    }}

下班~



0 0