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);
    }

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Android Fragment 是一种可以嵌入到 Activity 的可重用组件。当 Activity 运行时,Fragment 可以独立地添加、移除、替换和交互。下面是 Fragment 的执行过程: 1. 创建 Fragment 对象:通过调用 Fragment 的构造函数来创建一个新的 Fragment 对象。 2. 添加 Fragment 到 Activity:使用 FragmentManagerFragment 添加到对应的 Activity。 3. 为 Fragment 创建视图:在 Fragment 的生命周期方法 onCreateView() 创建和返回视图层次结构。 4. 绑定 Fragment 到 Activity:在 Fragment 的生命周期方法 onAttach() Fragment 绑定到它的父 Activity。 5. 创建 Fragment:在 Fragment 的生命周期方法 onCreate() 进行初始化工作,例如恢复保存的状态、初始化变量等。 6. 创建 Activity 视图:在 Activity 的生命周期方法 onCreateView() 创建 Activity 的视图层次结构。 7. 把 Fragment 加入到 Activity 视图:将 Fragment 在 Activity 视图占据的位置预留给它,可以使用 <fragment> 标签或者动态添加。 8. 可见性改变:在 Fragment 的生命周期方法 onStart() Fragment 变为可见状态,可以与用户交互。 9. 焦点获取:在 Fragment 的生命周期方法 onResume() Fragment 获取焦点,可以响应用户输入。 10. 用户交互:Fragment 可以通过用户输入(点击按钮、触摸屏幕等)响应用户交互,处理用户交互事件。 11. 生命周期变化:当 Activity 的生命周期发生变化时,Fragment 的生命周期也会随之变化。 12. 可见性改变:在 Fragment 的生命周期方法 onPause() Fragment 失去焦点,不再与用户交互。 13. 保存状态:在 Fragment 的生命周期方法 onSaveInstanceState() ,保存 Fragment 的状态。 14. 不可见性改变:在 Fragment 的生命周期方法 onStop() Fragment 变为不可见状态。 15. 解绑 Fragment:在 Fragment 的生命周期方法 onDetach() ,将 Fragment 与它的父 Activity 解绑。 16. 销毁 Fragment:在 Fragment 的生命周期方法 onDestroy() ,释放资源,销毁 Fragment 对象。 这是一个典型的 Fragment 生命周期流程,每个生命周期方法都提供了对应的回调函数,我们可以在这些方法执行相应的操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值