Navigation學(xué)習(xí)總結(jié)

Google 在官方開發(fā)工具包中(Android Jetpack)中提供了一個(gè)用于Android app導(dǎo)航的全新框架“Navigation”霎肯,配合IDE可以很方便的查看App中頁(yè)面之間或模塊之間的關(guān)聯(lián)關(guān)系檬输,這個(gè)跟IOS中StoryBoard很像辆琅。
官方文檔:The Navigation Architecture Component
官方教程:Navigation Codelab
官方Demo:android-navigation

概述



使用Navigation Architecture Component(后面簡(jiǎn)稱Navigation)不但可以實(shí)現(xiàn)App間復(fù)雜的導(dǎo)航關(guān)系而且還使得導(dǎo)航關(guān)系可視化在這一點(diǎn)上要比一些第三方的導(dǎo)航框架(ARouter等)要好的多焚刚。在Navigation框架中引入了以下幾個(gè)概念需要說明下:
Destination -- 直譯過來就是目的地的意思越庇,結(jié)合Android開發(fā)環(huán)境理解鞠柄,指的就是頁(yè)面或者模塊等骄瓣。Activity、Fragment师坎、Graph等都可以充當(dāng)一個(gè)Destination恕酸。

  • Action -- 就是頁(yè)面間的導(dǎo)航關(guān)系用于連接Destination
  • Graph -- 多個(gè)Destination通過Action連接起來就是一個(gè)Graph

Navigation 框架支持在Fragment、Activity胯陋、Graph蕊温、SubGraph、自定義Destination之間導(dǎo)航遏乔。包括前面提到的功能總結(jié)起來Navigation框架總共提供了以下一系列的附加功能义矛,用于輔助開發(fā)簡(jiǎn)化開發(fā)流程:

  • 處理Fragment的事務(wù)(Transactions)
  • 為返回操作(Back & Up)提供正確的默認(rèn)實(shí)現(xiàn)
  • 為動(dòng)畫和過渡提供標(biāo)準(zhǔn)的資源
  • 支持Deep link
  • 通過很少的額外操作就可以支持Navigation UI,例如Navigation Drawer按灶、Bottom Navigation等
  • 使頁(yè)面間傳值變的更加安全
  • 通過IDE可以實(shí)現(xiàn)可視化編輯

在使用Navigation框架的時(shí)候有以下幾點(diǎn)需要注意:

  • 使用一個(gè)棧來代表App的導(dǎo)航狀態(tài)
  • 必須要有一個(gè)固定的起始Destination
  • 不能使用Up button退出你的程序
  • 在App任務(wù)中向上和返回按鈕是等價(jià)的
  • 深度鏈接到目標(biāo)或?qū)Ш降较嗤哪繕?biāo)應(yīng)產(chǎn)生相同的堆棧

Navigation的使用


配置IDE

要是使用Navigation框架要求你的Android Studio版本必須是3.2+症革,如果你的Android Studio版本是3.2,你需要進(jìn)入IDE的設(shè)置界面找到“Enable Navigation Editor”選項(xiàng)并選中(需要重新啟動(dòng)Android Studio)鸯旁。


image.png

配置項(xiàng)目



創(chuàng)建一個(gè)標(biāo)準(zhǔn)的Android Project然后在配置文件中(build.gradle)中配置Navigation依賴(這里需要注意Android官方更新了Support Library的命名空間具體參考官方文檔),具體配置方式如下:

dependencies {
    def nav_version = "1.0.0-alpha08"

    implementation "android.arch.navigation:navigation-fragment:$nav_version" // use -ktx for Kotlin
    implementation "android.arch.navigation:navigation-ui:$nav_version" // use -ktx for Kotlin

    // optional - Test helpers
    // this library depends on the Kotlin standard library
    androidTestImplementation "android.arch.navigation:navigation-testing:$nav_version"
}

如果要使用“Safe args”特性還需要增加如下配置:

apply plugin: "androidx.navigation.safeargs"

創(chuàng)建Navigation Graph



