——欢迎转载,请注明出处 ,未经本人同意请勿用于商业用途,谢谢——
原文链接:https://corner.squareup.com/2014/10/advocating-against-android-fragments.html
本文Gitbooks链接:
最近我在Droidcon Paris举办了一场技术讲座,我讲述了Square公司在使用Android fragments时遇到的问题,以及其他人如何避免使用fragments。
在2011年,基于以下原因我们决定在项目中使用fragments:
自从2011年以来,我们为Square找到了更好的选择。
关于fragments你所不知道的复杂的生命周期
Android中,Context是一个上帝对象(god object),而Activity是具有附加生命周期的context。具有生命周期的上帝对象?有点讽刺的意味。Fragments不是上帝对象,但它们为了弥补这一点,实现了及其复杂的生命周期。
Steve Pomeroy为Fragments复杂的生命周期制作了一张图表看起来并不可爱:
上面Fragments的生命周期使得开发者很难弄清楚在每个回调处要做什么,这些回调是同步的还是异步的?顺序如何?
难以调试
当你的app出现bug,你使用调试器并一步一步执行代码以便了解到底发生了什么,这通常能很好地工作,直到你遇到了FragmentManagerImpl:它是地雷。
下面这段代码很难跟踪和调试,这使得很难正确的修复app中的bug:
switch (f.mState) {case Fragment.INITIALIZING:if (f.mSavedFragmentState != null) {f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(FragmentManagerImpl.VIEW_STATE_TAG);f.mTarget = getFragment(f.mSavedFragmentState,FragmentManagerImpl.TARGET_STATE_TAG);if (f.mTarget != null) {f.mTargetRequestCode = f.mSavedFragmentState.getInt(FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);}f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);if (!f.mUserVisibleHint) {f.mDeferStart = true;if (newState > Fragment.STOPPED) {newState = Fragment.STOPPED;}}}// …}
如果你曾经遇到屏幕旋转时旧的unattached的fragment重新创建,那么你应该知道我在谈论什么(不要让我从嵌套fragments讲起)。
正如Coding Horror所说,根据法律要求我需要附上这个动画的链接。
经过多年深入的分析,我得到的结论是WTFs/min = 2^fragment的个数。
View controllers?没这么快
由于fragments创建,绑定和配置views,它们包含了大量的视图相关的代码。这实际上意味着业务逻辑没有和视图代码解耦-这使得很难针对fragments编写单元测试。
Fragment事务
Fragment事务使得你可以执行一系列fragment操作,不幸的是,提交事务是异步的,而且是附加在主线程handler队列尾部的。当你的app接收到多个点击事件或者配置发生变化时,将处于不可知的状态。
FragmentTransaction {int commitInternal(boolean allowStateLoss) {if (mCommitted)throw new IllegalStateException(“commit already called”);mCommitted = true;if (mAddToBackStack) {mIndex = mManager.allocBackStackIndex(this);} else {mIndex = -1;}mManager.enqueueAction(this, allowStateLoss);return mIndex;}}Fragment创建魔法
Fragment实例可以由你或者fragment manager创建。下面代码似乎很合理:
DialogFragment dialogFragment = new DialogFragment() { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { … }};dialogFragment.show(fragmentManager, tag);
然而,当恢复activity实例的状态时,fragment manager可能会尝试通过反射机制重新创建这个fragment类的实例。由于这是一个匿名内部类,它的构造函数有一个隐藏的参数,持有外部类的引用。
android.support.v4.app.Fragment$InstantiationException:Unable to instantiate fragment com.squareup.MyActivity$1:make sure class name exists, is public, and has an emptyconstructor that is publicFragments的经验教训
尽管存在缺点,fragments教给我们宝贵的教训,让我们在编写app的时候可以重用:
响应式UI:fragments vs 自定义viewsFragments
让我们看一个fragment的简单例子,一个列表和详情UI。
HeadlinesFragment是一个简单的列表:
{ OnHeadlineSelectedListener mCallback; {void onArticleSelected(int position); } (Bundle savedInstanceState) {super.onCreate(savedInstanceState);setListAdapter(new ArrayAdapter<String>(getActivity(),R.layout.fragment_list,Ipsum.Headlines)); } (Activity activity) {super.onAttach(activity);mCallback = (OnHeadlineSelectedListener) activity; } (ListView l, View v, int position, long id) {mCallback.onArticleSelected(position);getListView().setItemChecked(position, true); }}于是夜莺会在黎明到来之前勇敢的将胸膛顶住蔷薇的刺,