实例化新Android片段的最佳实践

我已经看到了在应用程序中实例化新片段的两种一般做法:

Fragment newFragment = new MyFragment();

Fragment newFragment = MyFragment.newInstance();

第二个选项使用静态方法newInstance()一般包含以下方法。

public static Fragment newInstance(){MyFragment myFragment = new MyFragment();return myFragment;}

起初,我认为主要的好处是,我可以重载newInstance()方法,以便在创建一个片段的新实例时提供灵活性——但我也可以通过为该片段创建一个重载的构造函数来做到这一点。

我错过什么了吗?

一种方法相对于另一种方法的好处是什么?或者这仅仅是一种好的做法?

385096 次浏览

如果Android决定稍后重新创建你的片段,它将调用你的片段的无参数构造函数。因此重载构造函数不是解决方案。

话虽如此,将内容传递给您的片段以便在Android重新创建片段后它们可用的方法是将捆绑包传递给setArguments方法。

因此,例如,如果我们想将整数传递给片段,我们将使用以下内容:

public static MyFragment newInstance(int someInt) {MyFragment myFragment = new MyFragment();
Bundle args = new Bundle();args.putInt("someInt", someInt);myFragment.setArguments(args);
return myFragment;}

稍后在片段onCreate()中,您可以使用以下命令访问该整数:

getArguments().getInt("someInt", 0);

即使该片段以某种方式由Android重新创建,该Bundle也将可用。

另请注意:setArguments只能在片段附加到活动之前调用。

这种方法也记录在android开发人员参考中:https://developer.android.com/reference/android/app/Fragment.html

使用我看到的newInstance()的唯一好处是:

  1. 您将有一个地方可以捆绑片段使用的所有参数,并且每次实例化片段时都不必编写下面的代码。

    Bundle args = new Bundle();args.putInt("someInt", someInt);args.putString("someString", someString);// Put any other argumentsmyFragment.setArguments(args);
  2. Its a good way to tell other classes what arguments it expects to work faithfully(though you should be able to handle cases if no arguments are bundled in the fragment instance).

So, my take is that using a static newInstance() to instantiate a fragment is a good practice.

还有另一种方式:

Fragment.instantiate(context, MyFragment.class.getName(), myBundle)

不同意和yydi回答说:

如果Android决定稍后重新创建您的Fra的,它会调用片段的无参数构造函数。所以重载构造函数不是解决方案。

我认为这是一个很好的解决方案,这正是它由Java核心语言开发的原因。

Android系统确实可以破坏和重新创建您的Fragment。所以你可以这样做:

public MyFragment() {//  An empty constructor for Android System to use, otherwise exception may occur.}
public MyFragment(int someInt) {Bundle args = new Bundle();args.putInt("someInt", someInt);setArguments(args);}

它将允许您从后者的getArguments()中提取someInt,即使Fragment已被系统重新创建。这比static构造函数更优雅的解决方案。

在我看来,static构造函数是无用的,不应该使用。如果将来您想扩展此Fragment并向构造函数添加更多功能,它们也会限制您。使用static构造函数,您不能这样做。

更新时间:

Android添加了检查,将所有非默认构造函数标记为错误。
由于上述原因,我建议禁用它。

我相信我有一个更简单的解决方案。

public class MyFragment extends Fragment{
private String mTitle;private List<MyObject> mObjects;
public static MyFragment newInstance(String title, List<MyObject> objects)MyFragment myFrag = new MyFragment();myFrag.mTitle = title;myFrag.mObjects = objects;return myFrag;}

由于关于最佳实践的问题,我想补充一点,在使用一些REST Web服务时,使用混合方法创建片段通常是个好主意

对于显示用户片段的情况,我们不能传递复杂的对象,例如一些User模型

但是我们能做的是签入onCreate那个用户!=null,如果不是-然后从数据层把他带来,否则-使用现有的。

通过这种方式,我们获得了在Android重新创建片段的情况下通过userId重新创建片段的能力,以及通过持有对象本身或仅持有其id来创建片段的能力

像这样的东西:

public class UserFragment extends Fragment {public final static String USER_ID="user_id";private User user;private long userId;
@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);userId = getArguments().getLong(USER_ID);if(user==null){//// Recreating here user from user id(i.e requesting from your data model,// which could be services, direct request to rest, or data layer sitting// on application model//user = bringUser();}}
public static UserFragment newInstance(User user, long user_id){UserFragment userFragment = new UserFragment();Bundle args = new Bundle();args.putLong(USER_ID,user_id);if(user!=null){userFragment.user=user;}userFragment.setArguments(args);return userFragment;
}
public static UserFragment newInstance(long user_id){return newInstance(null,user_id);}
public static UserFragment newInstance(User user){return newInstance(user,user.id);}}

虽然@yydl给出了一个令人信服的理由来说明为什么newInstance方法更好:

如果Android决定稍后重新创建您的Fra的,它会调用片段的无参数构造函数。所以重载构造函数不是解决方案。

使用构造函数仍然很有可能。要了解为什么会这样,首先我们需要了解为什么Android使用上述变通方法。

在使用片段之前,需要一个实例。Android调用YourFragment()没有争论构造函数)来构造片段的实例。在这里,你编写的任何重载构造函数都将被忽略,因为Android不知道要使用哪个。

在活动的生命周期中,片段会像上面一样被创建并被Android多次销毁。这意味着如果你把数据放在片段对象本身中,一旦片段被销毁,它就会丢失。

