欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android Fragment 和 FragmentManager 的代码分析

程序员文章站 2024-02-21 10:15:34
这两天在研究插件化编程,在使用 fragment 碰到了一些问题,于是查看源码,顺便分析了一下 fragment 和 fragmentmanager 以及其他几个 api...

这两天在研究插件化编程,在使用 fragment 碰到了一些问题,于是查看源码,顺便分析了一下 fragment 和 fragmentmanager 以及其他几个 api 的原代码,看看他们是怎么工作的。

我们知道 fragment 有个 oncreateview() 方法,这个方法在 fragment 创建 view 的时候被调用,并且返回一个 view 对象。那么 oncreateview 在什么时候被调用呢,咱们在 fragment 这个类里找到了一个方法,performcreateview() 方法。

fragment.java
public view oncreateview(layoutinflater inflater, @nullable viewgroup container,
  @nullable bundle savedinstancestate) {
 return null;
}

performcreateview 这个方法在什么时候会被调用呢,在 fragment 里找不到调用它的代码。咱们可以猜测一下,大概会在 fragmentmanager 里。

view performcreateview(layoutinflater inflater, viewgroup container,
  bundle savedinstancestate) {
 if (mchildfragmentmanager != null) {
  mchildfragmentmanager.notestatenotsaved();
 }
 return oncreateview(inflater, container, savedinstancestate);
}

在 fragmentmanager 里,咱们找到了调用 fragment.performcreateview 的代码,在 movetostate() 方法里,这个方法有点大,我只粘贴了部分代码。可以看到,它会在 fragment 初始化或者创建的时候被调用。并且我们知道,创建的view 被赋值给 fragment 的 mview 成员变量了。

fragmentmanager.java
void movetostate(fragment f, int newstate, int transit, int transitionstyle, boolean keepactive) {
 switch (f.mstate) {
 case fragment.initializing:
  if (f.mfromlayout) {
   f.mview = f.performcreateview(f.getlayoutinflater(
   f.msavedfragmentstate), null, f.msavedfragmentstate);
 }
 break;
case fragment.created:
 if (!f.mfromlayout) {
  f.mview = f.performcreateview(f.getlayoutinflater(
   f.msavedfragmentstate), container, f.msavedfragmentstate);
 }
break;
} 
}

接下来,咱们要看什么时候会调用 movetostate() 这个方法。找了一下,发现很 n 多的地方调用了这个方法。这样给咱们逆推找代码造成了一定的难度。于是咱们换个思路,正推来分析。怎么正推了,看咱们怎么使用 fragment 和 fragmentmanager 来分析。

一般咱们都是 getfragmentmanager() 或者 getsupportfragmentmanager() 的方法来获取 fragmentmanager.以 fragmentactivity 为例,一般情况下,咱们在这个类的子类里调用这两个方法之一。

咱们在 fragmentactivity 里找到了相应的代码。fragmentmanager 是一个抽象类,fragmentmanagerimpl 是 fragmentmanager 的子类,在 fragmentmanager 同一个 java 文件内,是一个内部类。它是 fragmentmanager 的实现。

fragmentactivity.java
//fragmentmanagerimpl is subclass of fragmentmanager
final fragmentmanagerimpl mfragments = new fragmentmanagerimpl();
public fragmentmanager getsupportfragmentmanager() {
 return mfragments;
}

获取到 fragmentmanager 后,咱们一般就会调用 begintransaction() 方法,返回一个 fragmenttransaction 。咱们看代码去。

fragmentmanager.java
public abstract fragmenttransaction begintransaction();
fragmentmanagerimpl extends fragmentmanager
@override
public fragmenttransaction begintransaction() {
 return new backstackrecord(this);
}
/**
* static library support version of the framework's {@link android.app.fragmenttransaction}.
* used to write apps that run on platforms prior to android 3.0. when running
* on android 3.0 or above, this implementation is still used; it does not try
* to switch to the framework's implementation. see the framework sdk
* documentation for a class overview.
*/
public abstract class fragmenttransaction

我们发现 fragmentmanager 是一个抽象方法,实现在 fragmentmanagerimpl。fragmentmanagerimpl.begintransaction() 返回的是一个backstackrecord,而 fragmenttransaction 是一个抽象类。那么 backstackrecord 是个什么鬼。

我们找到了 backstackrecord 这个类。我们注意到,它继承于 fragmenttransaction,并且实现了 runable 接口。它的方法有很多,咱们就分析一个咱们比较常用的,比如 add() 方法。

backstackrecord.java
final class backstackrecord extends fragmenttransaction implements fragmentmanager.backstackentry, runnable 
final fragmentmanagerimpl mmanager;
public backstackrecord(fragmentmanagerimpl manager) {
 mmanager = manager;
}

add() 方法其实没干啥,咱们一路追下去看。

