Fragment使用过程中遇到的一些问题

先看下 Fragment 的基础用法,这是Fragment动态用法四部曲

1.获得FragmentManager对象,通过getSupportFragmentManager()
2.获得FragmentTransaction对象,通过fm.beginTransaction()
3.调用add()方法或者repalce()方法加载Fragment;
4.最后调用commit()方法提交事务

另外关于Fragment的用法也可以参考一下博文: https://blog.csdn.net/lmj623565791/article/details/42628537

FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.frameLayout,MainFragment.newIntance());
transaction.commit();

Fragment异常一: getActivity()==null

//异常信息:java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
该异常出现的常见场景:比如:我们需要在一个Fragment页面进行异步网络请求,请求完成后我们需要使用 getActivity() 去进行一些操作,在网络请求还未完成时,此时我们跳转到另一个页面,这个时候 getActivity()会返回空,导致空指针异常,下面通过一个例子演示一下该异常
//TODO:演示 getActivity()==null
public class Bug1Activity extends FragmentActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bug1);

        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.add(R.id.frameLayout, Bug1Fragment.newInstance(),Bug1Fragment.class.getName());
        transaction.commit();
    }
}
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler(){
    @Override
    public void handleMessage(@NonNull Message msg) {
      super.handleMessage(msg);
      //模拟网络请求完成后 需要getActivity()的任务
      Toast.makeText(getActivity(),"任务执行了",Toast.LENGTH_SHORT).show();
    }
};

class NetworkTask implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(8500);//模拟网络耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            mHandler.sendEmptyMessage(1);
        }
    }
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    view.findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {//异步任务按钮点击事件
            NetworkTask task = new NetworkTask();
            Thread tread = new Thread(task);
            tread.start();
        }
    });
}

异常复现方法:当点击执行异步任务的按钮后,点击返回键退出该页面,之后会报getActivity()==null 的空指针异常,下面是解决方案

   private Context mActivity;//定义一个Context   
    
   @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        mActivity = getActivity();//在onAttach()方法中赋值
    }

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            //将getActivity()替换为 mActivity
            Toast.makeText(mActivity,"任务执行了",Toast.LENGTH_SHORT).show();
        }
    };
注意:上面的解决方案保证Fragment即使在onDetach后,仍持有Activity的引用,有引起内存泄露的风险,但是异步任务没停止的情况下,本身就可能已内存泄漏,相比Crash,这种做法“安全”些)

Fragment异常二: Can not perform this action after onSaveInstanceState

/**
 * TODO: Can not perform this action after onSaveInstanceState
 * bug复现方法:点击开启一个异步任务按钮后,点击返回键 销毁Activity
 * bug原因:commit()方法一定要在 onSaveInstanceState 调用之前调用
 * 否则就会报上面的错误!!
 
 * 不要在子线程 commit Fragment,防止Activity已经走完 onSaveInstanceState
 * 有时在网络比较好的时候不报这个错误,网络差时才报出来
 */
public class Bug2Activity extends FragmentActivity {
    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction transaction = fragmentManager.beginTransaction();
            transaction.replace(R.id.frameLayout, Bug2Fragment.newIntance(mHandler), Bug2Fragment.class.getName());
            transaction.commit();
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bug2);
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.add(R.id.frameLayout, Bug2Fragment.newIntance(mHandler), Bug2Fragment.class.getName());
        transaction.commit();
    }
}
public class Bug2Fragment extends BaseFragment {
    private Activity mActivity;
    private Handler mHandler;

    public static Fragment newIntance(Handler handler) {
        Bug2Fragment fragment = new Bug2Fragment();
        fragment.setHandler(handler);
        return fragment;
    }

    public void setHandler(Handler handler){
        mHandler = handler;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(){
                    public void run(){
                        while (true){
                            try {
                                Thread.sleep(5000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            mHandler.sendEmptyMessage(0);
                        }
                    }
                }.start();
            }
        });
    }

解决方案:1.不能在onSaveInstanceState之后的生命周期里面调用commit()方法   2.不要在子线程commit fragment  此时Activity已经走完onSaveInstanceState

Fragmet异常三: Fragment重叠异常

出现场景:常出现在旋转屏幕或者内存重启(比如:接了个电话,系统内存不足,杀掉了你的activity,可以打开手机上的“开发者选项”中的“不保留活动”选项,模拟内存重启)

出现原因:旋转屏幕时Activity会重新走onCreate,此时又会add一遍Fragment,同时Fragment本身又有一个自动恢复机制,解决方案如下:

public class MainActivity extends FragmentActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /*
          note: 这里进行判空是为了防止在屏幕旋转时
          出现 Fragment 重叠
          旋转屏幕时虽然走了 onDestroy 但是 Fragment本身有一个
          自动恢复机制,这样旋转屏幕后 onCreate方法里添加的 fragment 和 自动恢复的
          fragment 就发生重叠了,由于自动恢复机制是将 fragment 保存在了 savedInstanceState 中
          所以可以采用以下方法防止重叠
         */
        if(savedInstanceState == null){
            //TODO: 1.获取fragmentManager
            FragmentManager fragmentManager = getSupportFragmentManager();
            //TODO: 2.开启一个fragment事务
            FragmentTransaction transaction = fragmentManager.beginTransaction();
            //TODO: 3.向FrameLayout容器添加MainFragment,现在并未真正执行
            transaction.add(R.id.frameLayout, MainFragment.newIntance(), MainFragment.class.getName());
            //TODO: 4.提交事务,真正去执行添加动作
            transaction.commit();
        }
    }
}
public class MainFragment extends ListFragment {
    public static Fragment newIntance() {
        MainFragment fragment = new MainFragment();
        return fragment;
    }
    ArrayAdapter<String> arrayAdapter;
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        String[] array = new String[]{
                "getActivity==null",
                "Can not perform this action after onSaveInstanceState",
                "Fragment重叠异常",
                "嵌套的fragment不能在onActivityResult()中接收到返回值",
                "未必靠谱的出栈方法remove()",
                "mAvailIndeices的BUG",
                "popBackStack的坑",
                "pop多个Fragment时转场动画 带来的问题",
                "进入新的Fragment并立刻关闭当前Fragment 时的一些问题",
                "Fragment+viewpager",
                "popBackStack的坑",//为了演示 Fragment重叠添加的
                "pop多个Fragment时转场动画 带来的问题",
                "进入新的Fragment并立刻关闭当前Fragment 时的一些问题",
                "Fragment+viewpager"
        };
        arrayAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, array);
        setListAdapter(arrayAdapter);
    }

猜你喜欢

转载自blog.csdn.net/lollo01/article/details/112943194