Android 性能優化

周星馳的電影《功夫》里面借火云邪神之口說出了一句至理名言:“天下武功,唯快不破”。

在移動互聯網時代,同樣如此,如何把握住這個時機,迅速開發出產品,成為至關重要的一環。但是快速開發出來的產品代碼運行的效率怎么樣呢?我們的App 給用戶的體驗如何呢?

我們的App在低端機上經常ANR、閃退、卡頓等

我們的App在其他分辨率上顯示慘不忍睹?

我們的App在不同網絡的情況下如何處理的

我們的App體驗如此之差,導致大量的用戶流失。這些迫使我們認識到性能優化是非常重要,某種程度上甚至超過了新功能的開發。
也驗證了一句話:“別人有的我們也有,而且比他們的要好要快。

UI性能問題分析優化

UI可謂是一個應用的臉,所以每一款應用在開發階段我們的交互、視覺都拼命的想讓它變得自然大方美麗,可是現實總是不盡人意,視覺和交互總會覺得開發做出來的應用用上去感覺不自然,沒有達到他們心目中的自然流暢細節; 用戶要是能夠感覺出來,少則影響心情,多則卸載應用;所以一 個應用的UI顯示性能問題就不得不被開發人員重視

Android系統每隔16ms發出VSYNC信號,觸發對UI進行渲染,那么整個過程如果保證在16ms以內就能達到一個流暢的畫面。如果系統發出了VSYNC信號,而此時無法進行渲染,還在做別的操作,那么就會導致丟幀的現象。這樣的話,繪制就會在下一個16ms的時候才進行繪制,即使只丟一幀,用戶也會發現卡頓的

所謂的卡頓其實是可以量化的,每次是否能夠成功渲染是非常重要的問題,16ms能否完整的做完一次操作直接決定了UI卡頓與否。

這是因為人眼與大腦之間的協作無法感知超過60fps的畫面更新。12fps大概類似手動快速翻動書籍的幀率,這明顯是可以感知到不夠順滑的。24fps使得人眼感知的是連續線性的運動,這其實是歸功于運動模糊的 效果。24fps是電影膠圈通常使用的幀率,因為這個幀率已經足夠支撐大部分電影畫面需要表達的內容,同時能夠最大的減少費用支出。但是要順暢表現絢麗的畫面內容的,此時就需要用到60fps來達到想要的效果,當然超過60fps是沒有必要的

VSync機制就像是一臺轉速固定的發動機(60轉/s)。每一轉會帶動著去做一些UI相關的事情,但不是每一轉都會有工作去做(就像有時在空擋,有時在D檔)。有時候因為各種阻力某一圈工作量比較重超過了16.6ms,那么這臺發動機這秒內就不是60轉了,當然也有可能被其他因素影響,比如給油不足(主線程里干的活太多)等等,就會出現轉速降低的狀況。我們把這個轉速叫做流暢度。

應用UI卡頓常見原因


我們在使用App時會發現有些界面啟動卡頓、動畫不流暢、列表等滑動時也會卡頓,究其原因,很多都是丟幀導致的;通過上面卡頓原理的簡單說明,我們從應用開發的角度往回推理可以得出常見卡頓原因,如下:

  1. 人為在UI線程中做輕微耗時操作,導致UI線程卡頓;

  2. 布局Layout過于復雜,無法在16ms內完成渲染;

  3. View過度繪制,導致某些像素在同一幀時間內被繪制多次,從而使CPU或GPU負載過重;

  4. View頻繁的觸發measure、layout,導致measure、layout累計耗時過多及整個View頻繁的重新渲染;

  5. 內存頻繁觸發GC過多(同一幀中頻繁創建內存),導致暫時阻塞渲染操作;虛擬機在執行GC垃圾回收操作時所有線程(包括UI線程)都需要暫停,當GC垃圾回收完成之后所有線程才能夠繼續執行 也就是說當在16ms內進行渲染等操作時如果剛好遇上大量GC操作則會導致渲染時間明顯不足,也就從而導致了丟幀卡頓問題。
    冗余資源及邏輯等導致加載和執行緩慢;

  6. 臭名昭著的ANR;

