Android性能測評與優(yōu)化-流暢度測評

系統(tǒng)級別的流暢度優(yōu)化

流暢度應(yīng)該是終端用戶感知最明顯的性能指標(biāo)了频鉴,提升流暢度是提升用戶體驗性價比最高的方式之一,我們先來看看在系統(tǒng)層面上Google為了優(yōu)化流暢度做了哪些努力

Vsync(垂直同步)

垂直同步是一個游戲中很常見的概念列吼,它的出現(xiàn)是為了解決如下圖的畫面撕裂的問題

畫面撕裂

究其原因是屏幕的刷新并不是瞬時完成的拨脉,而GPU產(chǎn)生一幀新畫面的速度和屏幕刷新速度不同步,當(dāng)GPU速度又大于顯示器的刷新速度,在顯示器從上到下掃描顯示的過程中圖像緩沖就被更新了逻翁,顯示器并不知道這個變化還是繼續(xù)掃描,就產(chǎn)生了畫面撕裂

image.png

屏幕刷新過程

android 4.1的黃油計劃引入垂直同步之后捡鱼,只有在接收到Vsync信號八回,系統(tǒng)才會讓CPU/GPU開始下一幀的渲染工作,即每個屏幕刷新周期之間最多只會產(chǎn)生一幀畫面,以此避免畫面撕裂

一旦收到VSync信號缠诅,立刻就開始執(zhí)行下一幀的繪制工作溶浴。這樣也可以大大降低Jank出現(xiàn)的概率。只需要保證渲染一幀畫面的時間在1/60s(16ms)就行了


image.png
image.png

Triple Buffer

先看看雙緩沖的模型

image

兩個緩存區(qū)分別為 Back Buffer 和 Frame Buffer管引。GPU 向 Back Buffer 中寫數(shù)據(jù)士败,屏幕從 Frame Buffer 中讀數(shù)據(jù)。VSync 信號負(fù)責(zé)調(diào)度從 Back Buffer 到 Frame Buffer 的復(fù)制操作褥伴,可認(rèn)為該復(fù)制操作在瞬間完成(只是交換了內(nèi)存地址)谅将。

如果所有渲染操作都在16ms之內(nèi)完成,雙重緩沖可以很好的工作重慢,但是渲染耗時超過16ms呢

image

第一個B畫面的渲染超過了16ms戏自,因為此時B畫面占據(jù)了Back Buffer,所以當(dāng)接收到下一幀的VSync信號時系統(tǒng)沒有開始渲染工作伤锚,導(dǎo)致jank的發(fā)生

而三重緩沖增加了一個Back Buffer

image
image

在接收到VSync信號擅笔,B畫面還在渲染,因為CPU已經(jīng)空閑了屯援,而且有另一塊緩沖區(qū)猛们,所以同時開始了下一幀的渲染工作

三重緩沖可以更充分的利用CPU/GPU提升畫面顯示的流暢度

  • 為什么不繼續(xù)增加緩沖區(qū)來提升流暢度呢?

硬件加速

硬件加速就是依賴GPU實現(xiàn)圖形繪制加速狞洋。

gpu&cpu

可以看出GPU的ALU(算術(shù)邏輯單元)比CPU多的多弯淘,而圖形處理和柵格化操作實際就是大量的數(shù)學(xué)計算,所以用GPU去渲染圖形比CPU快的多

android 3.0引入了硬件加速吉懊,android4.0以后默認(rèn)開啟了硬件加速