public fragmenttransaction add(fragment fragment, string tag) {
 doaddop(0, fragment, tag, op_add);
 return this;
}
private void doaddop(int containerviewid, fragment fragment, string tag, int opcmd) {
 fragment.mfragmentmanager = mmanager;
 if (tag != null) {
  if (fragment.mtag != null && !tag.equals(fragment.mtag)) {
   throw new illegalstateexception("can't change tag of fragment "
     + fragment + ": was " + fragment.mtag
     + " now " + tag);
  }
  fragment.mtag = tag;
 }
 if (containerviewid != 0) {
  if (fragment.mfragmentid != 0 && fragment.mfragmentid != containerviewid) {
   throw new illegalstateexception("can't change container id of fragment "
     + fragment + ": was " + fragment.mfragmentid
     + " now " + containerviewid);
  }
  fragment.mcontainerid = fragment.mfragmentid = containerviewid;
 }
 op op = new op();
 op.cmd = opcmd;
 op.fragment = fragment;
 addop(op);
}
void addop(op op) {
 if (mhead == null) {
  mhead = mtail = op;
 } else {
  op.prev = mtail;
  mtail.next = op;
  mtail = op;
 }
 op.enteranim = menteranim;
 op.exitanim = mexitanim;
 op.popenteranim = mpopenteranim;
 op.popexitanim = mpopexitanim;
 mnumop++;
}

一直追到 addop() 就断了,好像啥事也没干。不过它大概是在一个 add 操作添加到一个链表上了。那咱们怎么办呢?一般咱们add 完后会 commit 一下,咱们看看 commit 都干了啥。

public int commit() {
 return commitinternal(false);
}
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;
}

commit 好像也没干啥特殊的事情,不过可以看到这么一行代码 mmanager.enqueueaction(this, allowstateloss); 看 enqueueaction 这个方法名,应该会做点事情的。

同样,咱们在 fragmentmanagerimpl 里找到了这个方法。

public void enqueueaction(runnable action, boolean allowstateloss) {
  if (!allowstateloss) {
    checkstateloss();
  }
  synchronized (this) {
    if (mdestroyed || mactivity == null) {
      throw new illegalstateexception("activity has been destroyed");
    }
    if (mpendingactions == null) {
      mpendingactions = new arraylist<runnable>();
    }
    mpendingactions.add(action);
    if (mpendingactions.size() == 1) {
      mactivity.mhandler.removecallbacks(mexeccommit);
      mactivity.mhandler.post(mexeccommit);
    }
  }
}

这个方法把咱们的 backstackrecord -- 其实是 fragmenttransaction,也是 runnable -- 添加到一个 mpendingactions 的 arraylist 里了。然后调用 mactivity.mhandler.post(mexeccommit); mexeccommit 又是什么鬼?

runnable mexeccommit = new runnable() {
  @override
  public void run() {
    execpendingactions();
  }
};
mactivity.mhandler.post(mexeccommit); 说明它在主线程里执行了 mexeccommit 的 run 方法。别问我咋知道的。
execpendingactions() 方法稍微比较大,我把注释写在代码里。
public boolean execpendingactions() {
  if (mexecutingactions) {
    throw new illegalstateexception("recursive entry to executependingtransactions");
  }
  //如果不是在主线程,抛出一个异常。
  if (looper.mylooper() != mactivity.mhandler.getlooper()) {
    throw new illegalstateexception("must be called from main thread of process");
  }
  boolean didsomething = false;
  // 这里有一个 while true 循环。
  while (true) {
    int numactions;
    // 这里在一个同步语句块里,把上次 mpendingactions 里的元素转移到 mtmpactions 数组里。并且执行 run方法。执行谁的 run 方法呢?!就是 backstackrecord , 也就是 fragmenttransaction 。我在最后面贴了 backstackrecord 的 run 方法。
    synchronized (this) {
      if (mpendingactions == null || mpendingactions.size() == 0) {
        break;
      }
      numactions = mpendingactions.size();
      if (mtmpactions == null || mtmpactions.length < numactions) {
        mtmpactions = new runnable[numactions];
      }
      mpendingactions.toarray(mtmpactions);
      mpendingactions.clear();
      mactivity.mhandler.removecallbacks(mexeccommit);
    }
    mexecutingactions = true;
    for (int i=0; i<numactions; i++) {
      mtmpactions[i].run();
      mtmpactions[i] = null;
    }
    mexecutingactions = false;
    didsomething = true;
  }
  // 这里有好几行代码,不知道干啥的,反正就是做了一些判断,最后可能会调用 startpendingdeferredfragments() 方法。
  if (mhavependingdeferredstart) {
    boolean loadersrunning = false;
    for (int i=0; i<mactive.size(); i++) {
      fragment f = mactive.get(i);
      if (f != null && f.mloadermanager != null) {
        loadersrunning |= f.mloadermanager.hasrunningloaders();
      }
    }
    if (!loadersrunning) {
      mhavependingdeferredstart = false;
      startpendingdeferredfragments();
    }
  }
  return didsomething;
}