可以看見,上面這些導致卡頓的原因都是我們平時開發中非常常見的。有些人可能會覺得自己的應用用著還蠻OK的,其實那是因為你沒進行一些瞬時測試和壓力測試,一旦在這種環境下運行你的App你就會發現很多性能問題。

應用的主UI線程的概念及其重要性是每個Android開發者都應理解。當一個應用啟動,系統會為應用創建一個名為“main”的主線程。這個主線程(也就是UI主線程)主要負責把事件分發給合適的view或者widget。例如,如果你點擊了屏幕上的一個按鈕,UI線程會把點擊時間交給view處理,view接到事件后會設置它的pressed狀態,然后向事件隊列中發送一個invalidate請求。 UI線程會依次讀取隊列并且告訴view去重繪自己。

解決方法

分析UI卡頓我一般都借助工具,通過工具一般都可以直觀的分析出問題原因,從而反推尋求優化方案,具體如下細說各種強大的工具。

  • 使用GPU過繪分析UI過度繪制問題

如果我們粉刷過一個房間或一所房子,就會知道給墻壁涂上顏色需要做大量的工作。假如你還要重新粉刷一次的話,第二次粉刷的顏色會覆蓋住第一次的顏色,第一次的顏色就永遠不可見了,等于你第一次粉刷做的大量工作就完全被浪費掉。

同樣的道理,如果在我們的應用程序中浪費精力去繪制一些東西同樣會產生性能問題。過度繪制這個名詞就是用來描述屏幕上一個像素在1幀中被重繪了多少次

過度繪制其實是一個性能和設計的交叉點。我們在設計上追求很華麗的視覺效果,但一般來說這種視覺效果會采用非常多的層疊組件來實現,這時候就會帶來過度繪制的問題。

過度繪制也許是因為你的UI布局中存在大量重疊的view,但一個更為普遍的情況是因為那些不必要的重疊著的背景。例如某個Activity有一個背景,Layout也有自己的背景,同時它的子View又分別有自己的背景。

設置-開發者選項-調試GPU過度繪制(過度渲染等,不同機器可能不同)

圖片名稱
圖片名稱
圖片名稱
圖片名稱

開啟后,啟動我們的應用,可以看到各種顏色的區域,其中:

圖片名稱
圖片名稱

藍色 1x過度繪制

綠色 2x過度繪制

淡紅色 3x過度繪制

紅色 超過4x過度繪制

最理想的是藍色,一個像素只繪制一次。合格的頁面繪制是白色、藍色為主,顏色越淺越好。

過度繪制產生的原因:

  1. 太多重疊的背景

  2. 太多疊加的View,本來這個UI布局就很復雜或者你是為了追求一個炫麗的視覺效果,這都有可能使得很多view疊加在一起。

  3. 復雜的layout層級

我們可以根據這些原因來查找代碼中存在的問題,并做出適當的修改

  • Hierarchy Viewer

Hierarchy Viewer是隨AndroidSDK發布的工具,位置在tools文件夾下,名為hierarchyviewer.bat。它是Android自帶的非常有用而且使用簡單的工具,可以幫助我們更好地檢視和設計用戶界面(UI),從可視化的角度直觀地獲得UI布局設計結構和各種屬性的信息,幫助我們優化布局設計;

hierarchy_view

一個Activity的View樹,通過這個樹可以分析出View嵌套的冗余層級
Hierarchy Viewer是隨AndroidSDK發布的工具,位置在tools文件夾下,名為hierarchyviewer.bat。它是Android自帶的非常有用而且使用簡單的工具,可以幫助我們更好地檢視和設計用戶界面(UI),從可視化的角度直觀地獲得UI布局設計結構和各種屬性的信息,幫助我們優化布局設計;

hierarchy_view1

類似上圖可以很方便的查看到當前View的許多信息;上圖最底那三個彩色原點代表了當前View的性能指標,從左到右依次代表測量、布局、繪制的渲染時間,紅色和黃色的點代表速度渲染較慢的View(當然了,有些時候較慢不代表有問題,譬如ViewGroup子節點越多、結構越復雜,性能就越差)

  • lint

