Android 片段真的需要一个空的构造函数吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10450348/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Do fragments really need an empty constructor?
提问by Chris.Jenkins
I have a Fragment
with a constructor that takes multiple arguments. My app worked fine during development, but in production my users sometimes see this crash:
我有一个Fragment
带有多个参数的构造函数。我的应用程序在开发过程中运行良好,但在生产中,我的用户有时会看到此崩溃:
android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment
make sure class name exists, is public, and has an empty constructor that is public
I could make an empty constructor as this error message suggests, but that doesn't make sense to me since then I would have to call a separate method to finish setting up the Fragment
.
我可以按照此错误消息的提示创建一个空的构造函数,但这对我来说没有意义,因为从那时起我将不得不调用一个单独的方法来完成Fragment
.
I'm curious as to why this crash only happens occasionally. Maybe I'm using the ViewPager
incorrectly? I instantiate all the Fragment
s myself and save them in a list inside the Activity
. I don't use FragmentManager
transactions, since the ViewPager
examples I have seen did not require it and everything seemed to be working during development.
我很好奇为什么这种崩溃只是偶尔发生。也许我使用ViewPager
不正确?我Fragment
自己实例化所有s 并将它们保存在Activity
. 我不使用FragmentManager
事务,因为ViewPager
我看到的例子不需要它,而且在开发过程中一切似乎都在工作。
回答by Chris.Jenkins
Yes they do.
是的,他们这样做。
You shouldn't really be overriding the constructor anyway. You should have a newInstance()
static method defined and pass any parameters via arguments (bundle)
无论如何,您不应该真正覆盖构造函数。您应该newInstance()
定义一个静态方法并通过参数(包)传递任何参数
For example:
例如:
public static final MyFragment newInstance(int title, String message) {
MyFragment f = new MyFragment();
Bundle bdl = new Bundle(2);
bdl.putInt(EXTRA_TITLE, title);
bdl.putString(EXTRA_MESSAGE, message);
f.setArguments(bdl);
return f;
}
And of course grabbing the args this way:
当然,以这种方式获取 args:
@Override
public void onCreate(Bundle savedInstanceState) {
title = getArguments().getInt(EXTRA_TITLE);
message = getArguments().getString(EXTRA_MESSAGE);
//...
//etc
//...
}
Then you would instantiate from your fragment manager like so:
然后你会像这样从你的片段管理器实例化:
@Override
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState == null){
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content, MyFragment.newInstance(
R.string.alert_title,
"Oh no, an error occurred!")
)
.commit();
}
}
This way if detached and re-attached the object state can be stored through the arguments. Much like bundles attached to Intents.
这样,如果分离并重新附加,则可以通过参数存储对象状态。很像附加到 Intent 的捆绑包。
Reason - Extra reading
原因 - 额外阅读
I thought I would explain why for people wondering why.
我想我会解释为什么人们想知道为什么。
If you check: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/Fragment.java
如果你检查:https: //android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/Fragment.java
You will see the instantiate(..)
method in the Fragment
class calls the newInstance
method:
您将看到instantiate(..)
的方法Fragment
类调用的newInstance
方法:
public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
try {
Class<?> clazz = sClassMap.get(fname);
if (clazz == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = context.getClassLoader().loadClass(fname);
if (!Fragment.class.isAssignableFrom(clazz)) {
throw new InstantiationException("Trying to instantiate a class " + fname
+ " that is not a Fragment", new ClassCastException());
}
sClassMap.put(fname, clazz);
}
Fragment f = (Fragment) clazz.getConstructor().newInstance();
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.setArguments(args);
}
return f;
} catch (ClassNotFoundException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (java.lang.InstantiationException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (IllegalAccessException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (NoSuchMethodException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": could not find Fragment constructor", e);
} catch (InvocationTargetException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": calling Fragment constructor caused an exception", e);
}
}
http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#newInstance()Explains why, upon instantiation it checks that the accessor is public
and that that class loader allows access to it.
http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#newInstance()解释了为什么,在实例化时它会检查访问器是否存在public
以及该类加载器是否允许访问它。
It's a pretty nasty method all in all, but it allows the FragmentManger
to kill and recreate Fragments
with states. (The Android subsystem does similar things with Activities
).
总而言之,这是一种非常讨厌的方法,但它允许使用状态FragmentManger
杀死和重新创建Fragments
。(Android 子系统与 做类似的事情Activities
)。
Example Class
示例类
I get asked a lot about calling newInstance
. Do not confuse this with the class method. This whole class example should show the usage.
我被问到很多关于打电话的问题newInstance
。不要将此与类方法混淆。整个类示例应该显示用法。
/**
* Created by chris on 21/11/2013
*/
public class StationInfoAccessibilityFragment extends BaseFragment implements JourneyProviderListener {
public static final StationInfoAccessibilityFragment newInstance(String crsCode) {
StationInfoAccessibilityFragment fragment = new StationInfoAccessibilityFragment();
final Bundle args = new Bundle(1);
args.putString(EXTRA_CRS_CODE, crsCode);
fragment.setArguments(args);
return fragment;
}
// Views
LinearLayout mLinearLayout;
/**
* Layout Inflater
*/
private LayoutInflater mInflater;
/**
* Station Crs Code
*/
private String mCrsCode;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCrsCode = getArguments().getString(EXTRA_CRS_CODE);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mInflater = inflater;
return inflater.inflate(R.layout.fragment_station_accessibility, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mLinearLayout = (LinearLayout)view.findViewBy(R.id.station_info_accessibility_linear);
//Do stuff
}
@Override
public void onResume() {
super.onResume();
getActivity().getSupportActionBar().setTitle(R.string.station_info_access_mobility_title);
}
// Other methods etc...
}
回答by JesperB
As noted by CommonsWare in this question https://stackoverflow.com/a/16064418/1319061, this error can also occur if you are creating an anonymous subclass of a Fragment, since anonymous classes cannot have constructors.
正如 CommonsWare 在这个问题https://stackoverflow.com/a/16064418/1319061 中所指出的,如果您正在创建 Fragment 的匿名子类,也会发生此错误,因为匿名类不能具有构造函数。
Don't make anonymous subclasses of Fragment :-)
不要创建 Fragment 的匿名子类 :-)
回答by Sveinung Kval Bakken
Yes, as you can see the support-package instantiates the fragments too (when they get destroyed and re-opened). Your Fragment
subclasses need a public empty constructor as this is what's being called by the framework.
是的,正如您所看到的,支持包也会实例化片段(当它们被破坏并重新打开时)。你的Fragment
子类需要一个公共的空构造函数,因为这是框架调用的。
回答by Alecs
Here is my simple solution:
这是我的简单解决方案:
1 - Define your fragment
1 - 定义你的片段
public class MyFragment extends Fragment {
private String parameter;
public MyFragment() {
}
public void setParameter(String parameter) {
this.parameter = parameter;
}
}
2 - Create your new fragment and populate the parameter
2 - 创建新片段并填充参数
myfragment = new MyFragment();
myfragment.setParameter("here the value of my parameter");
3 - Enjoy it!
3 - 享受吧!
Obviously you can change the type and the number of parameters. Quick and easy.
显然,您可以更改参数的类型和数量。快捷方便。