美文网首页
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补漏

    1. extends Activity/FragmentActivity/AppCompatActivity Fr...

  • Stata--多重补漏mipolate

    mipolate多重补漏的命令,可用最邻近补漏,线性补漏、高次项补漏等方式进行补漏。

  • 补漏

    出现问题时,最重要的措施是淡定、积极面对。烦恼了两三天,陈成先电话协调,何不以为然。但陈成的态度非常坚决。对方坚定...

  • 补漏

    家里有个铁饭盆,用了很多年了,一日盛水后,却发现盆里的水没有了,桌子上有很多水,仔细看盆,原来盆底有了一个非...

  • 补漏

    昨天听说物业的会给业主外墙和内墙漏水的地方进行修补,不巧的是我恰要上班。 其实我之前并不知道的,去上班的时候从楼梯...

  • 补漏

    昨天因为我的他回家,我同孩子们一起兴奋起来,以致于忘记日更。 一年两度的欢庆时刻,我们一家同食同宿,过半个月的正常...

  • 补漏

    居然漏了一天 好可怕 2020.3.3 已经三号了啊,外面下起了很大的雪。还是睡到了十点多,做了很累的上班的梦。最...

  • 补漏

    单元楼道台阶,由于施工者偷工减料,在二楼台阶出现了一个窟窿,约有鸡蛋大小。可是上楼下楼的住户们都熟视无睹吧,任凭窟...

  • 补漏

    母亲的存在意义究竟在哪里?——她是每个人人生的补漏者。 昨晚临睡,刘爽说要看会儿书,——在他放学看了两个小时之后。...

  • 补漏

    补漏 最近一段时间,附近装修的人家多了起来,冲击钻、电锤...

网友评论

      本文标题:Fragment补漏

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