ViewPager with Fragments 引起的崩溃和一些问题

参考:

  1. android 中的状态保存与恢复流程
  2. 对Fragment的状态保存恢复机制原理的分析

  3. Fragment 之addToBackStack的理解

 

崩溃1: 

throw new IllegalStateException("Fragment does not have a view");

崩溃分析:

  • 从崩溃栈我们可以发现这个崩溃是在Fragment,initChildFragmentManager 中的回掉中发生的。(mContainer.onFindViewById 有两个实现,一个是在FragmnetActivity 中,这个不会导致这个崩溃。)
  • 崩溃时机:ParentFragment 还没有调用 onCreateView 的时候。

崩溃代码调用位置:(state : fragment crated :)container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);

 
case Fragment.CREATED:
                    // This is outside the if statement below on purpose; we want this to run
                    // even if we do a moveToState from CREATED => *, CREATED => CREATED, and
                    // * => CREATED as part of the case fallthrough above.
                    ensureInflatedFragmentView(f);

                    if (newState > Fragment.CREATED) {
                        if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
                        if (!f.mFromLayout) {
                            ViewGroup container = null;
                            if (f.mContainerId != 0) {
                                if (f.mContainerId == View.NO_ID) {
                                    throwException(new IllegalArgumentException(
                                            "Cannot create fragment "
                                                    + f
                                                    + " for a container view with no id"));
                                }
                                container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
                                if (container == null && !f.mRestored) {
                                    String resName;
                                    try {
                                        resName = f.getResources().getResourceName(f.mContainerId);
                                    } catch (NotFoundException e) {
                                        resName = "unknown";
                                    }
                                    throwException(new IllegalArgumentException(
                                            "No view found for id 0x"
                                            + Integer.toHexString(f.mContainerId) + " ("
                                            + resName
                                            + ") for fragment " + f));
                                }
                            }
                            f.mContainer = container;
                            f.mView = f.performCreateView(f.performGetLayoutInflater(
                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
                            if (f.mView != null) {
                                f.mInnerView = f.mView;
                                f.mView.setSaveFromParentEnabled(false);
                                if (container != null) {
                                    container.addView(f.mView);
                                }
                                if (f.mHidden) {
                                    f.mView.setVisibility(View.GONE);
                                }
                                f.onViewCreated(f.mView, f.mSavedFragmentState);
                                dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
                                        false);
                                // Only animate the view if it is visible. This is done after
                                // dispatchOnFragmentViewCreated in case visibility is changed
                                f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
                                        && f.mContainer != null;
                            } else {
                                f.mInnerView = null;
                            }
                        }

                        f.performActivityCreated(f.mSavedFragmentState);
                        dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
                        if (f.mView != null) {
                            f.restoreViewState(f.mSavedFragmentState);
                        }
                        f.mSavedFragmentState = null;
                    }
                    // fall through

界面构成信息: Activity 中存在Fragment , Fragment 中使用了ViewPager , ViedwPager 中嵌套了ViewPager with fragmnets ;

相关知识:关于Fragment state 的 保存与恢复

  • child fragment 的状态保存时机:
    Fragment.java called in FragmentManager. when performSaveInstanceState;
    void performSaveInstanceState(Bundle outState) {
        onSaveInstanceState(outState);
        if (mChildFragmentManager != null) {
            Parcelable p = mChildFragmentManager.saveAllState();
            if (p != null) {
                outState.putParcelable(FragmentActivity.FRAGMENTS_TAG, p);
            }
        }
    }
  • child fragment 的恢复时机:
  • Fragment.java
    @CallSuper
    public void onCreate(@Nullable Bundle savedInstanceState) {
        mCalled = true;
        restoreChildFragmentState(savedInstanceState);
        if (mChildFragmentManager != null
                && !mChildFragmentManager.isStateAtLeast(Fragment.CREATED)) {
            mChildFragmentManager.dispatchCreate();
        }
    }
  • Fragment 状态恢复的时机:
  • FragmentActivity.onCreate();
    ...
    mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
    ...
    mFragments.dispatchCreate();
  • Fragment onCreate 调用的时机:
  • (mv to sate : )Fragment.INITIALIZING in FragmentManager
  • Fragment.CRREATED: 状态执行,是FragmentManager add fragment 后通过commit 触发, 或者在界面恢复的时候通过mFragmentManager.dispatchCreate();触发。

崩溃原因:

  • Parante Fragment 在界面恢复的时候,也恢复了childFragments。
  • childFragment 在恢复的时候,进入了  fragment crated 状态。崩溃在childFragment 中。
  • Parent Fragment 的initChildFragmentManager在Parent 的onCreateView 之前被调用到。从而导致child fragment 去查找自己的容器添加自己视图的时候,崩溃。

解决方案: (work around)

    在ParentFragment onCreate 的时候, 手动移除所有的child fragment;

 

崩溃场景2: 当为程序设置不保留后台活动的情况下。 重新回到 ViewPager + fragments 的界面中程序崩溃。

崩溃日志:

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object android.util.SparseArray.get(int)' on a null object reference
        at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:902)
        at android.support.v4.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java:216)
        at android.support.v4.view.ViewPager.onRestoreInstanceState(ViewPager.java:1453)
        at android.view.View.dispatchRestoreInstanceState(View.java:15784)
        at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3274)
        at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3282)
        at android.view.View.restoreHierarchyState(View.java:15762)
        at android.support.v4.app.Fragment.restoreViewState(Fragment.java:510)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1445)
        at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1740)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1809)
        at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:799)
        at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2580)
        at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2367)
        at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2322)
        at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2229)
        at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:700)
        at android.os.Handler.handleCallback(Handler.java:761)

