Fragment事務流程分析

Fragment事務流程分析

簡言

簡單的事務使用流程代碼

getSupportFragmentManager().beginTransaction().add(R.id.fl, ftMain).commit();

使用的方法很簡單趣竣,但是Activity是如何實現(xiàn)事務的管理的呢终娃?

我們先上一個簡單的類圖
image-20200404103145645

這個里面FragementManagerImpl持有mHost绿鸣,一開始不知道是如何設置的,后來才發(fā)現(xiàn)是通過attachHost方法進行設置的躏吊。

我們一步步去分析這個UML的視圖是如何建立的

getSupportFragmentManager() 來看一下是如何獲取的

getSupportFragmentManager()

// FragmentActivity.java
  final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
   public FragmentManager getSupportFragmentManager() {
        return mFragments.getSupportFragmentManager();
    }
//FragmentController.java
    //實際是HostCallbacks對象宴凉。
    private final FragmentHostCallback<?> mHost;

    public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
        return new FragmentController(callbacks);
    }

    private FragmentController(FragmentHostCallback<?> callbacks) {
        mHost = callbacks;
    }

    public FragmentManager getSupportFragmentManager() {
        return mHost.getFragmentManagerImpl();
    }

這里可以看到我們的Activity持有FragmentControl對象。而FragmentControl持有了HostCallbacks對象恬口,而HostCallbacks是繼承自FragmentHostCallback的校读。

    //FragmentHostCallback.java
    final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
    FragmentManagerImpl getFragmentManagerImpl() {
        return mFragmentManager;
    }

可以看到這里HostCallback是持有FragmenManagerImpl的。而FragmenManagerImpl繼承FragmentManager

所以其實我們的第一步驟getSupportFragmentManager()是獲取到了一個FragmenManagerImpl對象祖能。

beginTransaction

//FragmentManager.java
public FragmentTransaction beginTransaction() {
    return new BackStackRecord(this);
}

可以看到這里創(chuàng)建了一個BackStackRecord對象歉秫,對象持有了FragmentManager。這里的BackStackRecord是繼承自FragmentTransaction類的养铸。

到這里我們是獲取獲取到了一個事務記錄類雁芙。也就是BackStackRecord

add

當有了事務 記錄類之后,就可以進行各種事務的記錄信息钞螟,add,remove,replace等等兔甘。我們這里只是跟蹤我們的測試代碼,其他的是有相同的處理機制筛圆。

//BackStackRecord.java
public FragmentTransaction add(int containerViewId, Fragment fragment) {
    doAddOp(containerViewId, fragment, null, OP_ADD);
    return this;
}

public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
    doAddOp(containerViewId, fragment, tag, OP_ADD);
    return this;
}

private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
    fragment.mFragmentManager = mManager;
    //一系列檢測
    ...
    //將當前的操作添加到隊列中
    addOp(new Op(opcmd, fragment));
}
//增加到處理隊列中
void addOp(Op op) {
    mOps.add(op);
    //動畫的處理
    op.enterAnim = mEnterAnim;
    op.exitAnim = mExitAnim;
    op.popEnterAnim = mPopEnterAnim;
    op.popExitAnim = mPopExitAnim;
}

可以看到裂明,其實BackStackRecord內部維護了一個要執(zhí)行的隊列,當進行事務的提交時太援,肯定是需要挨個將這個隊列進行去除執(zhí)行的闽晦。

commit

現(xiàn)在我們已經準備好了一個隊列了,當所有的事務內部要執(zhí)行的操作處理完后提岔,回通過commit()或者commitNowAllowingStateLoss(),commitNow()來進行提交仙蛉。我們這里跟蹤commit()的執(zhí)行,剩下的留以后再慢慢分析碱蒙。

//BackStackRecord.java
    public int commit() {
        return commitInternal(false);
    }
    int commitInternal(boolean allowStateLoss) {
        //防止重復提交
        if (mCommitted) {
            throw new IllegalStateException("commit already called");
        }
        ...
        mCommitted = true;
        if (mAddToBackStack) {//如果執(zhí)行了addToBackStack,mAddToBackStack才會為true
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        //調用manager的入隊操作
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }

commit()主要執(zhí)行了入隊的操作荠瘪。

#FragmentManager.java
    public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            checkStateLoss();
        }
        //加鎖
        synchronized (this) {
            ...
            mPendingActions.add(action);
            scheduleCommit();
        }
    }
    private void scheduleCommit() {
        synchronized (this) {
            ...
            //這里的mHost是Fragment中的HostCallBack,這里的getHandler獲取到的是主線程Handler
            mHost.getHandler().removeCallbacks(mExecCommit);
            mHost.getHandler().post(mExecCommit);
        }
    }
    
    Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions();
        }
    };

