美文网首页
Fragment补漏

Fragment补漏

作者: 吃茶泡饭丶 | 来源:发表于2018-12-05 21:01 被阅读0次
    1. extends Activity/FragmentActivity/AppCompatActivity

    Fragment (碎片容器 )在Android 3.0(Level 11)中出现,为了在低版本中使用Fragment需要引入android-support-v4.jar。

    1.1.延伸 support-v4、v7、v13

    android-support-v4 是谷歌推出的兼容包,最低兼容Android1.6的系统。

    android-support-v7是谷歌推出的版本兼容包,最低兼容Android2.1的系统,这个包通常和appcompat-v7这个工程一起使用,appcompat-v7这个工程可以让开发者统一开发,在任何系统版本下保证兼容性。包含了support-v4的全部内容(是appcompat-v7包含的),开发Android工程时,要兼容低版本都要导入v7工程。

    android-support-v13是谷歌推出的版本兼容包,最低兼容Android3.2的系统。当初是为了开发平板做设计的。Android 3.x系统都是平板专用系统,但是3.x系统失败了。所以使用v13的包没有任何价值。

    v7版本适用于任何版本的开发,保证了兼容性,所以在使用的时候一定要采用。

    1.2.v4/v7冲突解决
    a.尽量使用在线依赖库,不要使用jar包可以减少冲突;
    b.使用exclude 关键字将v4包从v7中去除;

    compile('com.android.support:appcompat-v7:23.3.0') {
         exclude module: 'support-v4'
    }
    

    1.3.区别

    Activity:3.0 之前不能直接使用Fragment需要继承FragmentActivity;3.0之后可以直接使用,通过getFragmentManager()获取Manager。

    FragmentActivity:android.support.v4.app包下,继承自SupportActivity,用来解决Fragment版本兼容问题,通过getSupportFragmentManager() 获取Manager。

    AppCompatActivity:android.support.v7.app包下,继承自FragmentActivity,提供了ActionBar、和其他支持。

    2.Fragment 的四种提交方式

    注意:同一个transaction只能commit()提交一次。

    • commit();
      commit()并非一个立即执行的方法,他需要等待线程准备好了再执行(如果需要立即执行则调用executePendingTransactions())。

      为了确保Activity因为各种原因被系统杀死后能正确保存和恢复状态, commit()方法必须要在Activity(Fragment的容器)执行onSaveInstanceState() 方法之前执行;因为onSaveInstanceState() 方法之后再执行 commit 方法的话,Fragment 的状态会丢失,这是很危险的。

      源码:
      AndroidStudio查看子类实现快捷键:Ctrl+Alit+LMB

      public int commit() {
            return this.commitInternal(false);
        }
      

      查看 commitInternal()

      int commitInternal(boolean allowStateLoss) {
            //检查是否已经进行过commit
            if (this.mCommitted) {
                throw new IllegalStateException("commit already called");
            } else {
                if (FragmentManagerImpl.DEBUG) {
                    Log.v("FragmentManager", "Commit: " + this);
                    LogWriter logw = new LogWriter("FragmentManager");
                    PrintWriter pw = new PrintWriter(logw);
                    this.dump("  ", (FileDescriptor)null, pw, (String[])null);
                    pw.close();
                }
      
                this.mCommitted = true;
                //未加入返回栈会返回-1
                if (this.mAddToBackStack) {
                    this.mIndex = this.mManager.allocBackStackIndex(this);
                } else {
                    this.mIndex = -1;
                }
              
                this.mManager.enqueueAction(this, allowStateLoss);
                return this.mIndex;
            }
        }
      

      继续看enqueueAction()

      public void enqueueAction(FragmentManagerImpl.OpGenerator action, boolean allowStateLoss) {
            // 先去检查当前状态
            if (!allowStateLoss) {
                this.checkStateLoss();
            }
      
            synchronized(this) {
                if (!this.mDestroyed && this.mHost != null) {
                    if (this.mPendingActions == null) {
                        this.mPendingActions = new ArrayList();
                    }
                    //将action放入等待序列中,其实就是用一个arrayList把操作存进去,等待执行
                    this.mPendingActions.add(action);
                    //用来规划这个action的执行时间
                    this.scheduleCommit();
                } else if (!allowStateLoss) {
                    throw new IllegalStateException("Activity has been destroyed");
                }
            }
        }
      

      scheduleCommit()实际开了一个子线程来执行等待队列里的操作。
      继续查看checkStateLoss()

      private void checkStateLoss() {
            if (this.isStateSaved()) {
                 //onSaveInstanceState之后不能执行此操作
                throw new IllegalStateException("Can not perform this action after onSaveInstanceState");
            } else if (this.mNoTransactionsBecause != null) {
                throw new IllegalStateException("Can not perform this action inside of " + this.mNoTransactionsBecause);
            }
        }
      

      这里抛出两个异常
      查看isStateSaved()

      public boolean isStateSaved() {
            return this.mStateSaved || this.mStopped;
        }
      

      找到了异常发生的原因

    • commitAllowingStateLoss();
      查看commitAllowingStateLoss()

      public int commitAllowingStateLoss() {
            return this.commitInternal(true);
        }
      

      commitAllowingStateLoss()和 commit() 调用的是同一个方法,只是传入的boolean值不一样;
      从流程看使用commitAllowingStateLoss()确实可以避免发生状态丢失的异常,但是在我们使用的时候,应该尽量少使用这个方法。

    • commitNow();
      commitNow()方法是立即执行,所有被加入的碎片都会被立刻完成生命周期状态,在此之前,任何被移除的碎片都会被相应的撕碎;
      commitNow()方法产生的 Fragment 不能添加到回退栈。和 commit() 方法 一样,会检查 Activity 的状态。

      查看源码:

      public void commitNow() {
          //禁止添加到回退栈
          this.disallowAddToBackStack();
          this.mManager.execSingleAction(this, false);
      }
      

      查看disallowAddToBackStack()

      public FragmentTransaction disallowAddToBackStack() {
          if (this.mAddToBackStack) {
              throw new IllegalStateException("This transaction is already being added to the back stack");
          } else {
              this.mAllowAddToBackStack = false;
              return this;
          }
      }
      

      添加回退栈会抛出异常:
      This transaction is already being added to the back stack(该事务已经 被添加到退回栈)

      查看execSingleAction()

        public void execSingleAction(FragmentManagerImpl.OpGenerator action, boolean allowStateLoss) {
            if (!allowStateLoss || this.mHost != null && !this.mDestroyed) {
                this.ensureExecReady(allowStateLoss);
                if (action.generateOps(this.mTmpRecords, this.mTmpIsPop)) {
                    this.mExecutingActions = true;
      
                    try {
                        this.removeRedundantOperationsAndExecute(this.mTmpRecords, this.mTmpIsPop);
                    } finally {
                        this.cleanupExec();
                    }
                }
      
                this.doPendingDeferredStart();
                this.burpActive();
            }
        }
      

      在主线程开始提交事务
      在这里真正执行操作的是action.generateOps()方法而非try{...}...finally{..}中的方法,因为commitNow直接在主线程提交的事务,是一种线程不安全的操作,并且影响了其他的transaction,所以后面的都是对其进行扫尾和优化的工作。

      源码到此结束 generateOps()内部执行事务操作;

    • commitNowAllowingStateLoss();
      除了不检查 Activity 的状态以外,其他方面和 CommitNow一样
    4.Fragment 的生命周期

    生命周期
    不同加载方式生命周期的差别
    举例FragmentOne 切换到FragmentTwo
    add() / show() / hide()

    • add FragmentOne时的生命周期:onAttach() -- onCreate() -- onActivityCreated() -- onStart() -- onResume()
    • 切换到 FragmentTwo时:onAttach() -- onCreate() -- onHiddenChanged() -- onActivityCreated() -- onStart() -- onResume()
    • 切回到FragmentOne时:FragmentTwo:onHiddenChanged() --
      FragmentOne:onHiddenChanged()

    总结:当以这种方式进行 FragmentOne 与 FragmentTwo 的切换时,Fragment 隐藏的时候并不走结束的生命周期,所有的显示也不会走onCreateView 方法,所有的 view 都会保存在内存。

    测试代码:

    public class MainActivity extends AppCompatActivity {
    
        private Button btnTwo, btnOne;
        private OneFragment oneFragment;
        private TwoFragment twoFragment;
    
        private FragmentManager manager;
        private FragmentTransaction transaction;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            btnOne = findViewById(R.id.activity_main_replace_one);
            btnTwo = findViewById(R.id.activity_main_replace_two);
    
            manager = getSupportFragmentManager();
            transaction = manager.beginTransaction();
    
            oneFragment = new OneFragment();
            transaction.add(R.id.activity_main_contents, oneFragment, "ONE")
                    .commit();
    
            btnOne.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    hideFragment(manager.beginTransaction()).show(oneFragment).commit();
                }
            });
    
            btnTwo.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(twoFragment==null){
                        twoFragment = new TwoFragment();
                        transaction = manager.beginTransaction();
                        transaction.add(R.id.activity_main_contents, twoFragment, "TWO")
                                .hide(oneFragment)
                                .show(twoFragment)
                                .commit();
                    }else{
                        hideFragment(manager.beginTransaction()).show(twoFragment).commit();
                    }
                }
            });
        }
    
        public FragmentTransaction hideFragment(FragmentTransaction transaction) {
            if (oneFragment != null) {
                transaction.hide(oneFragment);
            }
            if (twoFragment != null) {
                transaction.hide(twoFragment);
            }
            return transaction;
        }
    }
    

    replace()

    • replace FragmentOne时的生命周期:onAttach() -- onCreate() -- onActivityCreated() -- onStart() -- onResume()
    • 切换到 FragmentTwo时:FragmentTwo:onAttach() -- FragmentOne:onPause() -- onStop() -- onDestroyView() -- onDestory() -- onDetach() -- FragmentTwo:onActivityCreated() -- onStart() -- onResume()
    • 切回到FragmentOne时:FragmentOne:onAttach() -- FragmentTwo:onPause() -- onStop() -- onDestroyView() -- onDestory() -- onDetach() -- FragmentOne:onActivityCreated() -- onStart() -- onResume()

    总结:通过 replace 方法进行替换的时,Fragment 都是进行了销毁,重建的过程,相当于走了一整套的生命周期。

    测试代码:

    public class MainActivity extends AppCompatActivity {
    
        private Button btnTwo, btnOne;
        private OneFragment oneFragment;
        private TwoFragment twoFragment;
    
        private FragmentManager manager;
        private FragmentTransaction transaction;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            btnOne = findViewById(R.id.activity_main_replace_one);
            btnTwo = findViewById(R.id.activity_main_replace_two);
    
            manager = getSupportFragmentManager();
            initFragmentOne();
    
            btnOne.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    initFragmentOne();
                }
            });
    
            btnTwo.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    initFragmentTwo();
                }
            });
        }
    
        public void initFragmentOne() {
            if (oneFragment == null) {
                oneFragment = new OneFragment();
            }
            transaction = manager.beginTransaction();
            transaction.replace(R.id.activity_main_contents, oneFragment, "ONE")
                    .commit();
        }
    
        public void initFragmentTwo() {
            if (twoFragment == null) {
                twoFragment = new TwoFragment();
            }
            transaction = manager.beginTransaction();
            transaction.replace(R.id.activity_main_contents, twoFragment, "TWO")
                    .commit();
        }
    }
    

    ViewPager

    • 当viewpager滑动到第一页的时候,第一页加载完成,同时第二页也会加载完成。
    • 当viewpager滑动到第二页的时候,第二页获取焦点,第一页失去焦点,第三页加载完成。
    • 当viewpager滑动到第三页的时候,第三页获取焦点,第二页失去焦点,第一页会销毁,但是不解绑。依次类推。
    5.addToBackStack()方法对生命周期的影响

    新替换的Fragment(没有在BackStack中):onAttach > onCreate > onCreateView > onViewCreated > onActivityCreated > onStart > onResume

    新替换的Fragment(已经在BackStack中):onCreateView > onViewCreated > onActivityCreated > onStart > onResume

    被替换的Fragment:onPause > onStop > onDestroyView

    相关文章

      网友评论

          本文标题:Fragment补漏

          本文链接:https://www.haomeiwen.com/subject/bqiwqqtx.html