View.inflate方法和LayoutInflater.from(context).inflate方法詳解

  • 從xml中加載一個(gè)View,一般通過(guò)以下兩個(gè)方法:View#inflate(Context context, @LayoutRes int resource, ViewGroup root)方法和LayoutInflater#inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)方法

  • View#inflate方法源碼可以看到,它最終也是調(diào)用LayoutInflater#inflate方法

<!----------View----------->

  public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
        LayoutInflater factory = LayoutInflater.from(context);
        return factory.inflate(resource, root);
    }
  • 跟著進(jìn)入LayoutInflater#inflate方法源碼
<!----------LayoutInflater------------>

 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);   
    }
  • LayoutInflater兩個(gè)參數(shù)的inflate方法最終又調(diào)用了三個(gè)參數(shù)的inflate方法
   public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
  
        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
  • 三個(gè)參數(shù)的方法最終又調(diào)用了它的重載方法,真正的返回了View
 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

            final Context inflaterContext = mContext;
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            View result = root;

            try {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }

                final String name = parser.getName();
                

                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // Temp is the root view that was found in the xml
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;

                    if (root != null) {
                        if (DEBUG) {
                            System.out.println("Creating params from root: " +
                                    root);
                        }
                        // Create layout params that match root, if supplied
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            // Set the layout params for temp if we are not
                            // attaching. (If we are, we use addView, below)
                            temp.setLayoutParams(params);
                        }
                    }

                    if (DEBUG) {
                        System.out.println("-----> start inflating children");
                    }

                    // Inflate all children under temp against its context.
                    rInflateChildren(parser, temp, attrs, true);

                    if (DEBUG) {
                        System.out.println("-----> done inflating children");
                    }

                    // We are supposed to attach all the views we found (int temp)
                    // to root. Do that now.
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }

                    // Decide whether to return the root that was passed in or the
                    // top view found in xml.
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }

            } catch (XmlPullParserException e) {
                final InflateException ie = new InflateException(e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } catch (Exception e) {
                final InflateException ie = new InflateException(parser.getPositionDescription()
                        + ": " + e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } finally {
                // Don't retain static reference on context.
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;

                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }

            return result;
        }
    }
  • 為了方便分析取出關(guān)鍵代碼如下
 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
            View result = root;
            
            final String name = parser.getName();

            if (TAG_MERGE.equals(name)) {
                if (root == null || !attachToRoot) {
                    throw new InflateException("<merge /> can be used only with a valid "
                            + "ViewGroup root and attachToRoot=true");
                }

                rInflate(parser, root, inflaterContext, attrs, false);
            } else {
                // 獲取xml中的根view
                final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                ViewGroup.LayoutParams params = null;

                if (root != null) {

                    // Create layout params that match root, if supplied
                    params = root.generateLayoutParams(attrs);
                    if (!attachToRoot) {
                        // Set the layout params for temp if we are not
                        // attaching. (If we are, we use addView, below)
                        //如果ViewGroup root不為null并且attachToRoot == false,為xml獲取的根view temp設(shè)置 ViewGroup.LayoutParams
                        temp.setLayoutParams(params);
                    }
                }


                // 將temp下所有的子view添加到temp中
                rInflateChildren(parser, temp, attrs, true);  

                //如果ViewGroup root不為null并且attachToRoot == true,將temp添加到root中,并設(shè)置 ViewGroup.LayoutParams
                if (root != null && attachToRoot) {
                    root.addView(temp, params);
                }

                // 如果傳進(jìn)來(lái)的ViewGroup root為null 或者attachToRoot == false,返回xml中的根view temp,其它情況返回傳進(jìn)來(lái)的ViewGroup root
                if (root == null || !attachToRoot) {
                    result = temp;
                }
            }

            return result;  
    }
  • 從最后的方法中可以看到,關(guān)鍵的點(diǎn)在于傳入的ViewGroup root的值和boolean attachToRoot的值,這兩個(gè)值得取值不同,最終影響返回的是傳入的ViewGroup root還是獲取xml中的根view temp,并且影響是否為temp設(shè)置了ViewGroup.LayoutParams
  1. ViewGroup root == null && attachToRoot == false ====> 返回獲取xml中的根view temp,同時(shí)temp并沒(méi)有設(shè)置了ViewGroup.LayoutParams(此時(shí)如果獲取LayoutParams可能為空)
  2. ViewGroup root == null && attachToRoot == true ====> 返回獲取xml中的根view temp,同時(shí)temp并沒(méi)有設(shè)置了ViewGroup.LayoutParams(此時(shí)如果獲取LayoutParams可能為空)
  3. ViewGroup root != null && attachToRoot == false====>返回獲取xml中的根view temp,同時(shí)設(shè)置了ViewGroup.LayoutParams
  4. ViewGroup root != null && attachToRoot == true====> 返回ViewGroup root,同時(shí)為獲取到xml中的根view temp設(shè)置了ViewGroup.LayoutParams,并添加到ViewGroup root
  • 現(xiàn)在再來(lái)看View#inflate(Context context, @LayoutRes int resource, ViewGroup root)方法,它實(shí)際上調(diào)用了inflate(resource, root, root != null) ,也就是說(shuō)有兩種情況:
  1. 一種是 ViewGroup root == null && attachToRoot == false,此時(shí)需要注意的是返回的是xml布局的根View,并且并未為該根View設(shè)置ViewGroup.LayoutParams,在這種情況需要獲取view的LayoutParams進(jìn)行操作的需要特別注意
  2. 另外一種是ViewGroup root != null && attachToRoot == true,此時(shí)返回的是傳入的ViewGroup root,同時(shí)為獲取到xml中的根view temp設(shè)置了ViewGroup.LayoutParams,并添加到ViewGroup root,這種情況需要注意的是xml中的布局已經(jīng)被添加到ViewGroup root中,如果需要添加到另外的地方,這種方法是不可行的
  • 也就是說(shuō)對(duì)于View#inflate方法,想要滿足ViewGroup root != null && attachToRoot == false是無(wú)法滿足的。而如果需要使用xml中的根view的ViewGroup.LayoutParams,首先需要滿足ViewGroup root不為空(當(dāng)然在onLayout之后使用是可以的,此時(shí)已經(jīng)有parent了),另外對(duì)于ReclclerView/ListView來(lái)說(shuō),它會(huì)自己在合適的時(shí)機(jī)將child添加進(jìn)來(lái),所以attachToRoot必須需要false,否則在渲染View的時(shí)候就會(huì)首先添加到ViewGroup root中,導(dǎo)致重復(fù)添加到parent中報(bào)錯(cuò),因此View#inflate方法是無(wú)法滿足的,需要使用LayoutInflater#inflate(resource,root,false)方法
  • 對(duì)于LayoutInflater#inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)方法,同樣的也會(huì)根據(jù)傳入的值得不同得到不同的結(jié)果,在選擇使用哪種方法獲取View的時(shí)候就需要考慮對(duì)于接下來(lái)要對(duì)View進(jìn)行的操作是否有影響

  • 擴(kuò)展
    在學(xué)習(xí)Fragment的時(shí)候,下面的寫法應(yīng)該是熟悉到不能再熟悉了

 @Nullable
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(layoutRes, container, false);
        return view ;
    }
  • View view = inflater.inflate(layoutRes, container, false);一直是我們的固定寫法,我想一定有同學(xué)會(huì)跟我一樣覺(jué)得為什么一定要false,改成true可不可以