當(dāng)啟動硬件加速后庐橙,Android 使用 “DisplayList” 組件進(jìn)行繪制而非直接使用 CPU 繪制每一幀。DisplayList 是一系列繪制操作的記錄借嗽,抽象為 RenderNode 類态鳖。
這樣間接的進(jìn)行繪制操作的優(yōu)點很多:

  • DisplayList 創(chuàng)建時并不是真正的繪制,只是記錄了繪制的操作恶导,所以對DisplayList的修改開銷比較小浆竭。
  • 特定的View屬性變化(如 translation, scale 等)只是修改了View對應(yīng)的DisplayList的屬性惨寿,而不需要重新生成新的DisplayList邦泄。
  • 當(dāng)知曉了所有繪制操作后,可以針對其進(jìn)行優(yōu)化:例如裂垦,所有的文本可以一起進(jìn)行繪制一次顺囊。
  • 可以將對 DisplayList 的處理轉(zhuǎn)移至另一個線程(非 UI 線程)。

RenderThread

RenderThread是Android 5.0引入的功能蕉拢。
渲染工作的真正執(zhí)行者是 GPU特碳,而 GPU是不知道什么是動畫的:執(zhí)行動畫的唯一途徑便是將每一幀的不同繪制操作分發(fā)給 GPU诚亚,但該邏輯本身不能在 GPU 上執(zhí)行。
而如果在 UI 線程執(zhí)行該操作测萎,任意的重操作都將阻塞新的繪制指令及時分發(fā),動畫就很容易出現(xiàn)延遲和卡頓届巩。
添加一個RenderThread專門用來處理渲染的相關(guān)操作硅瞧,UI線程只管計算生成一個DisplayList,剩下的渲染相關(guān)的事情就交給RenderThread恕汇,這樣減輕了UI線程的負(fù)擔(dān)腕唧,也提升了動畫的流暢度

image.png

  • Android 6.0不同場景的軟/硬件繪制分析
渲染場景 純軟件繪制 硬件加速 加速效果分析
頁面初始化 繪制所有View 創(chuàng)建所有DisplayList GPU分擔(dān)了復(fù)雜計算任務(wù)
在一個復(fù)雜頁面調(diào)用背景透明TextView的setText(),且調(diào)用后其尺寸位置不變 重繪臟區(qū)所有View TextView及每一級父View重建DisplayList 重疊的兄弟節(jié)點不需CPU重繪瘾英,GPU會自行處理
TextView逐幀播放Alpha / Translation / Scale動畫 每幀都要重繪臟區(qū)所有View 除第一幀同場景2枣接,之后每幀只更新TextView對應(yīng)RenderNode的屬性 刷新一幀性能極大提高,動畫流暢度提高
修改TextView透明度 重繪臟區(qū)所有View 直接調(diào)用RenderNode.setAlpha()更新 加速前需全頁面遍歷缺谴,并重繪很多View但惶;加速后只觸發(fā)DecorView.updateDisplayListIfDirty,不再往下遍歷湿蛔,CPU執(zhí)行時間可忽略不計

小結(jié)

系統(tǒng)層面對于UI流暢度的優(yōu)化措施

  • VSync(解決畫面撕裂)
  • 三重緩沖(提升CPU/GPU利用率)
  • 硬件加速(使用GPU加速畫面渲染)
  • RenderThread(減輕UI線程負(fù)擔(dān))

收集流暢度相關(guān)信息

google為了畫面的流暢度可謂是用心良苦膀曾,作為一個有追求的開發(fā)者,我們當(dāng)然也要朝著如絲般順滑努力阳啥,首先先從數(shù)據(jù)收集開始

開啟GPU呈現(xiàn)模式分析

image.png

這個就是傳說中的玄學(xué)曲線了添谊,可以通過它可視化的直觀掌握當(dāng)前界面是否流暢
綠線是16ms的分界線,每一個豎條代表渲染一幀的耗時察迟,要保證流暢理論上需要每一條都在綠線之下

image.png
image.png

有幾個關(guān)鍵點需要注意一下

  • 表中的顏色跟實際的真機(jī)顏色有一些差別
  • 雖然叫GPU呈現(xiàn)模式分析斩狱,但是表中的所有階段都發(fā)生在CPU中

優(yōu)點:

  • 實時
  • 直觀

缺點:

  • 無法量化

gfxinfo