所以這里的是將要執(zhí)行的BackStackRecord的記錄信息存放到了mPendingActions隊列之中,然后通過主線程的Looper機制來進行調用執(zhí)行execPendingActions()方法赛惩。

execPendingActions

#FragmentManager.java
//只能從主線程調用
public boolean execPendingActions() {
    //進行一些線程的檢測哀墓,是否銷毀等的檢測操作
    ensureExecReady(true);
    boolean didSomething = false;
    //generateOpsForPendingActions會將我們的PendingActions中的數(shù)據(jù)取出來,然后存放到臨時的mTmpRecords中
    while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
        mExecutingActions = true;
        try {
            removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
        } finally {
            cleanupExec();
        }
        didSomething = true;
    }

    doPendingDeferredStart();
    burpActive();

    return didSomething;
}

我們看一下generateOpsForPendingActions是如何將數(shù)據(jù)獲取并存放到臨時list中的

#FragmentManager.java
    //遍歷循環(huán)喷兼,將我們的PendingActions中的數(shù)據(jù)取出來篮绰,然后存放到臨時的mTmpRecords隊列中
    private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,ArrayList<Boolean> isPop) {
    boolean didSomething = false;
    synchronized (this) {
        final int numActions = mPendingActions.size();
        for (int i = 0; i < numActions; i++) {
            didSomething |= mPendingActions.get(i).generateOps(records, isPop);
        }
        mPendingActions.clear();
        //當隊列中的數(shù)據(jù)已經保存到臨時的records中以后,移除回調
        mHost.getHandler().removeCallbacks(mExecCommit);
    }
    return didSomething;
}

所以是通過循環(huán)季惯,調用了generateOps方法

//BackStackRecord.java
    public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
        if (FragmentManagerImpl.DEBUG) {
            Log.v(TAG, "Run: " + this);
        }
        //直接添加到list中
        records.add(this);
        //記錄當前是非pop操作
        isRecordPop.add(false);
        if (mAddToBackStack) {//記錄是否是入棧操作
            mManager.addBackStackState(this);
        }
        return true;
    }

其實這里面的存放到臨時list中的操作相對來說比較簡單吠各。我們回到主線去看看將數(shù)據(jù)存放到臨時list中以后臀突,又對這些事務做了什么操作。也就是removeRedundantOperationsAndExecute()函數(shù)的作用贾漏。

removeRedundantOperationsAndExecute

//FragmentManager.java
//這里進行了事務的優(yōu)化操作候学,事務的最終執(zhí)行也由executeOpsTogether來執(zhí)行的
    private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {

        //完成以前被推遲但現(xiàn)在已經準備好的事務的執(zhí)行。
        executePostponedTransaction(records, isRecordPop);
        final int numRecords = records.size();
        int startIndex = 0;
        for (int recordNum = 0; recordNum < numRecords; recordNum++) {
            //是否允許重新排序
            final boolean canReorder = records.get(recordNum).mReorderingAllowed;
            if (!canReorder) {//如果不允許重排序纵散,那么會獲取這個位置之后的連續(xù)不允許排序的隊列然后批量執(zhí)行
                //執(zhí)行之前的所有操作
                if (startIndex != recordNum) {
                    //如果不允許重排序梳码,那么就將之前的所有的操作批量執(zhí)行
                    executeOpsTogether(records, isRecordPop, startIndex, recordNum);
                }
                //reorderingEnd會從當前不能重排序的位置開始,遍歷搜索困食,尋找到連續(xù)的不能重排序的pop操作的位置
                //然后將recordNum到reorderingEnd的位置進行批量執(zhí)行
                int reorderingEnd = recordNum + 1;
                if (isRecordPop.get(recordNum)) {
                    //當前是個pop操作边翁,那么會一直搜索到下一個非pop的位置,然后記錄硕盹。然后會將這些所有的pop操作批量執(zhí)行
                    while (reorderingEnd < numRecords && isRecordPop.get(reorderingEnd) && !records.get(reorderingEnd).mReorderingAllowed) {
                        //是pop操作符匾,不允許排序
                        reorderingEnd++;
                    }
                }
                executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);
                //設置下一個批量執(zhí)行的起始位置
                startIndex = reorderingEnd;
                recordNum = reorderingEnd - 1;
            }
        }
        if (startIndex != numRecords) {
            executeOpsTogether(records, isRecordPop, startIndex, numRecords);
        }
    }

