27
2017
09

Fragment中getActivity()和getContext()为空的问题

问题描述

最近在项目中遇到一个问题,在项目中,Fragment中的一个图标,需要在点击缩放动画完成后,跳转到另一个界面,但是经常会出现getActivity()或getContext()方法返回值为空的问题。在网上查找了很多解决方案,大概有以下几种:
1. 根据fragment的生命周期,将方法的调用写到onStart()方法内部。我将方法的调用写到了onResume()方法中,都同样会出现空指针。
2. 不使用getActivity()和getContext()方法,重写onAttach()方法,自定义一个Context,获取onAttach()时,传入的context。试了这个方法,一样会出现异常。
3. 使用applicationContext(),试了这个方法,同样会出现异常,异常的信息为Fragment not attached to Activity。
4. 还有的说是因为Activity被回收,需要对onSaveBundle()方法做相关处理,但是我是在当前Acitivity刚打开的时候就会出现这个问题,并没有进行切换。

问题求解

使用了第二种方式,自定义了一个Context,接收onAttach()方法中传入的context,然后在onDetach()方法中释放引用 context = null; 同样出现了空指针异常。

@Override
public void onResume() {
    super.onResume();
    // 设置点击事件
    imgType.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(context != null) {      // 判断是否已经添加到了Activity中
                LogZS.d(TAG+"->onClick","Context值为"+context.toString());
                showRoom();             
            }
        }
    });
}

    private void showRoom(){
        // 点击反馈的动画
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(1.0f,1.1f,1.0f);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                imgType.setScaleX(value);
                imgType.setScaleY(value);
            }
        });
        valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                LogZS.d(TAG+"->onAnimationStart","动画开始");
            }
            @Override
            public void onAnimationEnd(Animator animation) {
                    LogZS.d(TAG+"->onAnimationEnd","Context为:"+context.toString());
                    Intent intent = new Intent(context, RoomDetailActivity.class);      // 报错位置
                    intent.putExtra("roomNum", grpNo);
                    startActivity(intent);
            }
            @Override
            public void onAnimationCancel(Animator animation) {

            }
            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        valueAnimator.setDuration(350);
        valueAnimator.start();
    }

通过日志打印看到,在方法调用前,context是不为空的,动画可以执行,日志有输出,但是在动画执行完之后,context为空。
所以我猜测,应该是动画执行的时候触发了Fragment的onDetach()方法释放了context,于是加了打印的日志。

    @Override
    public void onDetach() {
        super.onDetach();
        context = null;
        LogZS.d(TAG+"->onDetach","Context被清空");
    }

日志打印的结果,发现”Context被清空”的信息调用了多次,也就是说onDetach()方法被调用了多次,我是ViewPager中显示的fragment,
所以猜测问题原因可能是每次viewPager刷新数据源adapter.notifyDataSetChange()时,导致的fragment调用了onDetach()方法,
于是在数据源刷新出增加日志输出。

LogZS.e(TAG + "->loadRoom", "LoadRoom被调用");
     adapter.notifyDataSetChanged();

结果”loadRoom被调用”打印的次数和”Context被清空的次数相同”。
所以问题的原因就是数据源刷新的时候,viewPager中的fragment从activity上分离,所以才会出现异常。

解决方案

在知道了问题原因的基础下,使用以上任何一中解决方案都没有问题,前提是,在代码执行之前,要调用isAdd()方法,进行判断,确保fragment
与Activity的状态是关联的状态。

上一篇:美团面试 2017年秋季 下一篇:Android百度地图点聚合开发