Fragment 懒加载 与 getActivity()返回null的问题

最近实现了一个界面功能,其实很简单就是一个Tab,下面三个可滑动的view。使用TabLayout+ViewPager可以实现这样的效果。一开始ViewPager中我放入的是一个view,但是功能实现完了之后,感觉界面滑动不太顺畅,生命周期什么的都不好控制,后来直接索性就换了Fragment 加载布局。但是ViewPager有预加载机制,即是设置了预加载页数为0,这个方法是不失效的。因为在该方法中做了判断,至少会加载一页。那么在这个activity刚创建的时候就变成需要初始化大量资源。所以采用懒加载的方式来处理。

重点Fragment里的setUserVisibleHint这个方法

官方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.
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.
Parameters

大概意思是这样的:有道~~~

设置一个系统提示是否这个片段的UI是目前用户可见的。这提示默认为正确并持续在片段实例保存和恢复状态。
应用程序可以设置为false,以表明片段的UI是滚动的可见性或另有用户不能直接看到。这可能是使用的系统优化操作,比如片段的生命周期更新或加载程序命令的行为。
参数
isVisibleToUser真实如果这个片段的UI目前可见的用户(默认),如果它不是空的。

代码 Fragment V4包下的
public class abstract  BaseFragment extends Fragment {
    /*
    * 当前界面是否呈现给用户的状态标志
    */
    protected boolean isVisible;
    /**
     * 重写Fragment父类生命周期方法,在onCreate之前调用该方法,实现Fragment数据的缓加载.
     * @param isVisibleToUser 当前是否已将界面显示给用户的状态
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if(getUserVisibleHint()) {
            isVisible = true;
            onVisible();
        } else {
            isVisible = false;
            onInvisible();
        }
    }
    /**
     * 当界面呈现给用户,即设置可见时执行,进行加载数据的方法
     * 在用户可见时加载数据,而不在用户不可见的时候加载数据,是为了防止控件对象出现空指针异常
     */
    protected void onVisible(){
        setlazyLoad();
    }
    /**
     * 当界面还没呈现给用户,即设置不可见时执行
     */
    protected void onInvisible(){
    }
    /**
     * 加载数据方法,必须由子类实现。
     */
    protected abstract  void setlazyLoad();
}
子类实现
public class OpenResultFragment extends BaseFragment {
    // 标志位,标志已经初始化完成。
    private boolean isPrepared;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(LOG_TAG, "onCreateView");
        View view = inflater.inflate(R.layout.fragment_open_result, container, false);
        
        .........XXX初始化view的各控件

        isPrepared = true;    // 对标识符进行赋值动作
        lazyLoad();
        return view;
    }
    @Override
    protected void lazyLoad() {
        if(!isPrepared || !isVisible) {
            return;
        }
        //填充各控件的数据,请求网络数据 
    }
}

子类中增加了一个标志位isPrepared,用于标志是否初始化完成。然后在我们所需要的初始化操作完成之后调用,如上面的例子当中,在初始化view之后,设置 isPrepared为true,同时调用lazyLoad()方法。而在lazyLoad()当中,判断isPrepared和isVisible只要有一个不为true就不往下执行。也就是仅当初始化完成,并且可见的时候才继续加载,这样的避免了未初始化完成就使用而带来的问题。

Fragment中调用getActivity为null的问题

今天在测试的时候,也发现了一个问题,Fragment基于Activity上,快速切换界面,或者什么情况下,会导致Activity销毁,这种情况下,在Fragment中getActivity获取上下文,这样后果就是应用程序直接崩掉了,并且报空指针。因为,Activity不存在了,获取一个不存在的东西,肯定是报error的。
度娘了一把~~~~

解决方式1

查阅一下Fragment的生命周期:

Fragment 懒加载 与 getActivity()返回null的问题_第1张图片

在onAttach() 与 onDetach()方法之间 getActivity方法是存在,反之为null,因此,如果我正在做某些操作联网,在等待过程中点击Back键返回,使得这个Fragment被销毁了,这时Fragment就会和Activity解除附着(onDetach),当再试图弹出Toast的时候,再次通过getActivity方法获取上下文,就报null了。

解决方式2

创建变量,保存实例。
在Fragment附着在Activity上时用一个变量保存引用,即:

@Override
public void onAttach(Activity activity){
    this.mContext = activity;
}
解决方式3

获取全局上下文,因为全局上下文的是和应用程序的生命周期一致的,不必担心为null
Android程序中Application、Service和Activity都实现了Context,但只有Application才能保证在程序运行期间一直存在并且具有唯一性,因此在程序中可以使用Application来获得Context而不用担心空指针。

你可能感兴趣的:(Fragment 懒加载 与 getActivity()返回null的问题)