Fragment补漏

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容