Действительно ли фрагменты нуждаются в пустом конструкторе?
у меня есть 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
я мог бы сделать пустой конструктор, как это сообщение об ошибке предлагает, но это не имеет смысла для меня с тех пор я должен был бы вызвать отдельный метод, чтобы закончить настройку Fragment.
мне любопытно, почему эта авария происходит только иногда. Может быть, я использую ViewPager неправильно? Я создаю все экземпляры Fragments себя и сохранить их в списке внутри Activity. Я не использую FragmentManager сделки, поскольку ViewPager примеры, которые я видел, не требовали этого, и все, казалось, работало во время разработки.
4 ответов:
Да, они делают.
вы не должны действительно переопределять конструктор в любом случае. Вы должны иметь
newInstance()статический метод определен и передает любые параметры через аргументы (bundle)например:
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; }и, конечно, захватывая args таким образом:
@Override public void onCreate(Bundle savedInstanceState) { title = getArguments().getInt(EXTRA_TITLE); message = getArguments().getString(EXTRA_MESSAGE); //... //etc //... }затем вы создадите экземпляр из своего менеджера фрагментов следующим образом:
@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(); } }таким образом, если отсоединить и повторно присоединить состояние объекта можно сохранить через аргумент. Очень похоже на связки, прикрепленные к намерениям.
причина - внеклассное чтение
я думал, что объясню, почему для людей интересно, почему.
если вы проверяете: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/Fragment.java
вы увидите
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() объясняет, почему при создании экземпляра он проверяет, что метод доступа
publicи что этот загрузчик класса позволяет получить к нему доступ.это довольно неприятный метод в целом, но он позволяет
FragmentMangerубить и пересоздатьFragmentsС государствами. (Подсистема Android делает подобные вещи сActivities).пример Класс
меня спрашивают много о вызове
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... }
Как отмечает CommonsWare в этом вопросе https://stackoverflow.com/a/16064418/1319061, Эта ошибка также может возникнуть при создании анонимного подкласса фрагмента, так как анонимные классы не могут иметь конструкторов.
Не делайте анонимные подклассы фрагмент :-)
да, как вы можете видеть, пакет поддержки также создает экземпляры фрагментов (когда они уничтожаются и снова открываются). Ваши подклассы Fragemnt нуждаются в публичном пустом конструкторе, поскольку это то, что вызывается платформой.
вот мое простое решение:
1-Определите свой фрагмент
public class MyFragment extends Fragment { private String parameter; public MyFragment() { } public void setParameter(String parameter) { this.parameter = parameter; } }2-Создайте новый фрагмент и заполните параметр
myfragment = new MyFragment(); myfragment.setParameter("here the value of my parameter");3 - Наслаждайтесь!
очевидно, что вы можете изменить тип и количество параметров. Быстро и легко.
Comments