關(guān)于這里官方教程有點(diǎn)啰嗦總結(jié)起來就是以下幾步:

  1. 在你項(xiàng)目工程的“res”目錄下創(chuàng)建“navigation”文件夾
  2. 在新建的“navigation”目錄下右鍵新建一個(gè)“Navigation resource file”

完成后預(yù)覽界面和源碼界面分別如下圖所示:


image.png

image.png

根據(jù)提示點(diǎn)擊"+"創(chuàng)建幾個(gè)測(cè)試用的Destination量蕊,完成后如圖:

image.png

對(duì)應(yīng)的源碼如下:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            android:id="@+id/setting_nav_graph"
            app:startDestination="@id/mainSettingFragment">

    <fragment
            android:id="@+id/mainSettingFragment"
            android:name="com.wangqiang.pro.navigationdemo.MainSettingFragment"
            android:label="fragment_main_setting"
            tools:layout="@layout/fragment_main_setting">
        <action
                android:id="@+id/action_mainSettingFragment_to_cameraSettingFragment"
                app:destination="@id/cameraSettingFragment"/>
    </fragment>
    <fragment
            android:id="@+id/cameraSettingFragment"
            android:name="com.wangqiang.pro.navigationdemo.CameraSettingFragment"
            android:label="fragment_camera_setting"
            tools:layout="@layout/fragment_camera_setting"/>
</navigation>

然后編輯Activity的布局文件铺罢,在布局文件中增加“NavHostFragment”,其中"main_nav"就是剛剛新建的Navigation Graph文件名(main_nav.xml)代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"

        app:defaultNavHost="true"
        app:navGraph="@navigation/main_nav" />

</android.support.constraint.ConstraintLayout>

頁(yè)面跳轉(zhuǎn)



要實(shí)現(xiàn)從MainSettingFragment到CameraSettingFragment我們只需要在MainSettingFragment中的按鈕點(diǎn)擊事件中添加如下代碼:

view.findViewById<Button>(R.id.camera)
.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_mainSettingFragment_to_cameraSettingFragment))

“action_mainSettingFragment_to_cameraSettingFragment”就是你定義的Action的id残炮。

頁(yè)面?zhèn)髦?/h1>



官方提倡通過這種方式傳遞一些輕量級(jí)的數(shù)據(jù)韭赘,如果數(shù)據(jù)量比較大的情況下使用“ViewModel”在Fragment之間共享數(shù)據(jù)。被傳遞的數(shù)據(jù)需要在Destination上配置势就,配置方法有兩種泉瞻,可以使用IDE提供的圖形界面進(jìn)行配置也可以使用源碼的方式直接編輯Navigation Graph源文件實(shí)現(xiàn)脉漏。這里我們的目標(biāo)Destination(CameraSettingFragment)需要一個(gè)integer類型的“camera_id”參數(shù),配置完成后文件內(nèi)容如下:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            android:id="@+id/setting_nav_graph"
            app:startDestination="@id/mainSettingFragment">

    <fragment
            android:id="@+id/mainSettingFragment"
            android:name="com.wangqiang.pro.navigationdemo.MainSettingFragment"
            android:label="fragment_main_setting"
            tools:layout="@layout/fragment_main_setting">
        <action
                android:id="@+id/action_mainSettingFragment_to_cameraSettingFragment"
                app:destination="@id/cameraSettingFragment"/>
    </fragment>
    <fragment
            android:id="@+id/cameraSettingFragment"
            android:name="com.wangqiang.pro.navigationdemo.CameraSettingFragment"
            android:label="fragment_camera_setting"
            tools:layout="@layout/fragment_camera_setting">
        <argument
                android:name="camera_id"
                app:argType="integer"
                android:defaultValue="0" />
    </fragment>
</navigation>