這里,會將我們的隊列進行切割拆分瘩例,將連續(xù)的允許重新排序和不允許重新排序的分別切割出來啊胶,然后通過executeOpsTogether對隊列進行一個批量的操作。我們看看如何批量處理的垛贤。

//執(zhí)行BackStackRecords列表的一個子集集合焰坪,所有這些子集合要么都允許重新排序,要么都不允許排序聘惦。
private void executeOpsTogether(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
    //獲取當前批次是否允許重排序
    final boolean allowReordering = records.get(startIndex).mReorderingAllowed;
    boolean addToBackStack = false;
    if (mTmpAddedFragments == null) {
        mTmpAddedFragments = new ArrayList<>();
    } else {
        mTmpAddedFragments.clear();
    }
    //保存已經添加的fragment臨時快照
    mTmpAddedFragments.addAll(mAdded);
    //獲取當前棧頂?shù)膄ragment
    Fragment oldPrimaryNav = getPrimaryNavigationFragment();
    for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
        //獲取事務
        final BackStackRecord record = records.get(recordNum);
        //判斷事務是否為pop
        final boolean isPop = isRecordPop.get(recordNum);
        if (!isPop) {//重點方法   不是pop某饰,那么就對事務中的操作進行優(yōu)化
            oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
        } else {//如果是pop操作,那么就執(zhí)行反向操作善绎。也就是add的反向操作和remove的反向操作
            record.trackAddedFragmentsInPop(mTmpAddedFragments);
        }
        addToBackStack = addToBackStack || record.mAddToBackStack;
    }
    //這里直接clear了是什么鬼黔漂?難道只是為了進行優(yōu)化用的臨時表?禀酱?
    mTmpAddedFragments.clear();
    if (!allowReordering) {//不允許排序
        //開始轉換
        FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex,
                false);
    }
    //重點方法  炬守,最重要的地方。是commit操作的最終執(zhí)行的地方
    executeOps(records, isRecordPop, startIndex, endIndex);
    int postponeIndex = endIndex;
    if (allowReordering) {
        ArraySet<Fragment> addedFragments = new ArraySet<>();
        addAddedFragments(addedFragments);
        postponeIndex = postponePostponableTransactions(records, isRecordPop,startIndex, endIndex, addedFragments);
        makeRemovedFragmentsInvisible(addedFragments);
    }

    if (postponeIndex != startIndex && allowReordering) {
        // need to run something now
        FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
                postponeIndex, true);
        moveToState(mCurState, true);
    }

    for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
        final BackStackRecord record = records.get(recordNum);
        final boolean isPop = isRecordPop.get(recordNum);
        if (isPop && record.mIndex >= 0) {
            freeBackStackIndex(record.mIndex);
            record.mIndex = -1;
        }
        record.runOnCommitRunnables();
    }

    if (addToBackStack) {
        reportBackStackChanged();
    }
}

這個方法里面有兩個需要關注的重點剂跟。

  1. expandOps()函數(shù)减途,對執(zhí)行的事務會進行一個優(yōu)化。
  2. executeOps()函數(shù)曹洽,用來執(zhí)行最終的commit操作鳍置,將fragment和Activity進行關聯(lián)綁定以及生命周期的同步。

這里我們可以簡單看一下expandOps()函數(shù)中優(yōu)化的邏輯送淆。