作為移動應用開發者,我們總希望發布的apk文件越小越好,不希望資源文件沒有用到的圖片資源也被打包進apk,不希望應用中使用了高于minSdk的api,也不希望AndroidManifest文件存在異常,lint就能解決我們的這些問題。靜態代碼分析工具,無需運行,無需測試用例 掃描整個項目,分析以下潛在的問題,分類指出問題描述、問題位置,并提供合理的修改建議

Android lint是在ADT 16提供的新工具,它是一個代碼掃描工具,能夠幫助我們識別代碼結構存在的問題,主要包括:

1)性能 布局性能(以前是 layoutopt工具,可以解決無用布局、嵌套太多、布局太多、overdraw) 其他性能(如:draw/layout 時進行對象的聲明等)

2)未使用到資源、資源缺少(不同資源的適配)

3)有更高性能的資源替換 ---- eg:SparseBooleanArray SparseIntArray

4)國際化問題(硬編碼)

5)圖標的問題(重復的圖標,錯誤的大?。?/p>

6)可用性問題(如不指定的文本字段的輸入型)

7)manifest文件的錯誤 -- 未注冊activity service等等

8)內存泄露 --- 如:handle的不當使用 。

9)占內存的資源及時回收 --- 如:TypedArray未回收資源等

如何使用lint檢索我們代碼中存在的問題呢?

在編輯窗口郵件調出菜單選項

lin3
lin3

然后彈出選擇檢測目標project/module/files等

lint1
lint1

通過lint我們可以直觀的看到我們代碼中存在的不易被發現的問題,
下方的視圖是檢索出我的代碼中存在問題的列表,點擊后可以在右邊視圖展示問題詳細信息,以及給出我們一些建議

lint4
lint4
lint4
lint4
lint4
lint4
  • 布局優化建議

布局優化的一些建議

  1. 首先刪除布局中無用的控件和層級,其次有選擇的使用性能較低的ViewGroup,比如RelativeLayout.如果布局中既可以使用LinearLayout也可以使用RelativeLayout,那么就采用LinearLayout,這是因為RelativeLayout的功能比較復雜,它的布局過程需要花費更多的CPU時間.

  2. 布局優化的另外一種手段是抽象布局標簽
    采用<include>標簽,<merge>標簽和,<include>標簽主要用于布局的重用, ,<merge>標簽一般和<include>配合使用,它可以降低減少布局的層級, 某布局作為子布局被其他布局include時,使用merge當作該布局的頂節點,這樣在被引入時頂結點會自動被忽略,而將其子節點全部合并到主布局中。

  3. 去除不必要的嵌套和View節點
    首次不需要使用的節點設置為GONE或使用viewstub
    viewstub標簽同include標簽一樣可以用來引入一個外部布局,不同的是,viewstub引入的布局默認不會擴張,即既不會占用顯示也不會占用位置,按需加載,當需需要時才會將ViewStup中的布局加載到內存,這提高了程序的初始功率.
    從而在解析layout時節省cpu和內存。viewstub常用來引入那些默認不會顯示,只在特殊情況下顯示的布局,如進度布局、網絡失敗顯示的刷新布局、信息出錯出現的提示布局等。

  4. 減少不必要的infalte
    對于inflate的布局可以直接緩存,用全局變量代替局部變量,避免下次需再次inflate

  5. 用SurfaceView或TextureView代替普通View
    SurfaceView或TextureView可以通過將繪圖操作移動到另一個單獨線程上提高性能。
    普通View的繪制過程都是在主線程(UI線程)中完成,如果某些繪圖操作影響性能就不好優化了,這時我們可以考慮使用SurfaceView和TextureView,他們的繪圖操作發生在UI線程之外的另一個線程上。
    因為SurfaceView在常規視圖系統之外,所以無法像常規試圖一樣移動、縮放或旋轉一個SurfaceView。TextureView是Android4.0引入的,除了與SurfaceView一樣在單獨線程繪制外,還可以像常規視圖一樣被改變。