完成后IDE應(yīng)該是自動(dòng)幫我們生成MainSettingFragmentDirections和CameraSettingFragmentArgs兩個(gè)類(如果沒有生成手動(dòng)編譯一下工程)袖牙,這兩個(gè)類的命名規(guī)則是分別在起始Des 提 nation和目標(biāo)Destination加上“Directions”和“Args”后綴侧巨,他們的作用分別如下:

  • MainSettingFragmentDirections -- 設(shè)置要傳遞的參數(shù)
  • CameraSettingFragmentArgs -- 取出傳遞的參數(shù)

然后修改下我們的跳轉(zhuǎn)代碼:

//MainSettingFragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    view.findViewById<Button>(R.id.camera).setOnClickListener {
        val action = MainSettingFragmentDirections.actionMainSettingFragmentToCameraSettingFragment().setCameraId(1)
        findNavController().navigate(action)
    }
}

取出傳遞參數(shù):

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val cameraId = CameraSettingFragmentArgs.fromBundle(arguments).cameraId
}

到此為止頁(yè)面間傳值實(shí)現(xiàn)完成,如果不生效或發(fā)生錯(cuò)誤請(qǐng)檢查依賴組件是否正確配置鞭达。這里只是簡(jiǎn)略的說明下該框架的使用方法司忱,作為對(duì)該框架使用流程的備忘,還有很多細(xì)節(jié)的地方?jīng)]有涉及到畴蹭,如果需要請(qǐng)自行查閱官方文檔坦仍。

總結(jié)分析



用如此優(yōu)雅的方式重新定義Android App中的頁(yè)面導(dǎo)航,在這里我獻(xiàn)上在認(rèn)知范圍內(nèi)的所有贊美叨襟,Navigation框架的出現(xiàn)確實(shí)為Android App開發(fā)過程中那謎一樣的跳轉(zhuǎn)帶來了光明與秩序繁扎。下面是我個(gè)人對(duì)Navigation Architecture Component粗鄙的認(rèn)知與理解,如有不到位的地方歡迎留言(拍磚)指正糊闽。Navigation Architecture Component中主要有以下核心類組成梳玫,主要關(guān)系如圖:

image.png

該圖所對(duì)應(yīng)的Navigation Architecture Component版本為1.0.0-alpha07由于是alpha版本所以版本之間的變化可以可能會(huì)有比較大的變化各位看官請(qǐng)注意。
我認(rèn)為關(guān)于Navigation最核心的東西就是以上這些了墓怀,下面說下我的個(gè)人理解汽纠。先來看下NaviHostFragment的源碼

public class NavHostFragment extends Fragment implements NavHost {
  private NavController mNavController;
   @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final Context context = requireContext();

        mNavController = new NavController(context);
        mNavController.getNavigatorProvider().addNavigator(createFragmentNavigator());
      ....
    }
  
    @NonNull
    @Override
    public NavController getNavController() {
        if (mNavController == null) {
            throw new IllegalStateException("NavController is not available before onCreate()");
        }
        return mNavController;
    }
}

public interface NavHost {
    @NonNull
    NavController getNavController();
}

源碼比較簡(jiǎn)單只有三百行左右的,這里只摘取了用于說明問題的關(guān)鍵代碼傀履,主要作用就是作為其它的功能頁(yè)面(Fragment)的宿主(容器)虱朵,實(shí)現(xiàn)功能頁(yè)面的切換。前面Demo中在Activity中的xml布局文件中寫的fragment標(biāo)簽就是它(NavHostFragment)钓账。NahHostFragment里面有一個(gè)mNavController實(shí)例變量同時(shí)實(shí)現(xiàn)了一個(gè)NavHost的接口碴犬,這個(gè)接口只有一個(gè)getNavController方法其主要作用就是用于獲取NavHostFragment的私有變量 mNavController,關(guān)于NavController的源碼如下:

public class NavController {
  final Deque<NavDestination> mBackStack = new ArrayDeque<>();
  private final SimpleNavigatorProvider mNavigatorProvider = new SimpleNavigatorProvider() {
        @Nullable
        @Override
        public Navigator<? extends NavDestination> addNavigator(@NonNull String name,
                @NonNull Navigator<? extends NavDestination> navigator) {
            Navigator<? extends NavDestination> previousNavigator =
                    super.addNavigator(name, navigator);
            if (previousNavigator != navigator) {
                if (previousNavigator != null) {
                    previousNavigator.removeOnNavigatorNavigatedListener(mOnNavigatedListener);
                }
                navigator.addOnNavigatorNavigatedListener(mOnNavigatedListener);
            }
            return previousNavigator;
        }
  };
  
  public NavController(@NonNull Context context) {
        mContext = context;
        while (context instanceof ContextWrapper) {
            if (context instanceof Activity) {
                mActivity = (Activity) context;
                break;
            }
            context = ((ContextWrapper) context).getBaseContext();
        }
        mNavigatorProvider.addNavigator(new NavGraphNavigator(mContext));
        mNavigatorProvider.addNavigator(new ActivityNavigator(mContext));
    }
}

NavController的主要是用于控制頁(yè)面(Fragment, Activity, NavGraph)的切換梆暮,主要有兩個(gè)實(shí)例變量需要注意分別是mBackStack和mNavigatorProvider服协。加載到NavHostFragment中的頁(yè)面(Destination)的棧存儲(chǔ)結(jié)構(gòu)就是通過mBackStack去記錄維護(hù)。mNavigatorProvider是一個(gè)導(dǎo)航(跳轉(zhuǎn))策略集合啦粹,為什么要這樣搞偿荷?個(gè)人覺得這里設(shè)計(jì)的就比較巧妙,這是因?yàn)橥瑫r(shí)支持Fragment唠椭,Activity和NavGraph導(dǎo)航(跳轉(zhuǎn))而這三種Destination的跳轉(zhuǎn)方式并不一樣胚宦,所以通過這種設(shè)計(jì)方法就可以支持多種跳轉(zhuǎn)策略羔味,這個(gè)策略集合默認(rèn)添加了ActivityNavigator蜜葱、NavGraphNavigator和FragmentNavigator祠够。細(xì)心的你可能發(fā)現(xiàn)上面的源碼沒有FragmentNavigator,??對(duì)上面確實(shí)沒有,因?yàn)樗皇窃贜avController實(shí)例化的時(shí)候添加的斗塘,它是是在NavHostFragment初始化的時(shí)候通過外部注冊(cè)的方式添加的赢织。理解了這一點(diǎn),你就可以靈活的對(duì)Navigation框架的跳轉(zhuǎn)策略進(jìn)行擴(kuò)展馍盟,例如你想對(duì)框架增加View之間路由(跳轉(zhuǎn))的擴(kuò)展于置!怎么搞?你只需要寫一個(gè)繼承Navigator的ViewNavigator朽合。

Navigator是什么俱两?Action是對(duì)一個(gè)導(dǎo)航(或者說跳轉(zhuǎn))動(dòng)作的描述,而Navigator就是Action的具體執(zhí)行者曹步,這是我能想到的對(duì)Navigator的最簡(jiǎn)潔的描述宪彩。關(guān)于Navigator的核心內(nèi)容如下:

public abstract class Navigator<D extends NavDestination> {

    @Retention(RUNTIME)
    @Target({TYPE})
    @SuppressWarnings("UnknownNullness") // TODO https://issuetracker.google.com/issues/112185120
    public @interface Name {
        String value();
    }

    @Retention(SOURCE)
    @IntDef({BACK_STACK_UNCHANGED, BACK_STACK_DESTINATION_ADDED, BACK_STACK_DESTINATION_POPPED})
    @interface BackStackEffect {}
  
    private final CopyOnWriteArrayList<OnNavigatorNavigatedListener> mOnNavigatedListeners =
            new CopyOnWriteArrayList<>();
  
    @NonNull
    public abstract D createDestination();
  
      public abstract void navigate(@NonNull D destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Extras navigatorExtras);
}

