ragment切换时,transaction调用commit()
问题
最近遇到个问题,当多个fragment被添加到activity中后,如果在当前fragment中停留过久的话,再去操作fragment之间的切换,可能会造成ANR(PS.这个应用是运行在一种配置很低的设备上)。
思索
当时就想,是不是由于Android系统在资源吃紧的情况下,会自动回收后台的进程,但是,只有一个activity+多个fragment,对所有的生命周期进行了日志观察,并未有任何异常;然后,我又对内存dump文件进行了分析,所有关键的对象都没有被回收;当我对调用的函数进行trace分析后,发现在执行fragmentTransaction.commit()的时候,出现了“卡死”,也可以认为是阻塞了
fragmentTransaction.commit()
既然在这里出现阻塞,那就看看里面到底是什么
/**
* Schedules a commit of this transaction. The commit does
* not happen immediately; it will be scheduled as work on the main thread
* to be done the next time that thread is ready.
*
* <p class="note">A transaction can only be committed with this method
* prior to its containing activity saving its state. If the commit is
* attempted after that point, an exception will be thrown. This is
* because the state after the commit can be lost if the activity needs to
* be restored from its state. See {@link #commitAllowingStateLoss()} for
* situations where it may be okay to lose the commit.</p>
*
* @return Returns the identifier of this transaction's back stack entry,
* if {@link #addToBackStack(String)} had been called. Otherwise, returns
* a negative number.
*/
public abstract int commit();
上面的注释第一段已经说明了原因,大致意思是,在主线程中,commit()并不会立即执行,而是要等到下一次主线程准备好后,才能执行。当然,作为一个工作几年的码农来说,怎么可能就这样浅尝辄止呢,既然都查到这里了,那就看看commit()里面到底是如何实现的。
在BackStackState.java中,找到了commit()的具体实现
@Override
public int commit() {
return commitInternal(false);
}
int commitInternal(boolean allowStateLoss) {
if (mCommitted) throw new IllegalStateException("commit already called");
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Commit: " + this);
LogWriter logw = new LogWriter(TAG);
PrintWriter pw = new PrintWriter(logw);
dump(" ", null, pw, null);
pw.close();
}
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);//调用了Fragment中的方法
return mIndex;
}
关键函数是FragmentManager中的enqueueAction()方法,这个方法是将trasaction.commit()等这样的动作Action加到队列中
/**
* Adds an action to the queue of pending actions.
*
* @param action the action to add
* @param allowStateLoss whether to allow loss of state information
* @throws IllegalStateException if the activity has been destroyed
*/
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
if (allowStateLoss) {
// This FragmentManager isn't attached, so drop the entire transaction.
return;
}
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<>();
}
mPendingActions.add(action);//将commit()这样的一个action添加到延迟动作队列中
scheduleCommit();//提交action,并不会立即执行,需要按照action的先后顺序来执行
}
}
scheduleCommit()后面调用的一系列函数,大致意思就是要在主线程中按照队列的先后来执行action,所以当前需要执行action提交后,如果mPendingActions前面还有其他的action未执行完,那当前的action就会被卡住,最后,可能就出现了ANR。
解决方案
既然设计了队列,也应该可以插队了。于是就找到了commitNow()
/**
* Commits this transaction synchronously. Any added fragments will be
* initialized and brought completely to the lifecycle state of their host
* and any removed fragments will be torn down accordingly before this
* call returns. Committing a transaction in this way allows fragments
* to be added as dedicated, encapsulated components that monitor the
* lifecycle state of their host while providing firmer ordering guarantees
* around when those fragments are fully initialized and ready. Fragments
* that manage views will have those views created and attached.
*
* <p>Calling <code>commitNow</code> is preferable to calling
* {@link #commit()} followed by {@link FragmentManager#executePendingTransactions()}
* as the latter will have the side effect of attempting to commit <em>all</em>
* currently pending transactions whether that is the desired behavior
* or not.</p>
*
* <p>Transactions committed in this way may not be added to the
* FragmentManager's back stack, as doing so would break other expected
* ordering guarantees for other asynchronously committed transactions.
* This method will throw {@link IllegalStateException} if the transaction
* previously requested to be added to the back stack with
* {@link #addToBackStack(String)}.</p>
*
* <p class="note">A transaction can only be committed with this method
* prior to its containing activity saving its state. If the commit is
* attempted after that point, an exception will be thrown. This is
* because the state after the commit can be lost if the activity needs to
* be restored from its state. See {@link #commitAllowingStateLoss()} for
* situations where it may be okay to lose the commit.</p>
*/
public abstract void commitNow();
commitNow字面意思就是立即提交,但是注释中也警告了我们,可能会失去fragment的state,根据我当前的业务来看,不需要保存fragment的state,因此,就可以使用该方法。
作者:何浪
来源:CSDN
原文:https://blog.csdn.net/helang296479893/article/details/90515970
版权声明:本文为博主原创文章,转载请附上博文链接!