フラグメントはじめに
昔、私はAndroidの(2012年の冬程度を......)書き始めたとき、あなたは、マイクロチャンネル切り替えタブページと同じ以下を達成したい場合は、あなたが継承TabActivityに必要があり、その後、TabHostを使用し、TabHostに子を追加アクティビティが実装されています
今、私たちすべて知っているが、通常の状況下では、我々は今、我々はSDKのすべてのバージョンで使用することができ、3.0もサポートの断片を提供することができ、当社のサポートv4のパケットに新しく追加されFragmentActivityプラス断片、断片のAndroidを達成するために使用します。フラグメント。フラグメントは、把握に皆のための非常に重要な必要性である活動の一部を約フラグメントのライフサイクルは、当然のことながら、この時間は、我々はこの問題を議論しませんが、ご参考のための画像を提供するということですから、画像XXV /アンドロイド、ライフサイクル
使用に最初から
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.hide(firstStepFragment);
if (secondStepFragment==null){
ft.add(R.id.fl_content, secondStepFragment);
}else {
ft.show(secondStepFragment);
}
ft.commit();
コードから我々一般的に、このような動的な使用断片は、明らかにこの機能を反映することができる場合には、取引の方法によって行われるが、いくつかのケースでは、我々は)(コミットを実行している、などの例外は、あるでしょうStackOverflowの上の誤り、ソリューション非常に単純な、代わりにcommitAllowingStateLoss方法にコミットします。この異常は、それを生成する方法であることを?今日は、ソースから起きてからそれを見て
徐々にソースコードを参照してください
getSupportFragmentManagerメソッドFragmentActivityの開始:
public class FragmentActivity {
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
// ……
public FragmentManager getSupportFragmentManager() {
return mFragments.getSupportFragmentManager();
}
// ……
}
public class FragmentController {
private final FragmentHostCallback<?> mHost;
public FragmentManager getSupportFragmentManager() {
return mHost.getFragmentManagerImpl();
}
}
public abstract class FragmentHostCallback<E> extends FragmentContainer {
FragmentManagerImpl getFragmentManagerImpl() {
return mFragmentManager;
}
}
私たちのFragmentTransactionがFragmentManagerの実装クラスFragmentManagerImplのメソッドから返されたので、私たちはのbeginTransaction方法FragmentManagerImplのソースコードを見てみましょう:
final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {
@Override
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
}
私たちは、リターンがBackStackRecordで、見ることができ、およびすべての呼び出しは、次の最新の例であり、我々は表示されますので、BackStackRecordメソッドが一度だけ実行することができ、または例外がスローされますコミット。今、私たちは私達の注意のBackStackRecord元のいくつかを見てみましょう。
final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, Runnable {
final FragmentManagerImpl mManager;
static final class Op {
Op next;
Op prev;
int cmd;
Fragment fragment;
int enterAnim;
int exitAnim;
int popEnterAnim;
int popExitAnim;
ArrayList<Fragment> removed;
}
public BackStackRecord(FragmentManagerImpl manager) {
mManager = manager;
}
@Override
public FragmentTransaction add(Fragment fragment, String tag) {
doAddOp(0, fragment, tag, OP_ADD);
return this;
}
@Override
public FragmentTransaction add(int containerViewId, Fragment fragment) {
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}
@Override
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
}
@Override
public FragmentTransaction remove(Fragment fragment) {
Op op = new Op();
op.cmd = OP_REMOVE;
op.fragment = fragment;
addOp(op);
return this;
}
@Override
public FragmentTransaction hide(Fragment fragment) {
Op op = new Op();
op.cmd = OP_HIDE;
op.fragment = fragment;
addOp(op);
return this;
}
@Override
public FragmentTransaction show(Fragment fragment) {
Op op = new Op();
op.cmd = OP_SHOW;
op.fragment = fragment;
addOp(op);
return this;
}
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
final Class fragmentClass = fragment.getClass();
final int modifiers = fragmentClass.getModifiers();
if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
|| (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
+ " must be a public static class to be properly recreated from"
+ " instance state.");
}
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 (containerViewId == View.NO_ID) {
throw new IllegalArgumentException("Can't add fragment "
+ fragment + " with tag " + tag + " to container view with no id");
}
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++;
}
@Override
public int commit() {
return commitInternal(false);
}
@Override
public int commitAllowingStateLoss() {
return commitInternal(true);
}
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);
P 大专栏 从源码看commit和commitAllowingStateLoss方法区别rintWriter pw = new PrintWriter(logw);
dump(" ", null, pw, null);
}
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
}
我々は異なるアプローチ、オペアンプでのために、特定のオブジェクトはオペアンプで、我々はこの方法を示し、追加、削除、非表示、データ結果の二重リンクリストを生成し、最終的に実装addOpメソッドを実行するかどうかを、見ることができますこの値が同じではありませんcmdを。一般的なプロセスは、我々は、追加、削除、非表示、表示、および他の方法の後に、いずれかの操作コマンドは、我々はその前身を知ることができ、それは別のコマンドを生成し、その後、これらの操作は、二重リンクリストを形成するコマンド呼び出し、このようなものです後者は、どのような順序です。
ここで最も重要な部分であり、私たちはそこにcommitAllowingStateLossを犯し、我々は2つの方法が最終的にcommitInternal方法が、別の着信パラメータを呼び出すことがわかります、我々はまた、裁判官の最初の文がcommitInternalメソッドを持って見ることができます、トランザクションの実行にコミットした場合、我々は、上記またはcommitAllowingStateLossメソッドがスローされますことをIllegalStateException("commit already called")
、彼らはフラグメントを使用する場合、あなたはのbeginTransactionを生成するための新しいメソッドをコールする必要があるたびに、私たちはしばしば会った理由で例外を、ので、トランザクションは、その後、コミット二度同じトランザクションをコミットすることはできません
それからちょうど唯一の違いをコミットしcommitAllowingStateLoss見て、見下ろすと、あなたがcommitInternalを呼び出すときに、異なる入ってくるパラメータである、とcommitInternal方法では、このパラメータは、このメソッドのコードの第2文の逆数で使用していますmManager.enqueueAction(this, allowStateLoss);
、mManagerです私たちのFragmentManagerImplは、我々はこのクラスのenqueueActionメソッドが何をしたかを参照してください。
final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {
public void enqueueAction(Runnable action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<Runnable>();
}
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
}
}
private void checkStateLoss() {
if (mStateSaved) {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException(
"Can not perform this action inside of " + mNoTransactionsBecause);
}
}
}
場違いエラーメソッドの実装は、着信引数がfalseコミットするので、それは、私たちのcheckStateLoss方法である、それは2つの例外がスローされますcheckStateLoss方法では、checkStateLossを実行します、一つはmStateSavedが真であるため、一方があります2例があった理由でmNoTransactionsBecauseが空ではないので、次の我々の外観
真mStateSavedは何ですか
checkStateLoss方法mStateSaved限り、我々はコミット呼び出しが例外をスローします真であるようなので、問題を見つけることは非常に簡単で、mStateSavedで値がtrueに割り当てされる状況を見てみましょう。FragmentManagerImplソースコードを見ることによって、これらの2つの方法がmStateSavedがtrueに割り当てられ、実行されます。
static final boolean HONEYCOMB = android.os.Build.VERSION.SDK_INT >= 11;
Parcelable saveAllState() {
execPendingActions();
if (HONEYCOMB) {
mStateSaved = true;
}
// 下面的代码省略……
}
public void dispatchStop() {
mStateSaved = true;
moveToState(Fragment.STOPPED, false);
}
これらの2つの方法のFragmentManagerImplを見ることによって、それを実行しますときに、彼らは私たちのFragmentActivityで呼び出される関数のライフサイクルにあります。
public class FragmentActivity{
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
if (mPendingFragmentActivityResults.size() > 0) {
outState.putInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG, mNextCandidateRequestIndex);
int[] requestCodes = new int[mPendingFragmentActivityResults.size()];
String[] fragmentWhos = new String[mPendingFragmentActivityResults.size()];
for (int i = 0; i < mPendingFragmentActivityResults.size(); i++) {
requestCodes[i] = mPendingFragmentActivityResults.keyAt(i);
fragmentWhos[i] = mPendingFragmentActivityResults.valueAt(i);
}
outState.putIntArray(ALLOCATED_REQUEST_INDICIES_TAG, requestCodes);
outState.putStringArray(REQUEST_FRAGMENT_WHO_TAG, fragmentWhos);
}
}
@Override
protected void onStop() {
super.onStop();
mStopped = true;
mHandler.sendEmptyMessage(MSG_REALLY_STOPPED);
mFragments.dispatchStop();
}
}
OnSaveInstanceStateそれが実行された後、メソッドFragmentActivityがある限り、Android3.0は後でmStateSaved福になるように真である、実行のonStop方法は、どのような場合でもmStateSavedをtrueに割り当てられますとき、別のポーズでの見てみよう異常な
ときmNoTransactionsBecause空ではありません
一般的に、我々は常に我々がローダー、mNoTransactionsBecauseを使用する場合にのみ、それはもはや我々が興味を持っている、上記のような微細なように見えた関連を参照することはできませんされている特定のコードが割り当てられることが可能で、mNoTransactionsBecauseがnull値を持ちますソースコードが、私たちはローダーを追跡する必要があるが、何か、よりよく理解するために、私たちが見ることができている、この記事より詳細かつ明確に話して、。
なぜコミット例外がスローされます
私達はちょうどそれをなぜそれが例外をスローしますコミット、スローされた例外とトリガ条件の特定の位置に見えたが、commitAllowingStateLossしていませんか?私たちの活動は、十分なリソースが存在しない場合に破壊されることをすべて知っている、破壊する前に、活動を再作成したonSaveInstanceState、保存フラグメント、景色などを、呼び出して、あなたが状態を取り出す状態を保存することができ、活性をリロードし、 onSaveInstanceStateは、状態ダウンの活動を節約し、この時間は、我々がコミット呼ぶことにしますと、トランザクションは失わないようにFragmentTransaction、アンドロイドを保存されません、私は例外をスロー与える、もちろん、我々はそう、この損失を気にすることはできませんcommitAllowingStateLossは、メソッドを呼び出すことができます。その後、別の珍しい理由は?私は上記の記事を読んだ後、上側のmNoTransactionsBecauseは異なる状態にローダーを表現しながら、あなたは、私たちはいくつかのデータへのローダー非同期アクセス可能にするために知っておくべき、非同期実行で動作している場合、我々は、新しい状態とローダーをコミットAndroidは例外がスローされますときの状態を実行することはそれほど期待できないかもしれませんIllegalStateException("Can not perform this action inside of " + mNoTransactionsBecause)