android 6.0以上設(shè)備使用adb shell dumpsys gfxinfo <PACKAGE_NAME>可以獲取到Aggregate frame stats

Stats since: 752958278148ns
Total frames rendered: 82189
Janky frames: 35335 (42.99%)
90th percentile: 34ms
95th percentile: 42ms
99th percentile: 69ms
Number Missed Vsync: 4706 //垂直同步失敗
Number High input latency: 142  //因為處理輸入耗時
Number Slow UI thread: 17270   //UI線程任務(wù)過重造成的超時
Number Slow bitmap uploads: 1542 //加載bitmap導(dǎo)致的超時
Number Slow draw: 23342 //繪制太慢導(dǎo)致的超時

使用adb shell dumpsys gfxinfo <PACKAGE_NAME> reset可以重置數(shù)據(jù),結(jié)束進(jìn)程不會重置Aggregate frame stats

使用adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats 可以獲取上120幀的詳細(xì)耗時,這個數(shù)據(jù)跟GPU呈現(xiàn)模式的條形圖是對應(yīng)的

image.png

數(shù)據(jù)的單位是納秒扎瓶,通過統(tǒng)計計算可以獲得每一幀的各階段耗時
具體每一列數(shù)據(jù)代表什么可以看下面的鏈接
Framestats data format

優(yōu)點:

  • 數(shù)據(jù)詳細(xì)
  • 有整體的統(tǒng)計

缺點:

  • 不是實時數(shù)據(jù)
  • 只有120幀
  • framestats數(shù)據(jù)需要進(jìn)一步處理

OnFrameMetricsAvailableListener

從 7.0(API 24)開始所踊,安卓 SDK 新增 OnFrameMetricsAvailableListener 接口用于提供幀繪制各階段的耗時燥透,數(shù)據(jù)源與 GPU Profile 相同读处。

    public void startFrameMetrics(View view) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            final String activityName = getClass().getSimpleName();
            listener = new Window.OnFrameMetricsAvailableListener() {

                private int allFrames = 0;
                private int jankyFrames = 0;

                @Override
                public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics,
                        int dropCountSinceLastInvocation) {
                    FrameMetrics frameMetricsCopy = new FrameMetrics(frameMetrics);
                    allFrames++;
                    float totalDurationMs = (float) (0.000001 * frameMetricsCopy.getMetric(
                            FrameMetrics.TOTAL_DURATION));
                    if (totalDurationMs > 17) {
                        jankyFrames++;
                        String msg = String.format("Janky frame detected on %s with total duration: %.2fms\n",
                                activityName, totalDurationMs);
                        float layoutMeasureDurationMs = (float) (0.000001 * frameMetricsCopy.getMetric(
                                FrameMetrics.LAYOUT_MEASURE_DURATION));
                        float drawDurationMs = (float) (0.000001 * frameMetricsCopy.getMetric(
                                FrameMetrics.DRAW_DURATION));
                        float gpuCommandMs = (float) (0.000001 * frameMetricsCopy.getMetric(
                                FrameMetrics.COMMAND_ISSUE_DURATION));
                        float othersMs = totalDurationMs - layoutMeasureDurationMs - drawDurationMs - gpuCommandMs;
                        float jankyPercent = (float) jankyFrames / allFrames * 100;
                        msg += String.format("Layout/measure: %.2fms, draw:%.2fms, gpuCommand:%.2fms others:%.2fms\n",
                                layoutMeasureDurationMs, drawDurationMs, gpuCommandMs, othersMs);
                        msg += "Janky frames: " + jankyFrames + "/" + allFrames + "(" + jankyPercent + "%)"
                                + dropCountSinceLastInvocation;
                        Log.e("FrameMetrics", msg);
                    }
                }
            };
            getWindow().addOnFrameMetricsAvailableListener(listener, new Handler());

        } else {
            Log.w("FrameMetrics", "FrameMetrics can work only with Android SDK 24 (Nougat) and higher");
        }
    }