//BackStackRecords.java
//對事務中的操作進行優(yōu)化處理
Fragment expandOps(ArrayList<Fragment> added, Fragment oldPrimaryNav) {
    for (int opNum = 0; opNum < mOps.size(); opNum++) {
        final Op op = mOps.get(opNum);
        switch (op.cmd) {
            case OP_ADD:
            case OP_ATTACH://如果是add或者attach墓捻,將當前fragment放入到added中
                added.add(op.fragment);
                break;
            case OP_REMOVE:
            case OP_DETACH: {//如果是移除操作,那么從added隊列中移除fragment
                added.remove(op.fragment);
                if (op.fragment == oldPrimaryNav) {//如果移除的fragment是當前的oldPrimaryNav,需要做一個特殊處理砖第,這里有點不太明白,以后再研究
                    mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, op.fragment));
                    opNum++;
                    oldPrimaryNav = null;
                }
            }
            break;
            case OP_REPLACE: {//如果是replace操作环凿,那么就將對應的mContainerId中的fragment移除梧兼,然后再添加當前的fragment
                final Fragment f = op.fragment;
                final int containerId = f.mContainerId;//獲取到當前fragment占用的containerId信息
                boolean alreadyAdded = false;
                for (int i = added.size() - 1; i >= 0; i--) {
                    final Fragment old = added.get(i);
                    if (old.mContainerId == containerId) {//如果已經添加的fragment的containerId和當前containerId是相同的,那么說明其占用的是同一個containerId智听,需要將added里面的移除
                        if (old == f) {//如果這個fragment已經在added列表中羽杰,存在,設置一個標記位到推。
                            alreadyAdded = true;
                        } else {//將所有其他使用這個containerId的fragment都執(zhí)行一個remove操作考赛。這里是通過向mOps中增加新的操作來進行處理的,而不是直接移除莉测。是因為在遍歷列表的時候颜骤,不能執(zhí)行移除操作
                            // This is duplicated from above since we only make
                            // a single pass for expanding ops. Unset any outgoing primary nav.
                            if (old == oldPrimaryNav) {
                                mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, old));
                                opNum++;
                                oldPrimaryNav = null;
                            }
                            final Op removeOp = new Op(OP_REMOVE, old);
                            removeOp.enterAnim = op.enterAnim;
                            removeOp.popEnterAnim = op.popEnterAnim;
                            removeOp.exitAnim = op.exitAnim;
                            removeOp.popExitAnim = op.popExitAnim;
                            mOps.add(opNum, removeOp);
                            added.remove(old);
                            opNum++;
                        }
                    }
                }
                if (alreadyAdded) {//表明這個opNum位置的fragment已經在add列表中存在了,那么其實不需要再執(zhí)行這個replace命令了捣卤,直接將這個replace操作移除掉忍抽,這樣可以達到優(yōu)化效果
                    mOps.remove(opNum);
                    opNum--;
                } else {//如果不存在,證明這個fragment之前是不存在的董朝,需要對它執(zhí)行變?yōu)橐粋€add操作
                    op.cmd = OP_ADD;
                    added.add(f);
                }
            }
            break;
            case OP_SET_PRIMARY_NAV: {
                // It's ok if this is null, that means we will restore to no active
                // primary navigation fragment on a pop.
                mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, oldPrimaryNav));
                opNum++;
                // Will be set by the OP_SET_PRIMARY_NAV we inserted before when run
                oldPrimaryNav = op.fragment;
            }
            break;
        }
    }
    return oldPrimaryNav;
}

這里注釋很詳細鸠项,那么我們回到主線,看一下executeOps()函數(shù)的執(zhí)行子姜。

 //FragmentManager.java
 //執(zhí)行操作
private static void executeOps(ArrayList<BackStackRecord> records,ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
    for (int i = startIndex; i < endIndex; i++) {
        final BackStackRecord record = records.get(i);
        final boolean isPop = isRecordPop.get(i);
        if (isPop) {//如果是pop操作
            //所有的管理的fragment后面的堆棧計數(shù)器-1
            record.bumpBackStackNesting(-1);
            // Only execute the add operations at the end of
            // all transactions.
            boolean moveToState = i == (endIndex - 1);
            //執(zhí)行pop操作
            record.executePopOps(moveToState);
        } else {//如果是非pop操作
            //所有的管理的fragment后面的堆棧計數(shù)器+1
            record.bumpBackStackNesting(1);
            //執(zhí)行ops操作
            record.executeOps();
        }
    }
}

這里先對fragment中的表明后面的片段計數(shù)器進行了更新祟绊。然后根據(jù)是否是pop,分別執(zhí)行了executePopOpsexecuteOps方法哥捕,我們這里只先跟蹤一下executeOps()這個方法的執(zhí)行牧抽。

