Activity和Fragment通信遇到的坑——为什么要用setArguments方法?
在很早的版本Studio中是可以写如下的代码的
public ParamFragment() {
}
public ParamFragment(int type) {
}
也就意味着你可以通过构造方法传递参数进来,后来studio禁止了这种写法
在Android studio 3.4版本的提示中你可以看到官方推荐你用setArguments来传递参数
因为我之前没有看过setArguments的介绍,认为这个东西没有啥了不起的,还是我行我素用下面的方法通信
public ParamFragment setType(int type) {
....
return this;
}
new ParamFragment().setType(1)
测试的时候也没有发现什么问题,还沾沾自喜觉得这种办法很简单很方便
然后麻烦就出现了
线上日志提示app崩溃,代码定位到存在空指针异常,是参数为空导致的。
可是Fragment创建的时候是传递参数进去的,不可能参数为空的,那么到底是哪里出问题了呢?
于是我开始测试一下Activty和Fragment通信
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("tag", "onCreate: 1");
Fragment fragment = new ParamFragment();
Log.i("tag", "onCreate: 2");
getSupportFragmentManager().beginTransaction().add(R.id.ll_container, fragment).commit();
}
}
public class ParamFragment extends Fragment {
int type = 0;
public ParamFragment() {
Log.i("tag", "Test: fargment constructor");
}
public ParamFragment setType(int type) {
this.type = type;
Log.i("tag", "Test: fargment setType");
return this;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.i("tag", "Test: type is "+type);
TextView textView = new TextView(getActivity());
textView.setText("haha");
return textView;
}
}
运行结果如下
2019-04-28 22:19:32.437 10999-10999/com.justtest I/tag: onCreate: 1
2019-04-28 22:19:32.439 10999-10999/com.justtest I/tag: Test: fargment constructor
2019-04-28 22:19:32.439 10999-10999/com.justtest I/tag: onCreate: 2
2019-04-28 22:19:32.446 10999-10999/com.justtest I/tag: Test: type is 0
这个和预期结果一样,参数为0
修改Activity里面的代码:
Log.i("tag", "onCreate: 1");
Fragment fragment = new ParamFragment().setType(1);
Log.i("tag", "onCreate: 2");
结果如下所示:
2019-04-28 22:21:49.026 11243-11243/com.justtest I/tag: onCreate: 1
2019-04-28 22:21:49.044 11243-11243/com.justtest I/tag: Test: fargment constructor
2019-04-28 22:21:49.044 11243-11243/com.justtest I/tag: Test: fargment setType
2019-04-28 22:21:49.044 11243-11243/com.justtest I/tag: onCreate: 2
2019-04-28 22:21:49.075 11243-11243/com.justtest I/tag: Test: type is 1
好像也没啥特殊的地方哈,这个时候我们旋转屏幕看看会发生什么
2019-04-28 22:21:49.026 11243-11243/com.justtest I/tag: onCreate: 1
2019-04-28 22:21:49.044 11243-11243/com.justtest I/tag: Test: fargment constructor
2019-04-28 22:21:49.044 11243-11243/com.justtest I/tag: Test: fargment setType
2019-04-28 22:21:49.044 11243-11243/com.justtest I/tag: onCreate: 2
2019-04-28 22:21:49.075 11243-11243/com.justtest I/tag: Test: type is 1
2019-04-28 22:23:06.242 11243-11243/com.justtest I/tag: Test: fargment constructor
2019-04-28 22:23:06.270 11243-11243/com.justtest I/tag: onCreate: 1
2019-04-28 22:23:06.270 11243-11243/com.justtest I/tag: Test: fargment constructor
2019-04-28 22:23:06.270 11243-11243/com.justtest I/tag: Test: fargment setType
2019-04-28 22:23:06.270 11243-11243/com.justtest I/tag: onCreate: 2
2019-04-28 22:23:06.274 11243-11243/com.justtest I/tag: Test: type is 0
2019-04-28 22:23:06.276 11243-11243/com.justtest I/tag: Test: type is 1
屏幕旋转会发生呢?Activity的销毁和重建,与此对应的Fragment也是经历了销毁和重建的过程
2019-04-28 22:23:06.242 11243-11243/com.justtest I/tag: Test: fargment constructor
在其执行前没有执行onCreate: 1,之后也没有执行fargment setType,说明这是我们的系统认为帮我们恢复了
fragment,系统做了一件好事——自认为好心的帮我们重建了这个fragment,但是我们可以看到
2019-04-28 22:23:06.274 11243-11243/com.justtest I/tag: Test: type is 0
2019-04-28 22:23:06.276 11243-11243/com.justtest I/tag: Test: type is 1
这里初始化调用了onCreateView两次,一次是我们自己创建的碎片调用的,一次是系统帮我们恢复的碎片调用,这个时候我们就能看到同一个碎片但是值却不一样了,type 一个是1一个是0,系统帮我们恢复的碎片里面的值却没有保存上
看到这里你也就明白了为什么线上会报空指针异常了
系统帮我们恢复的碎片并没有将数据保存下来,导致读取数据的时候造成的空指针异常
接下来我们用setArguments方法试试
public class ParamFragment extends Fragment {
int type = 0;
public ParamFragment() {
Log.i("tag", "Test: fargment constructor");
}
public ParamFragment setType(int type) {
this.type = type;
Log.i("tag", "Test: fargment setType");
return this;
}
public static ParamFragment newInstance(int type) {
ParamFragment fragment = new ParamFragment();
Bundle bundle = new Bundle();
bundle.putInt("type",type);
fragment.setArguments(bundle);
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
getArgumentsData();
Log.i("tag", "Test: type is "+type);
TextView textView = new TextView(getActivity());
textView.setText("haha");
return textView;
}
private void getArgumentsData() {
Bundle bundle = getArguments();
if(bundle!=null){
type = bundle.getInt("type");
}
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("tag", "onCreate: 1");
Fragment fragment = ParamFragment.newInstance(1);
Log.i("tag", "onCreate: 2");
getSupportFragmentManager().beginTransaction().add(R.id.ll_container, fragment).commit();
}
}
这个时候先正常启动再旋转屏幕一下
2019-04-28 22:35:13.036 11882-11882/com.justtest I/tag: onCreate: 1
2019-04-28 22:35:13.042 11882-11882/com.justtest I/tag: Test: fargment constructor
2019-04-28 22:35:13.042 11882-11882/com.justtest I/tag: onCreate: 2
2019-04-28 22:35:13.048 11882-11882/com.justtest I/tag: Test: type is 1
2019-04-28 22:36:06.323 11882-11882/com.justtest I/tag: Test: fargment constructor
2019-04-28 22:36:06.353 11882-11882/com.justtest I/tag: onCreate: 1
2019-04-28 22:36:06.353 11882-11882/com.justtest I/tag: Test: fargment constructor
2019-04-28 22:36:06.353 11882-11882/com.justtest I/tag: onCreate: 2
2019-04-28 22:36:06.365 11882-11882/com.justtest I/tag: Test: type is 1
2019-04-28 22:36:06.367 11882-11882/com.justtest I/tag: Test: type is 1
果然结果是——系统把数据正常保存下来了
具体想知道setArguments做了什么操作,请看下面这篇博客
Android解惑 - 为什么要用Fragment.setArguments(Bundle bundle)来传递参数
平时不注意的细节只有被崩溃教做人,希望大家吸取这次教训,以后注意这个坑。