FrameMetrics中包含了渲染一幀各個階段的耗時數(shù)據(jù)


image.png

優(yōu)點:

  • 實時
  • 數(shù)據(jù)全面
  • 直接給出了每個階段的耗時,不用再算一遍了

缺點:

  • 只支持7.0及以上系統(tǒng)

Choreographer.FrameCallback

這種檢測流暢度的方法起源于FaceBook的一次關(guān)于UI流暢度的技術(shù)分享The Road to 60FPS

Choreographer是一個接收VSync信號并分發(fā)的組件桥氏,Choreographer 收到通知依次處理 Input乍赫、Animation瓣蛀、Draw,這三個過程都是通過 FrameCallback 回調(diào)的方式完成的雷厂。

通過Choreographer.FrameCallback可以獲取到VSync信號開始被處理的時間戳惋增,減去上一個時間戳,可以近似為上一幀的渲染耗時(只計算了UI線程的耗時改鲫,計算不到渲染線程和GPU耗時)

    public void postFrameCallback(View view) {
        lastTime = System.nanoTime();
        Choreographer.getInstance().postFrameCallback(this);
    }

    @Override
    public void doFrame(long frameTimeNanos) {
        //每個FrameCallback都只會回調(diào)一次诈皿,所以需要在回調(diào)中注冊下一幀VSync信號的回調(diào)
        Choreographer.getInstance().postFrameCallback(this);
        long jitterNanos = frameTimeNanos - lastTime;
        if (jitterNanos > FRAME_INTERVAL_NANOS) {
        Log.i(TAG,
                    "doFrame: lastTime:" + lastTime + "   frameTimeNanos:" + frameTimeNanos
                            + "  frame:" + jitterNanos);
        lastTime = frameTimeNanos;

    }

Choreographer.FrameCallback和之前的gfxinfo的數(shù)據(jù)有一些不同

  • Choreographer的回調(diào)是基于VSync信號的林束,而gfxinfo的數(shù)據(jù)是基于每一幀的渲染。
  • 當(dāng)畫面沒有發(fā)生變化稽亏,畫面是不需要重新渲染的壶冒,此時在GPU呈現(xiàn)模式上的條形圖也不會移動,framestats也不會記錄截歉。
  • 而VSync信號總是會發(fā)出來胖腾,所以Choreographer.FrameCallback在畫面沒有重新渲染時也會被回調(diào)到。

優(yōu)點:

  • 實時
  • 基于VSync信號

缺點:

  • 小于VSync信號間隔的渲染時間不知道精確值
  • 數(shù)據(jù)不夠全面瘪松,不知道每個階段的具體耗時
  • 對VSync信號的響應(yīng)只依賴于UI線程空閑與否咸作,渲染線程和GPU的阻塞無法判斷

adb shell dumpsys SurfaceFlinger --latency

這個是網(wǎng)上比較多介紹的獲取數(shù)據(jù)計算fps的方式,但是自己試驗獲取不到有效數(shù)據(jù)宵睦,所以計算fps只能通過gfxinfo中獲取的數(shù)據(jù)了

小結(jié)

流暢度相關(guān)數(shù)據(jù)的收集

  • 每一幀的渲染時間
    • GPU呈現(xiàn)模式(直觀记罚,無法量化)
    • adb shell gfxinfo(可以獲取到總的統(tǒng)計數(shù)據(jù),和最近120幀的詳細(xì)渲染數(shù)據(jù))
  • VSync信號被響應(yīng)的時間
    • Choreographer(無法精確計算每一幀的耗時)

流暢度的性能指標(biāo)

通過上面的介紹壳嚎,我們收集到了兩種與流暢度相關(guān)的核心數(shù)據(jù)

  • 渲染每一幀的耗時
  • VSync信號的響應(yīng)時間點
    那么這些數(shù)據(jù)如何和流暢度對應(yīng)起來呢桐智?

渲染超時的幀數(shù)量

最直觀的數(shù)據(jù)就是每一幀的渲染耗時了,當(dāng)一幀耗時大于1/60s烟馅,即使只超過1ms酵使,這一幀依然會錯過一個VSync,到下一個VSync信號產(chǎn)生時才能顯示到屏幕上焙糟】谟妫看上去這個值是和流暢度緊密相關(guān)的,但是會不會有什么問題呢穿撮?

先來看一個極端情況

image.png

這里每一幀渲染都是超過16ms的缺脉,但是因為三重緩沖和RenderThread的存在,這里是有60fps的悦穿,只不過顯示出現(xiàn)了1/30s的延遲攻礼,并不會讓用戶察覺有什么異樣。

所以單純用單位時間渲染超時的幀數(shù)量來衡量流暢度其實并不太合理栗柒,甚至于google做的那些底層優(yōu)化就是為了讓我們的應(yīng)用在渲染超過16ms時依然有良好的流暢度表現(xiàn)礁扮。

幀率

FPS這是最常用的衡量畫面流暢度的指標(biāo)。

不過在Android系統(tǒng)中用FPS用fps來衡量流暢度卻有些缺陷和不便

  • 首先如果畫面沒有變化瞬沦,其實是沒有畫面刷新的太伊,此時FPS為0,但是這種場景下并沒有發(fā)生卡頓逛钻。
  • 第二在android 7.0以下的系統(tǒng)中只能通過adb shell 來獲取120幀的數(shù)據(jù)僚焦,限制比較大
  • 第三framestats數(shù)據(jù)雖然全面,但是計算比較復(fù)雜
    • 有臟數(shù)據(jù)(flags不為0曙痘,測試這樣的數(shù)據(jù)不多芳悲,影響有限立肘,可以直接剔除)
    • 兩幀之間可能有重疊(三重緩沖和RenderThread)
    • 兩幀之間可能有間隔(畫面不需要更新)
    • 每一幀的時間不確定,按固定幀數(shù)計算FPS誤差比較大名扛,按單位時間計算需要自己處理時間周期

丟幀與流暢度

首先先說明下什么是丟幀谅年,理論上屏幕的刷新率是60hz,加入垂直同步機(jī)制之后肮韧,每秒渲染的畫面上限就是60融蹂,因為某一個VSync信號產(chǎn)生時因為UI線程卡頓或者圖像緩沖全部被占據(jù)等情況導(dǎo)致這一個VSync沒有被響應(yīng),這種情況就是丟幀惹苗。而在畫面沒有更新的情況下沒有新的幀需要渲染殿较,這種情況并不是丟幀耸峭。

注意一下丟幀與渲染超時的區(qū)別

  • 渲染超時出現(xiàn)時桩蓉,這一幀會被顯示在屏幕上,不過會有延遲劳闹;而丟幀發(fā)生時這個周期的UI狀態(tài)不會被顯示到屏幕上
  • 丟幀發(fā)生時一定存在渲染超時院究;而因為RenderThread和三重緩沖機(jī)制的存在,發(fā)生了渲染超時也不一定就會造成丟幀

因為丟幀的計算實際是依賴于對VSync信號的響應(yīng)本涕,自然得用到Choreographer.FrameCallback

    public void postFrameCallback(View view) {
        lastTime = System.nanoTime();
        Choreographer.getInstance().postFrameCallback(this);
        disposable = Flowable.interval(200, TimeUnit.MILLISECONDS)
                .map(new Function<Long, Integer>() {
                    @Override
                    public Integer apply(Long aLong) {
                        Log.i(TAG, "apply: " + aLong);
                        int sm = (frameCount - lastFrameCount) * 1000 / 200;
                        lastFrameCount = frameCount;
                        return sm;
                    }
                })
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer sm) throws Exception {
                        StringBuilder builder = new StringBuilder();
                        builder.append("Smoothness :").append(sm);
                        for (int i = 0; i < skipCount.length; i++) {
                            if (i == 0) {
                                builder.append("  normal: ");
                            } else {
                                builder.append("  skip ").append(i).append(" frames: ");
                            }
                            builder.append(skipCount[i]);

                        }
                        Log.i(TAG, builder.toString());
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {

                    }
                });
    }

    @Override
    public void doFrame(long frameTimeNanos) {
        Choreographer.getInstance().postFrameCallback(this);
        long jitterNanos = frameTimeNanos - lastTime;
        frameCount++;
        //index表示兩個VSync信號之間被忽略的信號數(shù)量业汰,即丟幀
        //FRAME_INTERVAL_NANOS取了17ms,因為取1/60s轉(zhuǎn)換成納秒進(jìn)行比較的話數(shù)值太接近了菩颖,稍有誤差都會比較大的影響結(jié)果
        int index = (int) (jitterNanos / FRAME_INTERVAL_NANOS);
        if (index > 7) {
            index = 7;
        }
        skipCount[index]++;
        lastTime = frameTimeNanos;
    }