通過上面UI性能的原理、原因、工具分析總結可以發現,我們在開發應用時一定要時刻重視性能問題,如若真的沒留意出現了性能問題,不妨使用上面的一些案例方式進行分析。但是那終歸是補救措施,在我們知道上面UI卡頓原理之后我們應該盡量從項目代碼架編寫時就避免一些UI性能問題,

當然了,上面只是列出了我們項目中常見的一些UI性能注意事項而已,相信還有很多其他的情況這里沒有說到,歡迎補充。還有一點就是我們上面所謂的UI性能優化分析總結等都是建議性的,因為性能這個問題是一個涉及面很廣很泛的問題,有些優化不是必需的,有些優化是必需的,有些優化掉以后又是得不償失的,所以我們一般著手解決那些必須的就可以了。

Memory內存性能問題

說完了應用開發中的UI性能問題后我們就該來關注應用開發中的另一個重要的性能問題了,那就是內存性能優化分析。Android其實就是嵌入式設備,嵌入式設備核心關注點之一就是內存資源;有人說現在的設備都在硬件配置已經很厲害了,所以內存不會再像以前那么緊張了,其實這句話聽著沒錯,但為啥再牛逼配置的Android設備還是越用系統越卡呢?

大家先想一個問題,假設有一個內存為1G的Android設備,上面運行了一個非常非常吃內存的應用,如果沒有任何機制的情況下是不是用著用著整個設備會因為我們這個應用把1G內存吃光然后整個系統運行癱瘓呢

為了能夠使得Android應用程序安全且快速的運行,Android 的每個應用程序都會使用一個專有的Dalvik虛擬機實例來運行,也就是說每個應用程序都是在屬于自己的進程中運行的。一方面,如果程序在運行過程中出現了內存溢出的問題,僅僅會使得自己的進程被殺掉,而不會影響其他進程(如果是system_process 等系統進程出問題的話,則會引起系統重啟)。另一方面Android為這些進程分配了內存使用上限,如果應用進程使用的內存超過了這個上限, 就會被殺掉。

Android把這些進程都保留在內存中,直到系統需要更多內存時才選擇性的釋放一些,保留在內存中是當再次啟動這些保留在內存的進程時可以明顯提高啟動速度,不需要再去加載。在Android系統中框架會定義如下幾類進程、在系統內存達到規定的不同level閾值時觸發清空不同level的進程類型。

Android內存泄露性能分析

千里之堤, 毀于蟻穴

在Android開發過程中,最為讓我們頭疼的就是內存的泄露問題了,很可能你很小的一個錯誤都會引起內存的泄露,

一些對象有著有限的生命周期。當這些對象所要做的事情完成了,我們希望他們會被回收掉。但是如果這個對象被超過自己生命周期以外的對象強引用,那么在我們期待這個對象生命周期結束的時候被收回的時候,它是不會被回收的。它還會占用內存,這就造成了內存泄露。持續累加,內存很快被耗盡。

在Java中有些對象的生命周期是有限的,當它們完成了特定的邏輯后將會被垃圾回收;

但是,如果在對象的生命周期本來該被垃圾回收時這個對象還被別的對象所持有引用,那就會導致內存泄漏;這樣的后果就是隨著我們的應用被長時間使用,他所占用的內存越來越大。

造成內存泄露泄露的最核心原理就是一個對象持有了超過自己生命周期以外的對象強引用導致該對象無法被正常垃圾回收;可以發現,應用內存泄露是個相當棘手重要的問題,我們必須重視。

  • Android應用開發規避內存泄露建議