關(guān)于Action的實(shí)現(xiàn)類叫NavAction實(shí)現(xiàn)很簡(jiǎn)單只有幾十行代碼,感興趣可以自己看這里不做贅述讲婚。

首先看到Navigator中定義了兩個(gè)注解分別是Name和BackStackEffect尿孔,作用如下:

  • Name 該注解的作用是用于自定義注冊(cè)到NavigatorProvider的名稱,繼承Navigator的子類必須使用該注解標(biāo)注筹麸。
  • BackStackEffect 該注解的作用類似android support包中的@IdRes注解活合,用于限定變量的取值范圍(BACK_STACK_UNCHANGED, BACK_STACK_DESTINATION_ADDED, BACK_STACK_DESTINATION_POPPED),用于編譯階段的檢查物赶,在OnNavigatorNavigatedListener.onNavigatorNavigated()中有用到白指。

然后還定義了一個(gè)抽象的navigate(...)方法,在執(zhí)行Destination間跳轉(zhuǎn)的時(shí)候就是調(diào)用該方法酵紫,對(duì)應(yīng)的ActivityNavigator告嘲、FragmentNavigator、GraphNavigator分別有不同的具體實(shí)現(xiàn)奖地。這里我們可以看下FragmentNavigator的具體實(shí)現(xiàn):

//定義注冊(cè)到NavigatorProvider中的名稱
@Navigator.Name("fragment")
public class FragmentNavigator extends Navigator<FragmentNavigator.Destination> {
  @Override
    public void navigate(@NonNull Destination destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
        if (mFragmentManager.isStateSaved()) {
            Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
                    + " saved its state");
            return;
        }
        final Fragment frag = destination.createFragment(args);
        final FragmentTransaction ft = mFragmentManager.beginTransaction();

        int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
        int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
        int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
        int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
        if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
            enterAnim = enterAnim != -1 ? enterAnim : 0;
            exitAnim = exitAnim != -1 ? exitAnim : 0;
            popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
            popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
            ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
        }

        ft.replace(mContainerId, frag);
        ft.setPrimaryNavigationFragment(frag);

        final @IdRes int destId = destination.getId();
        final boolean initialNavigation = mBackStack.isEmpty();
        final boolean isClearTask = navOptions != null && navOptions.shouldClearTask();
        // TODO Build first class singleTop behavior for fragments
        final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
                && navOptions.shouldLaunchSingleTop()
                && mBackStack.peekLast() == destId;

        int backStackEffect;
        if (initialNavigation || isClearTask) {
            backStackEffect = BACK_STACK_DESTINATION_ADDED;
        } else if (isSingleTopReplacement) {
            // Single Top means we only want one instance on the back stack
            if (mBackStack.size() > 1) {
                // If the Fragment to be replaced is on the FragmentManager's
                // back stack, a simple replace() isn't enough so we
                // remove it from the back stack and put our replacement
                // on the back stack in its place
                mFragmentManager.popBackStack();
                ft.addToBackStack(Integer.toString(destId));
                mIsPendingBackStackOperation = true;
            }
            backStackEffect = BACK_STACK_UNCHANGED;
        } else {
            ft.addToBackStack(Integer.toString(destId));
            mIsPendingBackStackOperation = true;
            backStackEffect = BACK_STACK_DESTINATION_ADDED;
        }
        if (navigatorExtras instanceof Extras) {
            Extras extras = (Extras) navigatorExtras;
            for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
                ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
            }
        }
        ft.setReorderingAllowed(true);
        ft.commit();
        // The commit succeeded, update our view of the world
        if (backStackEffect == BACK_STACK_DESTINATION_ADDED) {
            mBackStack.add(destId);
        }
        dispatchOnNavigatorNavigated(destId, backStackEffect);
    }
}

巴拉巴拉很長(zhǎng)一坨橄唬,主要的就是通過FragmentManager完成Fragment Destination的切換,剩下的就是為切換過程增加動(dòng)畫效果以及為代切換的Fragment設(shè)置屬性以及切換數(shù)據(jù)等等参歹。