界面构成信息: Activity 中存在Fragment , Fragment 中使用了ViewPager , ViedwPager 中嵌套了ViewPager with fragmnets 。而FragmentViewPager 的Adapter 则使用了StateFragmentAdapter。

public Fragment getFragment(Bundle bundle, String key) {
        int index = bundle.getInt(key, -1);
        if (index == -1) {
            return null;
        } else {
            Fragment f = (Fragment)this.mActive.get(index);
            if (f == null) {
                this.throwException(new IllegalStateException("Fragment no longer exists for key " + key + ": index " + index));
            }

            return f;
        }
    }

mActive的恢复代码:在FragmentManager中。对Activity 的FRM 在ActivityOnCreate 的时候就调用恢复了。对于childFragmentManager。在Fragment的onCreate中调用mChildFragmentManager.restoreAllState(p, mChildNonConfig);

void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
        // If there is no saved state at all, then there can not be
        // any nonConfig fragments either, so that is that.
        if (state == null) return;
        FragmentManagerState fms = (FragmentManagerState)state;
        if (fms.mActive == null) return;

        List<FragmentManagerNonConfig> childNonConfigs = null;

        // First re-attach any non-config instances we are retaining back
        // to their saved state, so we don't try to instantiate them again.
       ......

        // Build the full list of active fragments, instantiating them from
        // their saved state.
        mActive = new SparseArray<>(fms.mActive.length);
        for (int i=0; i<fms.mActive.length; i++) {
            FragmentState fs = fms.mActive[i];
            if (fs != null) {
                FragmentManagerNonConfig childNonConfig = null;
                if (childNonConfigs != null && i < childNonConfigs.size()) {
                    childNonConfig = childNonConfigs.get(i);
                }
                Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig);
                if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
                mActive.put(f.mIndex, f);
                // Now that the fragment is instantiated (or came from being
                // retained above), clear mInstance in case we end up re-restoring
                // from this FragmentState again.
                fs.mInstance = null;
            }
        }

        // Update the target of all retained fragments.
       ...

        // Build the list of currently added fragments.
        mAdded.clear();
        if (fms.mAdded != null) {
            for (int i=0; i<fms.mAdded.length; i++) {
                Fragment f = mActive.get(fms.mAdded[i]);
                if (f == null) {
                    throwException(new IllegalStateException(
                            "No instantiated fragment for index #" + fms.mAdded[i]));
                }
                f.mAdded = true;
                if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f);
                if (mAdded.contains(f)) {
                    throw new IllegalStateException("Already added!");
                }
                synchronized (mAdded) {
                    mAdded.add(f);
                }
            }
        }

       ....
        this.mNextFragmentIndex = fms.mNextFragmentIndex;
    }

崩溃分析:由下面两行可以确定是在Fragment ,case Fragment.CREATED, 执行视图恢复的时候崩溃的。

at android.support.v4.app.Fragment.restoreViewState(Fragment.java:510)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1445)

崩溃原因:

  • 界面恢复。恢复保存的Fragment。所有的一级fragemnt在恢复的时候,如果有child fragment ,也会恢复。
  • FragmentMangerImpl.mActive is null ; 在ParentFragment恢复childFragmentManager的时候,其中activie 为空。
  • ParentFragment 已经OnViewCreated了, 并且在开始执行f.restoreViewSate.
  • 使用了FragmentStatePagerAdapter(getSurpportedFragmentManager()) , 在销毁的时候,会保存当前pager fragment 中的状态。
  • 当界面重新创建的时候,会爱OnCreate 的时候,恢复Fragments 。当重新复这个ViewPager 的时候,恢复数据导致崩溃。

崩溃代码位置:

解决办法: 使用FragmentPagerAdapter 替代FragmentStatePagerAdapter。 FragmentPagerAdapter 不会保留状态。但是又引发了刷新问题。

后续问题: 页面不刷新。换成FragmentStatePagerAdapter,是可以刷新的。

问题原因: 历史代码中对adatper 设置新的fragment 数组后,再要求刷新。因为使用的是PagerFragmentAdapter。 在切换页面的时候,使用的是detach方式。而fragmentTag 又和 posotion 相关,导致notify 后,fragment 其实使用的是之前的fragment。(Fragment 复用了) 因此界面不刷新

解决方案:自定义新的Fragment Pager Adapter。 在destroy view 的时候,采用 remove 而不是 detach 方式。当然这样做存在效率问题。

 