startpendingdeferredfragments 方法又是一坨不知道啥意思的代码。最后可能调用了 performpendingdeferredstart()

void startpendingdeferredfragments() {
  if (mactive == null) return;
  for (int i=0; i<mactive.size(); i++) {
    fragment f = mactive.get(i);
    if (f != null) {
      performpendingdeferredstart(f);
    }
  }
}

在 这个方法里,咱们看到了很熟悉的 movetostate() 方法。接着就是上面的分析,fragment 的 oncreateview 会被调用。

public void performpendingdeferredstart(fragment f) {
  if (f.mdeferstart) {
    if (mexecutingactions) {
      // wait until we're done executing our pending transactions
      mhavependingdeferredstart = true;
      return;
    }
    f.mdeferstart = false;
    movetostate(f, mcurstate, 0, 0, false);
  }
}

咱们在回来看 backstackrecord 的 run 方法。这坨代码有点大,我还是写注释在代码里。

public void run() {
  if (fragmentmanagerimpl.debug) log.v(tag, "run: " + this);
  if (maddtobackstack) {
    if (mindex < 0) {
      throw new illegalstateexception("addtobackstack() called after commit()");
    }
  }
  bumpbackstacknesting(1);
  transitionstate state = null;
  sparsearray<fragment> firstoutfragments = null;
  sparsearray<fragment> lastinfragments = null;
  if (supports_transitions) {
    firstoutfragments = new sparsearray<fragment>();
    lastinfragments = new sparsearray<fragment>();
    calculatefragments(firstoutfragments, lastinfragments);
    state = begintransition(firstoutfragments, lastinfragments, false);
  }
  int transitionstyle = state != null ? 0 : mtransitionstyle;
  int transition = state != null ? 0 : mtransition;
  // 注意这里要开始 while 循环了,要遍历刚才咱们说的链表了。
  op op = mhead;
  while (op != null) {
    int enteranim = state != null ? 0 : op.enteranim;
    int exitanim = state != null ? 0 : op.exitanim;
    switch (op.cmd) {
      // op_add 很简单,mmanager.addfragment(f, false); 其他的几个也类似,调用 mmanager 相应的方法。
      case op_add: {
        fragment f = op.fragment;
        f.mnextanim = enteranim;
        mmanager.addfragment(f, false);
      } break;
      case op_replace: {
        fragment f = op.fragment;
        if (mmanager.madded != null) {
          for (int i=0; i<mmanager.madded.size(); i++) {
            fragment old = mmanager.madded.get(i);
            if (fragmentmanagerimpl.debug) log.v(tag,
                "op_replace: adding=" + f + " old=" + old);
            if (f == null || old.mcontainerid == f.mcontainerid) {
              if (old == f) {
                op.fragment = f = null;
              } else {
                if (op.removed == null) {
                  op.removed = new arraylist<fragment>();
                }
                op.removed.add(old);
                old.mnextanim = exitanim;
                if (maddtobackstack) {
                  old.mbackstacknesting += 1;
                  if (fragmentmanagerimpl.debug) log.v(tag, "bump nesting of "
                      + old + " to " + old.mbackstacknesting);
                }
                mmanager.removefragment(old, transition, transitionstyle);
              }
            }
          }
        }
        if (f != null) {
          f.mnextanim = enteranim;
          mmanager.addfragment(f, false);
        }
      } break;
      case op_remove: {
        fragment f = op.fragment;
        f.mnextanim = exitanim;
        mmanager.removefragment(f, transition, transitionstyle);
      } break;
      case op_hide: {
        fragment f = op.fragment;
        f.mnextanim = exitanim;
        mmanager.hidefragment(f, transition, transitionstyle);
      } break;
      case op_show: {
        fragment f = op.fragment;
        f.mnextanim = enteranim;
        mmanager.showfragment(f, transition, transitionstyle);
      } break;
      case op_detach: {
        fragment f = op.fragment;
        f.mnextanim = exitanim;
        mmanager.detachfragment(f, transition, transitionstyle);
      } break;
      case op_attach: {
        fragment f = op.fragment;
        f.mnextanim = enteranim;
        mmanager.attachfragment(f, transition, transitionstyle);
      } break;
      default: {
        throw new illegalargumentexception("unknown cmd: " + op.cmd);
      }
    }
    op = op.next;
  }
  // 最后还调用了movetostate() 这个方法。跟刚才的区别,看最后一个参数,一个true,一个false。
  // 而且注意,这行代码在 while 循环之后。
  mmanager.movetostate(mmanager.mcurstate, transition, transitionstyle, true);
  if (maddtobackstack) {
    mmanager.addbackstackstate(this);
  }
}

以上所述是小编给大家介绍的android fragment 和 fragmentmanager 的代码分析,希望对大家有所帮助