Destination對(duì)應(yīng)的實(shí)現(xiàn)類是NavDestination仰楚,主要有3個(gè)直接子類NavGraph、FragmentNavigator.Destination和ActivityNavigator.Destination犬庇,分別用于對(duì)導(dǎo)航圖僧界,F(xiàn)ragment和Activity的描述。多個(gè)Destination就組成了NavGraph臭挽,這里需要注意下NavGraph的子節(jié)點(diǎn)也可以是一個(gè)NavGraph捎泻,而且節(jié)點(diǎn)間可以任意調(diào)轉(zhuǎn),這說明NavGraph的數(shù)據(jù)結(jié)構(gòu)是圖埋哟。下面看下NavDestination的源碼:

public class NavDestination {
    //跳轉(zhuǎn)策略
    private final Navigator mNavigator;
    private NavGraph mParent;
    private int mId;
    private CharSequence mLabel;
    //跳轉(zhuǎn)需要的參數(shù)
    private Bundle mDefaultArgs;
    private ArrayList<NavDeepLink> mDeepLinks;
    private SparseArrayCompat<NavAction> mActions;
  
    public void navigate(@Nullable Bundle args, @Nullable NavOptions navOptions,
            @Nullable Navigator.Extras navigatorExtras) {
        Bundle defaultArgs = getDefaultArguments();
        Bundle finalArgs = new Bundle();
        finalArgs.putAll(defaultArgs);
        if (args != null) {
            finalArgs.putAll(args);
        }
        mNavigator.navigate(this, finalArgs, navOptions, navigatorExtras);
    }
}

NavDestination有一個(gè)mNavigator實(shí)例變量用于存儲(chǔ)跳轉(zhuǎn)策略,因?yàn)榍懊嬲f過NavDestination有多個(gè)類型(子類),不同類型的NavDestination之間的跳轉(zhuǎn)策略是不一樣的赤赊,NavDestination中的navigate(...)方法最終就是把跳轉(zhuǎn)工作委托給了mNavigator闯狱,我通過NavController執(zhí)行跳轉(zhuǎn)的時(shí)候最終就是調(diào)用到了這里。mActions是當(dāng)前節(jié)點(diǎn)可以導(dǎo)航(跳轉(zhuǎn))到哪些節(jié)點(diǎn)的一個(gè)集合抛计,是(1: n)的關(guān)系哄孤,典型的圖數(shù)據(jù)結(jié)構(gòu)。

最后感謝Google賜我Navigation框架吹截!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瘦陈,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子波俄,更是在濱河造成了極大的恐慌晨逝,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件懦铺,死亡現(xiàn)場(chǎng)離奇詭異捉貌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)冬念,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門趁窃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人急前,你說我怎么就攤上這事醒陆。” “怎么了裆针?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵刨摩,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我据块,道長(zhǎng)码邻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任另假,我火速辦了婚禮像屋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘边篮。我一直安慰自己己莺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布戈轿。 她就那樣靜靜地躺著凌受,像睡著了一般。 火紅的嫁衣襯著肌膚如雪思杯。 梳的紋絲不亂的頭發(fā)上胜蛉,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天挠进,我揣著相機(jī)與錄音,去河邊找鬼誊册。 笑死领突,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的案怯。 我是一名探鬼主播君旦,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼嘲碱!你這毒婦竟也來了金砍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤麦锯,失蹤者是張志新(化名)和其女友劉穎恕稠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體离咐,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谱俭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宵蛀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昆著。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖术陶,靈堂內(nèi)的尸體忽然破棺而出凑懂,到底是詐尸還是另有隱情,我是刑警寧澤梧宫,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布接谨,位于F島的核電站,受9級(jí)特大地震影響塘匣,放射性物質(zhì)發(fā)生泄漏脓豪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一忌卤、第九天 我趴在偏房一處隱蔽的房頂上張望扫夜。 院中可真熱鬧,春花似錦驰徊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)牺弹。三九已至时呀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間捐韩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留屎债,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓盆驹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親躯喇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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