2, 知识点: FragmentStatePagerAdapter 与 FragmentPagerAdapter 的区别:

  1.  相同点: 构造函数都需要传入FragmentManager。 都在内部维护了fragments 列表信息。
  2. 不同点:FragmentStatePagerAdapter 会在destroy 的时候saveState 。 执行remove 。而FragmentPagerAdapter 执行detach。

 

1) FragmentStatePagerAdapter : destroy 的时候, 先移除所有的adapter 并且保存状态。执行的是remove    在initiate 的时候,执行的addFragment 操作。

instantiateItem:的时候
 Fragment fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
        if (mSavedState.size() > position) {
            Fragment.SavedState fss = mSavedState.get(position);
            if (fss != null) {
                fragment.setInitialSavedState(fss);
            }
        }
        while (mFragments.size() <= position) {
            mFragments.add(null);
        }
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        mFragments.set(position, fragment);
        mCurTransaction.add(container.getId(), fragment);

 

destroy 的时候:

@Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
                + " v=" + ((Fragment)object).getView());
        while (mSavedState.size() <= position) {
            mSavedState.add(null);
        }
        mSavedState.set(position, fragment.isAdded()
                ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
        mFragments.set(position, null);

        mCurTransaction.remove(fragment);
    }

 

而 FragmentPagerAdapter 执行的只是detach attach 操作。 因此界面是不会刷新的(与Adapter 的具体实现相关)。

 @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
                + " v=" + ((Fragment)object).getView());
        mCurTransaction.detach((Fragment)object);
    }

 

3, commitNowAllowLossState 与 commitAllowLossState区别。

commitNowAllowLossState 如果前面调用了addToBackStack 就会导致崩溃。见源码。

 

4,  onSaveInstanceState (View 中也有这个方法,用于保存视图状态) onRestoreInstanceState.

调用顺序: onSaveInstanceState 未必会调用。 先调用onSaveInstanceState 在onStop 前执行。

如果我们没有覆写onSaveInstanceState()方法, 此方法的默认实现会自动保存activity中的某些状态数据, 比如activity中各种UI控件的状态.。android应用框架中定义的几乎所有UI控件都恰当的实现了onSaveInstanceState()方法,因此当activity被摧毁和重建时, 这些UI控件会自动保存和恢复状态数据. 比如EditText控件会自动保存和恢复输入的数据,而CheckBox控件会自动保存和恢复选中状态.开发者只需要为这些控件指定一个唯一的ID(通过设置android:id属性即可), 剩余的事情就可以自动完成了.如果没有为控件指定ID, 则这个控件就不会进行自动的数据保存和恢复操作。

 

 

5, fragment 恢复的时机: 

当Activity 被以外销毁后,系统会保留当前界面中的Fragment 数据。 在Activity 被系统重新建立的时候, 从新恢复Activity。

  • 恢复Fragment 的时机: FragmentActivity OnCreate 的时候,先调用super 。然后再使用FragmentController 执行Fragment 的恢复工作。会先后恢复active ,added , backStack 中的数据。

6, fragment 的addToBackStack 操作,只添加了transaction 事件到栈中。 在pop 的时候,执行对应的逆操作。因此界面中在执行某些添加与某些不添加到栈中这样的操作的时候,就很可能导致问题。参考文章3;

 

转载于:https://my.oschina.net/u/592116/blog/2967584

### Flink 大数据处理优化技巧与最佳实践 #### 调优原则与方法概述 对于Flink SQL作业中的大状态导致的反压问题,调优的核心在于减少状态大小以及提高状态访问效率。通过合理配置参数和调整逻辑设计可以有效缓解此类瓶颈[^1]。 #### 参数设置建议 针对不同版本下的具体特性差异,在实施任何性能改进措施前应当充分理解当前使用的Flink版本特点及其局限性;同时也要考虑特定应用场景的需求特征来定制化解决方案。这包括但不限于并行度设定、内存分配策略等方面的选择[^2]。 #### 数据流模式优化 采用广播变量机制可作为一种有效的手段用于降低主数据流转过程中所需维护的状态量级。当存在一对多关系的数据集间需频繁交互时,将较小规模的一方作为广播状态保存下来供另一方查询匹配使用不失为明智之举。此方式特别适用于维表Join操作中,其中一方变动相对较少但又必须保持最新记录的情况[^3]。 ```sql -- 创建临时视图以支持后续JOIN操作 CREATE TEMPORARY VIEW dim_table AS SELECT * FROM kafka_source; -- 定义Temporal Table Function以便获取指定时间点上的历史快照 CREATE FUNCTION hist_dim_table AS 'com.example.HistoricalDimTableFunction'; -- 执行带有时态条件约束的JOIN语句 SELECT o.order_id, d.product_name FROM orders o LEFT JOIN LATERAL TABLE(hist_dim_table(o.event_time)) AS d ON o.product_id = d.id; ``` 上述代码片段展示了如何利用Flink SQL实现基于时间戳的历史维度表连接功能,从而确保每次都能准确捕捉到事件发生瞬间对应的最恰当的产品名称信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值