public class TestViewInflaterFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.activity_main, container, true);
    }
}
  • 當(dāng)把它添加到activity中的時(shí)候
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        supportFragmentManager.beginTransaction().add(R.id.fl_container, TestViewInflaterFragment()).commit()
    }
}
  • 下面的錯(cuò)誤一定會(huì)教你老老實(shí)實(shí)做人,大家都是這樣說(shuō)用false是有道理的,但是我們除了記得需要用false,還是需要知道為什么一定要用false,而用true就不行
 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.test.demo/com.test.demo.MainActivity}: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
                                                                                  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
                                                                                  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
                                                                                  at android.app.ActivityThread.-wrap11(Unknown Source:0)
                                                                                  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
                                                                                  at android.os.Handler.dispatchMessage(Handler.java:105)
                                                                                  at android.os.Looper.loop(Looper.java:164)
                                                                                  at android.app.ActivityThread.main(ActivityThread.java:6541)
                                                                                  at java.lang.reflect.Method.invoke(Native Method)
                                                                                  at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
                                                                                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
                                                                               Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
                                                                                  at android.view.ViewGroup.addViewInner(ViewGroup.java:4915)
                                                                                  at android.view.ViewGroup.addView(ViewGroup.java:4746)
                                                                                  at android.view.ViewGroup.addView(ViewGroup.java:4686)
                                                                                  at android.view.ViewGroup.addView(ViewGroup.java:4659)
                                                                                  at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1425)
                                                                                  at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1740)
                                                                                  at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1809)
                                                                                  at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:799)
                                                                                  at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2580)
                                                                                  at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2367)
                                                                                  at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2322)
                                                                                  at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2229)
                                                                                  at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3221)
                                                                                  at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:3171)
                                                                                  at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:192)
                                                                                  at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:560)
                                                                                  at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:177)
                                                                                  at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1333)
                                                                                  at android.app.Activity.performStart(Activity.java:6992)
                                                                                  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2780)
                                                                                  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892) 
                                                                                  at android.app.ActivityThread.-wrap11(Unknown Source:0) 
                                                                                  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593) 
                                                                                  at android.os.Handler.dispatchMessage(Handler.java:105) 
                                                                                  at android.os.Looper.loop(Looper.java:164) 
                                                                                  at android.app.ActivityThread.main(ActivityThread.java:6541) 
                                                                                  at java.lang.reflect.Method.invoke(Native Method) 
                                                                                  at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
                                                                                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767) 
  • 仔細(xì)看一看錯(cuò)誤日志The specified child already has a parent. You must call removeView() on the child's parent first.,看這句結(jié)合前面說(shuō)過(guò)的內(nèi)容,我想大家肯定已經(jīng)知道為什么了,當(dāng)我們寫成true的時(shí)候,會(huì)將xml創(chuàng)建的root view添加到container,然后推測(cè)Fragment被加載的時(shí)候在某個(gè)地方又將xml創(chuàng)建的root view再添加到一個(gè)ViewGroup中,所以會(huì)導(dǎo)致這個(gè)錯(cuò)誤,繼續(xù)看一看推測(cè)是否正確

  • 先看Fragment#onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)在哪里被調(diào)用

  View performCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (mChildFragmentManager != null) {
            mChildFragmentManager.noteStateNotSaved();
        }
        mPerformedCreateView = true;
        return onCreateView(inflater, container, savedInstanceState);
    }
  • 繼續(xù)看Fragment#performCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)的調(diào)用