關于規避內存泄露我在下面列出了我在項目中經常遇見的一些情況,肯定不全面,歡迎補充!

  1. Activity中生成的對象原則上是應該在Activity生命周期結束之后就釋放的。Activity對象本身也是,所以應該盡量避免有appliction進程級別的對象來引用Activity級別的對象,如果有的話也應該在Activity結束的時候解引用。如不應用applicationContext在Activity中獲取資源。

  2. 線程未終止造成的內存泄露;譬如在Activity中關聯了一個生命周期超過Activity的Thread,在退出Activity時切記結束線程。一個典型的例子就是HandlerThread的run方法是一個死循環,它不會自己結束,線程的生命周期超過了Activity生命周期,我們必須手動在Activity的銷毀方法中中調運thread.getLooper().quit();才不會泄露。

  3. 對象的注冊與反注冊沒有成對出現造成的內存泄露;BroadCastReceiver、Service 解綁。

  4. 創建與關閉沒有成對出現造成的泄露;譬如Cursor資源必須手動關閉,WebView必須手動銷毀,I/O等對象必須手動關閉等。

  5. 不要在執行頻率很高的方法或者循環中創建對象,如onDraw中創建新的局部對象。

  6. 避免代碼設計模式的錯誤造成內存泄露;譬如循環引用,A持有B,B持有C,C持有A,這樣的設計誰都得不到釋放。

  7. 屬性動畫導致的內存泄露從Android 3.0開始,Google提供了屬性動畫,屬性動畫中有一類無限循環的動畫,如果在Activity中播放此類動畫且沒有在onDestroy中去停止動畫,那么動畫會一直播放下去,盡管已經無法在界面上看到動畫效果了,并且這個時候Activity的View會被動畫持有,而View又持有了Activity,最終Activity無法被釋放.

內存泄露察覺工具

知道了內存泄露的概念之后肯定就是想辦法來確認自己的項目是否存在內存泄露了,那該如何察覺自己項目是否存在內存泄露呢?

排查內存泄露是一個全手工的過程

以下幾個關鍵步驟:

重現問題。為了重現問題,機型非常重要,因為一些問題只在特定的設備上會出現。為了找到特定的機型,你需要想盡一切辦法,你可能需要去買,去借,。 當然,為了確定復現步驟,你需要一遍一遍地去嘗試。一切都是非常原始和粗暴的。

在發生內存泄露的時候,把內存 Dump 出來。具體看這里。然后,你需要在 MAT 的內存分析工具中反復查看,找到那些原本該被回收掉的對象。

計算這個對象到 GC roots 的最短強引用路徑。

確定引用路徑中的哪個引用是不該有的,然后修復問題。

很復雜對吧?

如果有一個類庫能在發生 OOM 之前把這些事情全部都搞定,然后你只要修復這些問題就好了,豈不妙哉!

直白的展現Android中的內存泄露

LeakCanary 是一個檢測內存泄露的開源類庫。我們可以在debug 包種輕松檢測內存泄露。

只需幾行代碼,LeakCanary就能自動檢測Activity的泄漏:比如我們在android studio中可以直接引入這個類庫

當存在內存泄漏時,會有一個通知和良好的展示界面:

圖片名稱
圖片名稱
圖片名稱
圖片名稱
圖片名稱
圖片名稱

如何使用:

在 build.gradle 中加入引用,不同的編譯使用不同的引用:

dependencies {
    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
}

在 Application 中:

public class ExampleApplication extends Application {

    @Override public void onCreate() {
        super.onCreate();
        LeakCanary.install(this);
    }
}

這樣,就萬事俱備了! 在 debug build 中,如果檢測到某個 activity 有內存泄露,LeakCanary 就是自動地顯示一個通知。

Android內存溢出OOM

上面我們探討了Android內存管理和應用開發中的內存泄露問題,可以知道內存泄露一般影響就是導致應用卡頓,但是極端的影響是使應用掛掉。前面也提到過應用的內存分配是有一個閾值的,超過閾值就會出問題,這里我們就來看看這個問題—–內存溢出(OOM–OutOfMemoryError)。

OOM1
OOM1

內存溢出的主要導致原因有兩類:

應用代碼存在內存泄露,長時間積累無法釋放導致OOM;

應用的某些邏輯操作瘋狂的消耗掉大量內存(譬如加載一張不經過處理的超大超高清圖片等)導致超過閾值OOM;

可以發現,無論哪種類型,導致內存溢出(OutOfMemoryError)的核心原因就是應用的內存超過閾值了。

OOM2
OOM2
  • Android應用規避內存溢出OOM建議

