先看下 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);
}