//-----------FramgnetManager---------------
 void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {   
                //省略部分代碼
                  ``` 
            switch (f.mState) {
                  //省略部分代碼
                  ``` 
                case Fragment.CREATED:
                    // This is outside the if statement below on purpose; we want this to run
                    // even if we do a moveToState from CREATED => *, CREATED => CREATED, and
                    // * => CREATED as part of the case fallthrough above.
                    ensureInflatedFragmentView(f);

                    if (newState > Fragment.CREATED) {
                        if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
                        if (!f.mFromLayout) {
                            ViewGroup container = null;
                            if (f.mContainerId != 0) {
                                if (f.mContainerId == View.NO_ID) {
                                    throwException(new IllegalArgumentException(
                                            "Cannot create fragment "
                                                    + f
                                                    + " for a container view with no id"));
                                }
                           //首先根據(jù)我們傳入的mContainerId(對(duì)于本案例來(lái)說(shuō)就是R.id.fl_container)找到container 
                                container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
                                if (container == null && !f.mRestored) {
                                    String resName;
                                    try {
                                        resName = f.getResources().getResourceName(f.mContainerId);
                                    } catch (NotFoundException e) {
                                        resName = "unknown";
                                    }
                                    throwException(new IllegalArgumentException(
                                            "No view found for id 0x"
                                            + Integer.toHexString(f.mContainerId) + " ("
                                            + resName
                                            + ") for fragment " + f));
                                }
                            }
                            f.mContainer = container;
                          //這個(gè)f.mView就是我們自己在onCreateView方法中創(chuàng)建返回的View
                            f.mView = f.performCreateView(f.performGetLayoutInflater(
                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
                            if (f.mView != null) {
                                f.mInnerView = f.mView;
                                f.mView.setSaveFromParentEnabled(false);
                                if (container != null) {
                            //關(guān)鍵地方,在這里會(huì)將xml創(chuàng)建的view添加到container
                                    container.addView(f.mView);
                                }
                          //省略部分代碼
                            ``` 
                    }

                   //省略部分代碼
                  ``` 
            }
    }
  • 方法太長(zhǎng),為了便于查看省略了部分代碼,看注釋已經(jīng)說(shuō)明了前面的推測(cè)是正確的,當(dāng)改為true的時(shí)候,在創(chuàng)建xml view的時(shí)候會(huì)將view add到傳入的parent(container)中,然后將Fragment加載進(jìn)來(lái)的時(shí)候,F(xiàn)ragmentManager會(huì)再一次將創(chuàng)建的xml view添加到container(也就是我們指定的container id指向的ViewGroup)中,所以導(dǎo)致了重復(fù)添加一個(gè)view到ViewGroup中的錯(cuò)誤。

  • 另外再說(shuō)一個(gè)查看源碼的小技巧,首先源碼太多太復(fù)雜,一頭扎進(jìn)去可能就出不來(lái)了。首先需要抱著一個(gè)目的去查看源碼,一般查看源碼是為了驗(yàn)證一個(gè)東西或者學(xué)習(xí)源碼是怎么樣實(shí)現(xiàn)某種效果或者某個(gè)功能的,這就是這次查看源碼的目的,主線。像前面就是為了驗(yàn)證是不是Fragment加載的時(shí)候會(huì)將創(chuàng)建的View添加到某個(gè)ViewGroup中,然后根據(jù)方法的調(diào)用去找尋可能是我們目的的代碼,有時(shí)候有些方法的調(diào)用是很復(fù)雜的,有些方法也特別長(zhǎng),稍微不注意可能就錯(cuò)過(guò)了想要的內(nèi)容。比如之前的Fragment#performCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)還有另外一個(gè)地方唄調(diào)用,可能我們會(huì)進(jìn)來(lái)了再一直深入,然后很容易就被繞暈了

 void ensureInflatedFragmentView(Fragment f) {
        if (f.mFromLayout && !f.mPerformedCreateView) {
            f.mView = f.performCreateView(f.performGetLayoutInflater(
                    f.mSavedFragmentState), null, f.mSavedFragmentState);
            if (f.mView != null) {
                f.mInnerView = f.mView;
                f.mView.setSaveFromParentEnabled(false);
                if (f.mHidden) f.mView.setVisibility(View.GONE);
                f.onViewCreated(f.mView, f.mSavedFragmentState);
                dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
            } else {
                f.mInnerView = null;
            }
        }
    }
  • 另外FragmentManager#moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive)也非常長(zhǎng),怎么樣才能提高找到線索的可能性呢? 還記的我們的目的嗎?驗(yàn)證是不是Fragment加載的時(shí)候會(huì)將創(chuàng)建的View添加到某個(gè)ViewGroup,添加到ViewGroup,第一時(shí)間應(yīng)該想到ViewGroup#addView方法,然后在對(duì)應(yīng)的方法里面搜索一下addView,如果找到了有,再前后代碼查看一下是不是我們的目的,一步一步排查下去最終解決我們的問(wèn)題。
  • 這只是一個(gè)查看源碼的小技巧,可能并不能每次都能解決所有的問(wèn)題。但是可以幫我們少走一點(diǎn)彎路。畢竟一入源碼深似海。而且最好不要抱著把源碼里面的所有東西都看懂,每一個(gè)變量代表什么都去糾結(jié)(當(dāng)然如果有這個(gè)能力的話也是可以的)。應(yīng)該抱著學(xué)習(xí)和解決問(wèn)題的目的,有針對(duì)性的去看源碼,沿著一條主線去搞懂并解決我們遇到的問(wèn)題。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,676評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 176,730評(píng)論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 63,118評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,873評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 55,266評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評(píng)論 3 443
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 42,482評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,036評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,846評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,025評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,279評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 34,684評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 35,953評(píng)論 1 289
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,751評(píng)論 3 394
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,016評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容