Fragment为什么须要无参带参数的构造方法法

Android Fragment 使用技巧 - 灰色飘零 - 博客园
随笔 - 114, 文章 - 0, 评论 - 32, 引用 - 0
1. Fragment 使用时要有一个无参构造函数
如果没有无参构造函数,而是像按照普通类来使用,只创建有参构造函数,则会出现&android.support.v4.app.Fragment$InstantiationException 错误。
原因:Fragment 和 Activity 都是生命周期的组件,不能看做一般的类。如果非要使用有参构造函数,可能在使用的时候第一次传参没有问题,但是大概率在后面使用的时候出现问题。因为Fragment的什么周期依附在Activity中,如果Activity为null,那么Fragment肯定不能够正常使用了,比如手机屏幕的横竖屏切换导致Activity重建了。
至于为什么是这样的呢?看下Fragment初始化的源码,有这么一段:
* Create a new instance of a Fragment with the given class name.
* the same as calling its empty constructor.
* @param context The calling context being used to instantiate the fragment.
* This is currently just used to get its ClassLoader.
* @param fname The class name of the fragment to instantiate.
* @param args Bundle of arguments to supply to the fragment, which it
* can retrieve with {@link #getArguments()}.
May be null.
* @return Returns a new fragment instance.
* @throws InstantiationException If there is a failure in instantiating
* the given fragment class.
This is it is not
* normally expected to happen.
public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
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);
sClassMap.put(fname, clazz);
Fragment f = (Fragment)clazz.newInstance();
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.mArguments =
} 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);
整个过程中,Fragment的创建其实也是利用了无参数的构造方法去实例化.但关键的是,它将Bundle传类新建的Fragment,这样旧的Fragment和新的Fragment就能拥有一样的Bundle,从而达到利用Bundle传递参数的目的.&
查看Android的SDK文档,也给出来相关的说法:
2. 给 Fragment 传递参数
一定要使用 Bundle 方式传递参数,而不是通过重载构造函数传递参数。
public static VechileFrag newInstance(Vehicle vehicle, String userId, boolean isAdd) {
VechileFrag mf = new VechileFrag();
Bundle args = new Bundle();
args.putString("userId", userId);
args.putBoolean("isAdd", isAdd);
args.putParcelable("vehicle", vehicle);
mf.setArguments(args);
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if (args != null) {
userId = args.getString("userId");
isAdd = args.getBoolean("isAdd");
vehicle = args.getParcelable("vehicle");
if (vehicle == null) {
vehicle = new Vehicle();
3. Fragment 与 Activity 通信
在 Fragment 中定义一个接口和要回调的方法, Activity实现Fragment接口,需要时回调 Fragment 方法。
public IVechile mIV
  public interface IVechile {
  public void submitCarSuccess(String carId, String plateNo);
  @Override
  public void onAttach(Activity activity) {
    fueltypes = FuelType.getList(activity);
    try {
      mIVechile = (IVechile)
    catch (Exception e) {
    // TODO: handle exceptionAndroid Fragment使用(一) 基础篇 温故知新 - 简书
Android Fragment使用(一) 基础篇 温故知新
Fragment使用的基本知识点总结, 包括Fragment的添加, 参数传递和通信, 生命周期和各种操作.
Fragment使用基础
Fragment添加
方法一: 布局里的标签标识符: tag, id, 如果都没有, container的id将会被使用.
方法二: 动态添加动态添加利用了一个transaction:
FragmentManager fragmentManager = getFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(FragmentB.TAG);
if (null == fragment) {
FragmentB fragmentB = new FragmentB();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragment_container, fragmentB, FragmentB.TAG)
.commit();
commit()方法并不立即执行transaction中包含的动作,而是把它加入到UI线程队列中.如果想要立即执行,可以在commit之后立即调用FragmentManager的方法.
commit()方法必须在状态存储之前调用,否则会抛出异常,如果觉得状态丢失没关系,可以调用commitAllowingStateLoss(). 但是除非万不得已, 一般不推荐用这个方法, 会掩盖很多错误.
Back Stack
Activity的back stack: 系统维护, 每个task一个back stack.Fragment的back stack: 宿主activity掌管, 每个activity一个.
通过调用addToBackStack(),commit()的一系列转换作为一个transaction被存储在back stack中,用户按Back键, 从栈中pop出一个transaction, 逆转操作, 可以返回上一个转换前的状态.
一个transaction可以包含多种操作, 并且不局限于对同一个Fragment, 所以每一个transaction实际上可以是一系列对多个fragment的操作的组合.加入到back stack中去的时候, 是把这一系列的组合作为一个原子, 加入到back stack中.
构造和参数传递
所有的Fragment都必须有一个public的无参构造函数, 因为framework经常会在需要的时候重新创建实例(状态恢复时), 它需要的就是这个构造.如果无参构造没有提供,会有异常.
所以不要给Fragment写有参数的构造函数, 也不要企图搞个什么单例的Fragment. 这些都是反设计的.
参数传递的正确姿势:
public static FragmentWithParameters newInstance(int num) {
FragmentWithParameters fragmentWithParameter = new FragmentWithParameters();
Bundle args = new Bundle();
args.putInt(NUM, num);
fragmentWithParameter.setArguments(args);
return fragmentWithP
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
num = getArguments() != null ? getArguments().getInt(NUM) : 0;
这里是提供了一个静态方法, 也可以new出对象后自己set Bundle参数.
Fragment的通信
除了DialogFragment和嵌套Fragment需要与自己的parent fragment通信以外, 一般的fragment是不与其他fragment有任何通信的. 因为要求应尽量独立, 模块化, 可复用.fragment与自己的parent activity (除了嵌套和dialog的情况外, 这个parent通常是activity) 有直接通信, 一般以这三种方式:
在构造fragment的时候, 通过Bundle传递参数.
parent可以直接调用fragment的public方法, 这里也可以传递一些参数.
Listener, 也即parent实现的callback接口, fragment可以在自己内部调用, 这里fragment也可以传递参数出去.
对于DialogFragment来说, 可以通过一个public的set方法将外面的target设置进去.比如用Fragment的这个方法:
对于嵌套(nested)Fragment, 通信方式与上面普通的fragment类似, 只不过parent此时不是activity而是一个fragment.后面会单独有一个文章说嵌套Fragment的使用, 敬请期待.
Fragment的生命周期
Fragment的生命周期首先和Activity的生命周期密切相关,如果activity stopped,其中所有的fragment都不能如果activity destroyed, 其中所有的fragment都会被destroyed.只有activity在resumed状态下,fragment的生命周期可以独立改变,否则它被activity控制.
Fragment生命周期
Fragment和Activity生命周期
Fragment生命周期
Fragment生命周期
上面这个图来自于:
FragmentTransaction基础操作
中对Fragment有如下几种操作:
attach(), detach()
add(), remove(),
show(), hide(),
除了replace()以外其他都是成对的.
其中attach()和detach()不是很常用.调用detach()之后, fragment实际的生命周期会走到onDestroyView(), 但不会走onDestroy()和onDetach(), 也即fragment本身并没有被销毁, 只是view被销毁了. 这和addToBackStack()的情况一样, 尽管调用detach()的时候没有addToBackStack(), 仍然只是走到view被销毁的阶段.
add()和remove()是将fragment添加和移除.remove()比detach()要彻底一些, 如果不加入到back stack, remove()的时候, fragment的生命周期会一直走到onDetach().
show()和hide()是用来设置fragment的显示和隐藏状态, 这两个方法并不对应fragment的状态变化,只是将view设置为visible和gone,然后调用onHiddenChanged()的回调.
实际上replace() == remove() + add(), 所以它的反操作也是replace(), 只不过把add和remove的东西交换一下.
关于replace()和show(), hide()的选择, 要根据实际使用情形来定.replace()的好处是会减少内存占用, 但是返回时需要重新走完初始化的过程.show()和hide()只是控制了fragment的显示和隐藏, 不会改变生命周期状态, 也即fragment始终是处于running状态的, 被保持在内存中, 适用于频繁切换的情形.
remove(), replace()是否加到back stack对生命周期的影响
前面说过, replace() == remove() + add()新的fragment将取代在容器布局中的fragment, 如果没有,将直接添加新的fragment.
是否添加到back stack对fragment的生命周期是有影响的.remove()或者replace()的时候,如果commit()之前没有调用addToBackStack(),那个旧fragment将会被destroyed和 即完全销毁和移除.
如果调用了addToBackStack(),旧的fragment会处在stopped状态,调用到onDestroyView(), 可以通过返回键来resume.这个时候对于旧的Fragment来说, 成员变量依然在,但是View被销毁了. 所以返回时它的生命周期从onCreateView()开始重建View.
最后, 欢迎订阅公众号: 圣骑士Wind:
圣骑士Wind微信公众号.png
Github: /mengdd
Github pages: http://mengdd.github.io/
博客: /mengdd/
微信公众号: 圣骑士Wind
Fragment要点Fragment作为Activity界面的一部分组成出现可以在一个Activity中同时出现多个Fragment,并且,一个Fragment亦可在多个Activity中使用。在Activity运行过程中,可以添加、移除或者替换Fragment(add()...
Fragment概述 Fragment是Activity中用户界面的一个行为或者说是一部分。主要是支持大屏幕上动态显示和更为灵活的去组合或是交换UI组件,通过将Activity的布局分割成若干个fragment,可以在运行时编辑activity的呈现,并且那些变化会被保存在...
原文地址:https://mp./s/dUuGSVhWinAnN9uMiBaXgw 导语 Fragment作为Android最基本,最重要的基础概念之一,在开发中经常会和他打交道。本文从为什么出现Fragment开始,介绍了Fragment相关的方...
一个Fragment看起来就是一个和Activity一样的用户界面。你可以结合多个Fragments到一个activity中,来构建一个有多方面功能的UI,还可以重用同一个Fragment在多个activities中。你可以把它当成是activity的一个组件,每个Frag...
Fragment的应用真的是越来越广泛了,之前Android在3.0版本加入Fragment的时候,主要是为了解决Android Pad屏幕比较大,空间不能充分利用的问题。但随着界面布局的复杂化,处理起来也更加的复杂,引入Fragment可以把activity拆分成各个部分...
有些人,一辈子也遇不到。 有些人,遇到就是一辈子。
你首先是一个人,然后才会有很多人。 在空旷的中学操场上,面对呼啸而过的晚风,我想起了这句话。跻身在狭隘的空间,我如此渴望能遇到一些志同道合的人。人生中每个阶段遇到的人都不一样,最让我念念不忘的还是在图书馆的那段日子。知道为什么在最有活力的年纪,可以度过一年寂静的时光吗?因为...
1 下班在出租车上和朋友聊天,聊着聊着,朋友突然很深沉的对我说,他的一个发小出车祸了。 我愣了几秒钟。 问朋友,怎么回事? 他说:听旁人说他在过马路的时候,低头看手机,一辆大货车突然开了过来,现在伤的挺严重的..... 第一次听这样的事件在自己身边发生,而且还是同龄人,心底...
来源:律政观察近期周围炒股的朋友纷纷吐槽,一时间国民皆成段子手“我要把我的资金交给国家”、“大家出门都多抬头看看,特别是经过高楼大厦的时候”、“心若在梦就在,大不了重头来”……赶上全民炒股的热潮,身边的朋友常常问律师问律师懂不懂点炒股,你可以说你不知道哪只股票会涨,但一定要...关于带参构造方法,不写无参构造方法为什么没报错?
编译结果是33.请问为什么没出错,不是说写了带参的构造方法就不会提供无参的默认构造方法了吗?
你又没调用无参构造方法,当然不报错你如果这样写 Stu55 s = new Stu55(); 就会报错了
因为你没写无参构造方法
你还没有登录,请先登录或注册慕课网帐号
无参构造,默认带一个,写了带参数构造 默认带的就没有了. 构造方法就是 new Stu55() 这就是用的无参构造,你没用这个,你也用不了了,因为没这个构造函数了,除非你再写一个无参构造你写的new Stu55(11,22) 就是用的带参数构造 ,这个带参数构造你写了,用起来当然不会报错了
你还没有登录,请先登录或注册慕课网帐号
你调用的是有参数的构造函数,当然不会出错啦;当你调用无参构造函数时,就会报错了,因为此时你并没有显示定义无参的构造函数
你还没有登录,请先登录或注册慕课网帐号
73732人关注
Copyright (C)
All Rights Reserved | 京ICP备 号-215:58 提问
给 ViewPager 中的 fragment 添加空的构造函数
我想创建了一个布局,类似 Google play's。我使用 ViewPager ,需要 fragment。我查了一些资料说一个 fragment 需要一个空的构造函数,但是事例中在
不包含一个构造函数。
事例代码如下:
public static class DemoObjectFragment extends Fragment {
public static final String ARG_OBJECT = "object";
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// The last two arguments ensure LayoutParams are inflated
// properly.
View rootView = inflater.inflate(
R.layout.fragment_collection_object, container, false);
Bundle args = getArguments();
((TextView) rootView.findViewById(android.R.id.text1)).setText(
Integer.toString(args.getInt(ARG_OBJECT)));
return rootV
需要在一个fragment中包含一个构造函数吗?或者直接省去构造函数?
按赞数排序
Java 编译器会在没有构造函数的类中自动添加一个默认的无参构造函数(就是问题中提到的 "empty constructor")
下面的空类:
public class A {
等同于下面这个空的无参构造函数:
public class A {
public A() {
你要明确的添加一个无参构造函数当包含另一个只有一个或多个参数的构造函数,因为这样的话,编辑器就不能添加。
默认superClass就有一个无参的构造函数
其他相关推荐日前在项目代码里遇到偷懒使用重写Fragment带参构造方法来传参的做法,顿生好奇,继承android.support.v4.app.Fragment而又不写无参构造方法不是会出现lint错误编译不通过的咩?仔细追究,原来是这货被加了@SuppressLint("ValidFragment")从而屏蔽了错误。(个人非常不建议使用SuppressLint来屏蔽错误,尽管编译阶段通过了,运行时错误却越加恐怖!)
废话不多说,我们回归正题来看为什么Fragment必须要使用无参的构造方法。为什么?因为不这么干的话咱的app就会崩崩崩呗(咖喱给给~)。
首先,让我崩给你看~将app定位至包含此非法(暂且叫它非法)的Fragment的页面,此时将app切至后台,等待app被系统回收,为了加快回收,你可以去玩半个小时比较耗内存的游戏等等,显而易见,这种等待十分愚蠢且不可预知,所以我们可以用借助开发工具立即触发app的被回收,这里我使用Android Studio来做。点击下图中箭头标注的小叉,你的app就被干掉了。(一定要是debug版本的才会在Android Monitor中出现哦)接下来我们从历史任务中打开我们的app,哦哦这下我们终于如愿以偿地看到的崩溃的异常堆栈信息:
Caused by: android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.tuanche.app.fragment.ChooseCarStyleOnlineRetailersFragment: make sure class name exists, is public, and has an empty constructor that is public
at android.support.v4.app.Fragment.instantiate(Fragment.java:413)
at android.support.v4.app.FragmentState.instantiate(Fragment.java:97)
at android.support.v4.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:1801)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:213)
at com.zhqnghq.app.activity.BaseFragmentActivity.onCreate(BaseFragmentActivity.java:36)
at com.zhanghq.app.activity.SecondActivity.onCreate(SecondActivity.java:97)
忽略最后两行,我们从倒数第三行开始看,打开FragmentActivity的onCreate方法,我们可以看到这样的代码:
if(savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable("android:support:fragments");
this.mFragments.restoreAllState(p, nc != null?nc.fragments:null);
因为是被kill后恢复,savedInstanceState不等于空,于是我们进入了if代码块,再往里走看this.mFragments.restoreAllState做了些什么我们关心的,再次挑出关键部分来看:
for(i = 0; i & fms.mActive. ++i) {
FragmentState var8 = fms.mActive[i];
if(var8 != null) {
Fragment var9 = var8.instantiate(this.mActivity, this.mParent);
if(DEBUG) {
Log.v("FragmentManager", "restoreAllState: active #" + i + ": " + var9);
this.mActive.add(var9);
var8.mInstance = null;
this.mActive.add((Object)null);
if(this.mAvailIndices == null) {
this.mAvailIndices = new ArrayList();
if(DEBUG) {
Log.v("FragmentManager", "restoreAllState: avail #" + i);
this.mAvailIndices.add(Integer.valueOf(i));
Fragment var9 = var8.instantiate(this.mActivity, this.mParent);这句代码是我们关注的重点,去看看它做了些什么:
if(this.mInstance != null) {
return this.mI
if(this.mArguments != null) {
this.mArguments.setClassLoader(activity.getClassLoader());
this.mInstance = Fragment.instantiate(activity, this.mClassName, this.mArguments);
if(this.mSavedFragmentState != null) {
this.mSavedFragmentState.setClassLoader(activity.getClassLoader());
this.mInstance.mSavedFragmentState = this.mSavedFragmentS
我滴个神啊,终于看到几行可以稍微看懂的代码了,啦啦啊~Fragment.instantiate()是在从字面意思就可以看出是要生成一个新的Fragment,不信我们可以点进去看:
public static Fragment instantiate(Context context, String fname, Bundle args) {
Class e = (Class)sClassMap.get(fname);
if(e == null) {
e = context.getClassLoader().loadClass(fname);
sClassMap.put(fname, e);
Fragment f = (Fragment)e.newInstance();
if(args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.mArguments =
} catch (ClassNotFoundException var5) {
throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname + ": make sure class name exists, is public, and has an" + " empty constructor that is public", var5);
} catch (java.lang.InstantiationException var6) {
throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname + ": make sure class name exists, is public, and has an" + " empty constructor that is public", var6);
} catch (IllegalAccessException var7) {
throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname + ": make sure class name exists, is public, and has an" + " empty constructor that is public", var7);
忽略冗长的catch代码,可以看到,instantiate方法显示想法设法构造了一个Class对象,然后调用Class::newInstance()方法构造了一个全新的Fragment对象,最后还原了Fragment的Argument,至此Fragment的恢复大功告成。我们回过头看来Class::newInstance()的方法注释(良好的注释很重要啊~):
* Returns a new instance of the class represented by this {@code Class},
* created by invoking the default (that is, zero-argument) constructor. If
* there is no such constructor, or if the creation fails (either because of
* a lack of available memory or because an exception is thrown by the
* constructor), an {@code InstantiationException} is thrown. If the default
* constructor exists but is not accessible from the context where this
* method is invoked, an {@code IllegalAccessException} is thrown.
* @throws IllegalAccessException
* if the default constructor is not visible.
* @throws InstantiationException
* if the instance can not be created.
看完之后豁然开朗啊有木有,原来这货会去调用当前Class的无参构造方法去构造实例,并且还在后面非常牛逼的警告了,如果没有无参构造方法或者构造失败或者这个默认构造方法访问不到,我就生气就抛异常!再回过头去看最开始贴出的异常信息,是不是和这里的异常message一模一样呢,哈哈。
阅读(...) 评论()

我要回帖

更多关于 java带参数的构造方法 的文章

 

随机推荐