這里不僅計算了每秒響應(yīng)的VSync信號數(shù)量作為流暢度(實時數(shù)據(jù))样漆,還記錄了不同連續(xù)丟幀發(fā)生的次數(shù)(可以制定多個維度的數(shù)據(jù)上報)

  • 丟幀超過50%
  • 連續(xù)丟幀2+超過30%
  • 丟幀18+表示發(fā)生了300ms以上的卡頓,說明有場景會造成嚴(yán)重卡頓
    ...

小結(jié)

綜合兼容性晦闰,數(shù)據(jù)與實際場景的契合程度還有計算復(fù)雜度放祟,使用Choreographer統(tǒng)計VSync信號是更為合理的選擇

To Be Continue

本來準(zhǔn)備一篇文章搞定的,但是寫著寫著發(fā)現(xiàn)內(nèi)容越來越多呻右,拆成兩篇感覺更清晰一些跪妥,下一篇會介紹如何去優(yōu)化UI流暢度。

參考資料

Getting To Know Android 4.1, Part 3: Project Butter - How It Works And What It Added
Triple Buffering: Why We Love It
理解 RenderThread
Android硬件加速(二)-RenderThread與OpenGL GPU渲染
Android GPU呈現(xiàn)模式原理及卡頓掉幀淺析
Test UI performance
Choreographer 解析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末声滥,一起剝皮案震驚了整個濱河市眉撵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌落塑,老刑警劉巖纽疟,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異憾赁,居然都是意外死亡仰挣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門缠沈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膘壶,“玉大人错蝴,你說我怎么就攤上這事⊥前牛” “怎么了顷锰?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長亡问。 經(jīng)常有香客問我官紫,道長,這世上最難降的妖魔是什么州藕? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任束世,我火速辦了婚禮,結(jié)果婚禮上床玻,老公的妹妹穿的比我還像新娘毁涉。我一直安慰自己,他們只是感情好锈死,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布贫堰。 她就那樣靜靜地躺著,像睡著了一般待牵。 火紅的嫁衣襯著肌膚如雪其屏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天缨该,我揣著相機(jī)與錄音偎行,去河邊找鬼。 笑死贰拿,一個胖子當(dāng)著我的面吹牛蛤袒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播壮不,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼汗盘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了询一?” 一聲冷哼從身側(cè)響起隐孽,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎健蕊,沒想到半個月后菱阵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缩功,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年晴及,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫡锌。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡虑稼,死狀恐怖琳钉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蛛倦,我是刑警寧澤歌懒,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站溯壶,受9級特大地震影響及皂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜且改,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一验烧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧又跛,春花似錦碍拆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽直砂。三九已至菌仁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間静暂,已是汗流浹背济丘。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留洽蛀,地道東北人摹迷。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像郊供,于是被迫代替她去往敵國和親峡碉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

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