還是那句話,等待OOM發生是為時已晚的事,我們應該將其扼殺于萌芽之中,至于如何在開發中規避OOM,如下給出一些我們應用開發中的常用的策略建議:

  1. 時刻記得不要加載過大的Bitmap對象;譬如對于類似圖片加載我們要通過BitmapFactory.Options,對相關參數進行配置來減少加載的像素,設置圖片的一些采樣比率和復用等,在BitmapFactory.Options中指定inSampleSize參數,這將表明一旦加載時結果Bitmap圖像所占的比例。例如,在這里將inSampleSize設置為4,這會產生一幅大小是原始圖像大小1/4的圖像

  2. 優化界面交互過程中頻繁的內存使用;譬如在列表等操作中只加載可見區域的Bitmap、滑動時不加載、停止滑動后再開始加載。

  3. 避免各種內存泄露的存在導致OOM。

  4. 對批量加載等操作進行緩存設計,譬如列表圖片顯示,Adapter的convertView緩存等。

  5. 盡可能的復用資源;譬如系統本身有很多字符串、顏色、圖片、動畫、樣式以及簡單布局等資源可供我們直接使用,我們自己也要盡量復用style等資源達到節約內存。

  6. 對于有緩存等存在的應用盡量實現onLowMemory()和onTrimMemory()方法來需要的時候釋放緩存。

盡量使用線程池替代多線程操作,這樣可以節約內存及CPU占用率。

盡量管理好自己的Service、Thread等后臺的生命周期,不要浪費內存占用。

盡量在做一些大內存分配等可疑內存操作時進行try catch操作,避免不必要的應用閃退。

盡量的優化自己的代碼,減少冗余,避免類加載時浪費內存。

可以發現,上面只是列出了我們開發中常見的導致OOM異常的一些規避原則,還有很多相信還沒有列出來,大家可以自行追加參考即可。

  • Android應用OnTrimMemory()實現性能建議

OnTrimMemory是Android 4.0之后加入的一個回調方法,作用是通知應用在不同的情況下進行自身的內存釋放,以避免被系統直接殺掉,提高應用程序的用戶體驗。系統會根據當前不同等級的內存使用情況調用這個方法,并且傳入當前內存等級,這個等級有很多種,我們可以依據情況實現不同的等級.

可以實現OnTrimMemory方法的系統組件有Application、Activity、Fragement、Service、ContentProvider;

關于OnTrimMemory釋放哪些內存其實在架構階段就要考慮清楚哪些對象是要常駐內存的,哪些是伴隨組件周期存在的,一般需要釋放的都是緩存。

OnLowMemory是Android提供的API,在系統內存不足,所有后臺程序(優先級為background的進程,不是指后臺運行的進程)都被殺死時,系統會調用OnLowMemory。

TRIM_MEMORY_COMPLETE:內存不足,并且該進程在后臺進程列表最后一個,馬上就要被清理

TRIM_MEMORY_MODERATE:內存不足,并且該進程在后臺進程列表的中部。

TRIM_MEMORY_BACKGROUND:內存不足,并且該進程是后臺進程。

TRIM_MEMORY_UI_HIDDEN:內存不足,并且該進程的UI已經不可見了。

以上4個是4.0增加

TRIM_MEMORY_RUNNING_CRITICAL:內存不足(后臺進程不足3個),并且該進程優先級比較高,需要清理內存

TRIM_MEMORY_RUNNING_LOW:內存不足(后臺進程不足5個),并且該進程優先級比較高,需要清理內存

TRIM_MEMORY_RUNNING_MODERATE:內存不足(后臺進程超過5個),并且該進程優先級比較高,需要清理內存

無論是什么電子設備的開發,內存問題永遠都是一個很深奧、無底洞的話題,上面的這些內存分析建議也單單只是Android應用開發中一些常見的場景而已,真正的達到合理的優化還是需要很多知識和功底的。

合理的應用架構設計、設計風格選擇、開源庫選擇、代碼邏輯規范等都會決定到應用的內存性能,我們必須時刻頭腦清醒的意識到這些問題潛在的風險與優劣,因為內存優化必須要有一個度,不能一味的優化,亦不能置之不理。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容