//BackStackRecords.java
//執(zhí)行事務包含的操作,只有不允許優(yōu)化的時候扭弧,fragment的狀態(tài)才會被改變
void executeOps() {
    final int numOps = mOps.size();
    for (int opNum = 0; opNum < numOps; opNum++) {
        final Op op = mOps.get(opNum);
        final Fragment f = op.fragment;
        if (f != null) {//設置動畫
            f.setNextTransition(mTransition, mTransitionStyle);
        }
        switch (op.cmd) {//通過manager來進行fragment的管理工作
            case OP_ADD:
                f.setNextAnim(op.enterAnim);
                mManager.addFragment(f, false);//這里我們隊addFragment來進行一個跟蹤處理
                break;
            case OP_REMOVE:
                f.setNextAnim(op.exitAnim);
                mManager.removeFragment(f);
                break;
            case OP_HIDE:
                f.setNextAnim(op.exitAnim);
                mManager.hideFragment(f);
                break;
            case OP_SHOW:
                f.setNextAnim(op.enterAnim);
                mManager.showFragment(f);
                break;
            case OP_DETACH:
                f.setNextAnim(op.exitAnim);
                mManager.detachFragment(f);
                break;
            case OP_ATTACH:
                f.setNextAnim(op.enterAnim);
                mManager.attachFragment(f);
                break;
            case OP_SET_PRIMARY_NAV:
                mManager.setPrimaryNavigationFragment(f);
                break;
            case OP_UNSET_PRIMARY_NAV:
                mManager.setPrimaryNavigationFragment(null);
                break;
            default:
                throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
        }
        if (!mReorderingAllowed && op.cmd != OP_ADD && f != null) {
            mManager.moveFragmentToExpectedState(f);
        }
    }
    if (!mReorderingAllowed) {
        // Added fragments are added at the end to comply with prior behavior.
        //對FragmentManager所有管理的fragment進行一次生命周期的同步
        mManager.moveToState(mManager.mCurState, true);
    }
}

這里是通過FragmentManager來對事務中的fragment來進行了管理阎姥,然后最后通過狀態(tài)的同步,將本次事務的所有變化的fragment的生命周期和綁定的activity的生命周期進行一次同步鸽捻。

總結

每一次學習都要有新的收獲呼巴,這次通過學習fragment的事務執(zhí)行的方法學到了新的知識點

  1. 事務的提交并不是直接就執(zhí)行,而是通過主線程的Handler機制來執(zhí)行的御蒲。我們經常遇到的add去顯示的時候衣赶,有一個isAdd的崩潰,哪怕我們先進行了isAdd的判斷厚满,也攔截不住府瞄,就是因為Handler執(zhí)行時,從隊列取出消息,如果這時候還沒有執(zhí)行遵馆,那么isAdd就仍然是空鲸郊。遇到過一種解決方案就是glide的,在add之前货邓,將創(chuàng)建的fragment存放到緩存中秆撮,然后commit之后,再發(fā)送一個handler消息换况,從緩存中移除即可职辨。
  2. 事務的執(zhí)行是有優(yōu)化的,會將一些能夠優(yōu)化的地方進行處理戈二。
  3. 對于fragment的管理舒裤,是通過FragmentManager來統(tǒng)一進行的。
  4. 當有多個事務時觉吭,會將事務按照是否能夠優(yōu)化和是否為pop來進行分批腾供,進行分批處理。

本篇文章由一文多發(fā)平臺ArtiPub自動發(fā)布

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末亏栈,一起剝皮案震驚了整個濱河市台腥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绒北,老刑警劉巖黎侈,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異闷游,居然都是意外死亡峻汉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門脐往,熙熙樓的掌柜王于貴愁眉苦臉地迎上來休吠,“玉大人,你說我怎么就攤上這事业簿×鼋福” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵梅尤,是天一觀的道長柜思。 經常有香客問我,道長巷燥,這世上最難降的妖魔是什么赡盘? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮缰揪,結果婚禮上陨享,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好抛姑,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布赞厕。 她就那樣靜靜地躺著,像睡著了一般定硝。 火紅的嫁衣襯著肌膚如雪坑傅。 梳的紋絲不亂的頭發(fā)上喷斋,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天蒜茴,我揣著相機與錄音星爪,去河邊找鬼粉私。 笑死顽腾,一個胖子當著我的面吹牛,可吹牛的內容都是我干的诺核。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼漓摩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了入客?” 一聲冷哼從身側響起管毙,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎桌硫,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卓舵,經...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡膀钠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了托修。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡砚嘴,死狀恐怖,靈堂內的尸體忽然破棺而出际长,到底是詐尸還是另有隱情,我是刑警寧澤工育,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站嘱朽,受9級特大地震影響,放射性物質發(fā)生泄漏搪泳。R本人自食惡果不足惜扼脐,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瓦侮。 院中可真熱鬧,春花似錦方妖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至坞生,卻和暖如春仔役,著一層夾襖步出監(jiān)牢的瞬間是己,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工沛厨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留摔认,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓电谣,卻偏偏與公主長得像秽梅,于是被迫代替她去往敵國和親剿牺。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容