为了解决问题,android要求您使用Bundle(调用setArguments())存储数据,然后可以从YourFragment访问。参数bundle受Android保护,因此保证为持久

设置此bundle的一种方法是使用静态newInstance方法:

public static YourFragment newInstance (int data) {YourFragment yf = new YourFragment()/* See this code gets executed immediately on your object construction */Bundle args = new Bundle();args.putInt("data", data);yf.setArguments(args);return yf;}

但是,构造函数:

public YourFragment(int data) {Bundle args = new Bundle();args.putInt("data", data);setArguments(args);}

可以做与newInstance方法完全相同的事情。

当然,这会失败,这也是Android希望您使用newInstance方法的原因之一:

public YourFragment(int data) {this.data = data; // Don't do this}

作为进一步的解释,这是Android的Frament Class:

/*** Supply the construction arguments for this fragment.  This can only* be called before the fragment has been attached to its activity; that* is, you should call it immediately after constructing the fragment.  The* arguments supplied here will be retained across fragment destroy and* creation.*/public void setArguments(Bundle args) {if (mIndex >= 0) {throw new IllegalStateException("Fragment already active");}mArguments = args;}

请注意,Android要求在构造时将参数设置为只有,并保证这些参数将被保留。

编辑:正如@JHH在评论中指出的,如果你提供的自定义构造函数需要一些参数,那么Java不会为你的片段提供没有参数默认构造函数。因此,这将需要你定义没有参数构造函数,这是你可以使用newInstance工厂方法避免的代码。

编辑:Android不再允许对片段使用重载构造函数。您必须使用newInstance方法。

实例化片段的最佳方法是使用默认的Fragment.instantiate方法或创建工厂方法来实例化片段
注意:总是在片段中创建一个空构造函数,而恢复片段内存将引发运行时异常。

在android中使用参数实例化片段的最佳实践是在片段中使用静态工厂方法。

public static MyFragment newInstance(String name, int age) {Bundle bundle = new Bundle();bundle.putString("name", name);bundle.putInt("age", age);
MyFragment fragment = new MyFragment();fragment.setArguments(bundle);
return fragment;}

你应该避免设置你的字段与一个片段的实例。因为每当android系统重新创建你的片段,如果它觉得系统需要更多的内存,比它将重新创建你的片段使用构造函数没有参数。

你可以在这里找到更多关于使用参数实例化片段的最佳实践的信息。

setArguments()没用,只会带来混乱。

public class MyFragment extends Fragment {
public String mTitle;public String mInitialTitle;
public static MyFragment newInstance(String param1) {MyFragment f = new MyFragment();f.mInitialTitle = param1;f.mTitle = param1;return f;}
@Overridepublic void onSaveInstanceState(Bundle state) {state.putString("mInitialTitle", mInitialTitle);state.putString("mTitle", mTitle);super.onSaveInstanceState(state);}
@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {if (state != null) {mInitialTitle = state.getString("mInitialTitle");mTitle = state.getString("mTitle");}...}}

一些kotlin代码:

companion object {fun newInstance(first: String, second: String) : SampleFragment {return SampleFragment().apply {arguments = Bundle().apply {putString("firstString", first)putString("secondString", second)}}}}

你可以得到这样的论点:

val first: String by lazy { arguments?.getString("firstString") ?: "default"}val second: String by lazy { arguments?.getString("secondString") ?: "default"}

使用此代码100%解决您的问题

第一个片段中输入此代码

public static yourNameParentFragment newInstance() {
Bundle args = new Bundle();args.putBoolean("yourKey",yourValue);YourFragment fragment = new YourFragment();fragment.setArguments(args);return fragment;}

此示例发送布尔数据

加密片段

yourNameParentFragment name =yourNameParentFragment.newInstance();Bundle bundle;bundle=sellDiamondFragments2.getArguments();boolean a= bundle.getBoolean("yourKey");

第一个片段中的值必须是静态的

快乐代码

我最近在这里。但我知道的一些事情可能会对你有所帮助。

如果您使用的是Java,则没有什么可更改的。但对于静态编程语言开发人员,我认为以下一些片段可以为您提供运行的基础:

  • 父片段:
inline fun <reified T : SampleFragment> newInstance(text: String): T {return T::class.java.newInstance().apply {arguments = Bundle().also { it.putString("key_text_arg", text) }}}
  • 正常通话
val f: SampleFragment = SampleFragment.newInstance("ABC")// or val f = SampleFragment.newInstance<SampleFragment>("ABC")
  • 您可以通过以下方式扩展子片段类中的父初始化操作:
fun newInstance(): ChildSampleFragment {val child = UserProfileFragment.newInstance<ChildSampleFragment>("XYZ")// Do anything with the current initialized args bundle here// with child.arguments = ....return child}

编码愉快。

你可以像这样使用smth:

val fragment = supportFragmentManager.fragmentFactory.instantiate(classLoader, YourFragment::class.java.name)

因为这个回答现在已被弃用

  1. 理想情况下,我们不应该在片段构造函数中传递任何东西,片段构造函数应该是空的或默认的。
  2. 现在第二个问题是,如果我们想传递接口变量或参数怎么办-
    1. 我们应该使用Bundle来传递数据。
    2. 对于Interface,我们可以在bundle中putParceble并使该接口实现parceble
    3. 如果可能的话,我们可以在活动和片段中实现该接口,我们可以在OnAttach中初始化侦听器,其中我们有上下文[(上下文)侦听器]。

因此,在配置更改(例如字体更改)期间,活动娱乐侦听器不会转到uninitialize,我们可以避免空指针异常。