Android 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. This is * 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 a runtime exception; it is not * normally expected to happen. */ 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); sClassMap.put(fname, clazz); } Fragment f = (Fragment)clazz.newInstance(); if (args != null) { args.setClassLoader(f.getClass().getClassLoader()); f.mArguments = 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); } }
整个过程中,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); return mf; } @Override 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 mIVechile; public interface IVechile { public void submitCarSuccess(String carId, String plateNo); } @Override public void onAttach(Activity activity) { fueltypes = FuelType.getList(activity); try { mIVechile = (IVechile) activity; } catch (Exception e) { // TODO: handle exception } } }