Android 優(yōu)化——內(nèi)存優(yōu)化

Android 優(yōu)化目錄


優(yōu)化的意義

  • 減少 OOM,提高應(yīng)用穩(wěn)定性扯夭。
  • 減少卡頓鳍贾,提高應(yīng)用流暢度。
  • 減少內(nèi)存占用交洗,提高應(yīng)用后臺運行時的存活率骑科。
  • 減少異常發(fā)生,減少代碼邏輯隱患构拳。

垃圾回收

在 GC 的過程中咆爽,其它在工作的線程會暫停,包括負責(zé)繪制的 UI 線程置森,并且在不同區(qū)域的內(nèi)存釋放速度也有一定的差異斗埂,但不管在哪個區(qū)域,都要到這次 GC 內(nèi)存回收完成后凫海,才會繼續(xù)執(zhí)行原來的線程呛凶。

雖然一次消耗性能不大,但如果大量這樣的重復(fù)行贪,就會影響到應(yīng)用的渲染 工作漾稀,造成垃圾回收動作太頻繁。這種情況很容易發(fā)生在短時間內(nèi)申請大量 的對象時建瘫,并且它們在極少的情況下能得到有效的釋放崭捍,這樣會出現(xiàn)內(nèi)存泄漏的情況。

一旦達到了剩余內(nèi)存的閾值啰脚,垃圾回收活動就會啟動殷蛇。即使有時內(nèi)存申請 很小,它們?nèi)匀粫o應(yīng)用程序的堆內(nèi)存造成壓力橄浓,還是會啟動垃圾回收晾咪,在 GC 頻繁的工作過程中消耗了非常多的時間,并且可能導(dǎo)致卡頓贮配。為了避免這樣的情況,設(shè)置一個 16ms 界線塞赂,只要 GC 消耗的時間超過了 16ms 的閾值泪勒,就會有丟幀的情況出現(xiàn)。

分析工具

使用 Memory Profiler 查看 Java 堆和內(nèi)存分配可分析內(nèi)存情況和內(nèi)存泄露。

內(nèi)存泄露

內(nèi)存泄漏就是存在一些被分配的對象圆存,可達但不可用叼旋,用不著了但還有鏈接引用著,導(dǎo)致 GC 無法回收沦辙。會導(dǎo)致內(nèi)存空間不斷減少夫植,最終內(nèi)存耗盡引起 OOM 問題。

分類

  • 資源對象未關(guān)閉

    資源性對象比如 BraodcastReceiver油讯、Cursor详民、File 等、往往都用了一些緩沖陌兑,在不使用的時候沈跨,應(yīng)該及時關(guān)閉它們,以便它們的緩沖及時回收內(nèi)存兔综。

    它們的緩沖不僅存在于 Java 虛擬機內(nèi)饿凛,還存在于 Java 虛擬機外。如果我們僅僅是把它的引用設(shè)置為 null软驰,而不關(guān)閉它們涧窒,往往會造成內(nèi)存泄露。因為有些資源性對象锭亏,比如 SQLiteCursor(在析構(gòu)函數(shù)finalize()纠吴,如果沒有關(guān)閉它,它自己會調(diào) close() 關(guān)閉)贰镣,但是這樣的效率太低呜象。

    對于資源性對象不使用的時候,應(yīng)該立即調(diào)用它的 close() 函數(shù)碑隆,將其關(guān)閉掉恭陡,然后再置為 null。

  • 注冊對象未注銷

    比如廣播上煤、觀察者監(jiān)聽未解除注冊休玩,會導(dǎo)致所在的 Activity 退出后無法釋放,不斷重新進入劫狠,可能造成多個對象一直釋放不掉拴疤。

  • 類的靜態(tài)變量持有大數(shù)據(jù)對象

    靜態(tài)變量長期維持對象的引用,阻止垃圾回收独泞,如果靜態(tài)變量持有大的 數(shù)據(jù)對象呐矾,如 Bitmap 等,就很容易引起內(nèi)存不足等問題懦砂。

    比如 Activity 里創(chuàng)建靜態(tài)的 View蜒犯,而 View 又持有 Activity 對象组橄,導(dǎo)致資源無法釋放。

  • 非靜態(tài)內(nèi)部類的靜態(tài)實例

    非靜態(tài)內(nèi)部類會維持一個到外部類實例的引用罚随,如果非靜態(tài)內(nèi)部類的實例是靜態(tài)的玉工,就會間接長期維持著外部類的引用,阻止被系統(tǒng)回收淘菩。

    比如 AsyncTask 或線程 new Runnable 都會有一個匿名內(nèi)部類遵班,因此它們對當(dāng)前 Activity 都有一個隱式引用,如果 Activity 在銷毀之前任務(wù)還未完成潮改,那么將導(dǎo)致 Activity 的內(nèi)存資源無法回收狭郑,造成內(nèi)存泄漏。

  • 非靜態(tài) Handler

    Handler 通過發(fā)送 Message 與主線程交互进陡,Message 發(fā)出之后存儲在 MessageQueue 中愿阐,有些 Message 不能馬上被處理。

    在 Message 中存在一個 target趾疚,是 Handler 的一個引用缨历,如果 Message 在 Queue 中存在的時間過長,就會導(dǎo)致 Handler 無法被回收糙麦。

    • 如果 Handler 是非靜態(tài)的辛孵,則會導(dǎo)致 Activity 或者 Service 不會被回收。所以 Handler 應(yīng)該定義為靜態(tài)內(nèi)部類赡磅,通過弱引用持有 Activity魄缚。

        ```java
        static class MyHandler extends Handler {  
            WeakReference<Activity> mActivityReference;  
            MyHandler(Activity activity) {  
                mActivityReference = new WeakReference<Activity>(activity);  
            }  
            @Override  
            public void handleMessage(Message msg) {  
                final Activity activity = mActivityReference.get();  
                if (activity != null) {  
                    activity.mImageView.setImageBitmap(mBitmap);  
                }  
            }  
        }  
        ```
      
    • 退出時 mHandler.removeCallbacksAndMessages(null),移除消息隊列中所有消息和所有的 Runnable焚廊。

  • 集合中對象沒清理

    把一些對象的引用加入到了集合中冶匹,當(dāng)不需要該對象時,如果沒有把它的引用從集合中清理掉咆瘟,這樣這個集合就會越來越大嚼隘。如果這個集合是 static 的話,情況就更嚴重袒餐。

  • WebView 泄露

    為 WebView 開啟獨立的一個進程飞蛹,使用 AIDL 與應(yīng)用的主進程通信,WebView 所在的進程可以根據(jù)業(yè)務(wù)的需要選擇合適的時機進行銷毀灸眼,達到正常釋放內(nèi)存的目的卧檐。

  • HandlerThread 沒有主動調(diào)用 quit

    HandlerThread 的 run 方法是一個死循環(huán),它不會自己結(jié)束焰宣。線程的生命周期超過了 Activity 生命周期霉囚,當(dāng)橫豎屏切換,HandlerThread 線程的數(shù)量會隨著 Activity 重建次數(shù)的增加而增加匕积。

    應(yīng)該在 onDestroy 時將線程停止掉:mThread.getLooper().quit()佛嬉,比如 IntentService 里做完任務(wù)自動調(diào)用了 stopSelf逻澳,進而調(diào)用 quit。

  • Bitmap 使用不當(dāng)

    用完 Bitmap 時暖呕,要及時的 recycle 掉。recycle 并不能確定立即就會將 Bitmap 釋放掉苞氮,但是會給虛擬機一個暗示:“該圖片可以釋放了”湾揽。

  • 獲取系統(tǒng)服務(wù)

    用 ApplicationContext 代替 Activity。

檢測函數(shù)庫 LeakCanary

LeakCanary 是 Square 公司的檢測內(nèi)存泄漏的函數(shù)庫笼吟,在 Debug 版本中監(jiān)控 Activity库物、Fragment 等的內(nèi)存泄漏。檢測到內(nèi)存泄漏時會將消息發(fā)到系統(tǒng)通知欄贷帮,點擊后打開 DisplayLeakActivity 的頁面戚揭,顯示泄漏的跟蹤消息,還默認保存了最近的 7 個 dump 文件到 APP 的目錄中撵枢,可以用 MAT 等工具進一步分析民晒。

使用

配置 gradle 文件:

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

只有 Debug 版本使用,Release 和 Test 版本用 no-op 版本锄禽,沒有實際代碼和操作潜必,不會對 APP 體積和性能產(chǎn)生影響。

在 Application 中初始化:

public class ExampleApplication extends Application {
    @Override public void onCreate() {
    super.onCreate();
    if (LeakCanary.isInAnalyzerProcess(this)) {
      // This process is dedicated to LeakCanary for heap analysis.
      // You should not init your app in this process.
      return;
    }
    LeakCanary.install(this);
    // Normal app init code...
  }
}

其中沃但,LeakCanary.install 方法會自動啟動一個 ActivityRefWatcher磁滚,自動監(jiān)控應(yīng)用中調(diào)用 Activity.onDestroy 之后發(fā)生泄漏的 Activity。

如果想監(jiān)控其它的對象宵晚,比如 Fragment垂攘,可以通過 install 方法返回的 RefWatcher 去監(jiān)控。

public class ExampleApplication extends Application {
    @Override public void onCreate() {
    super.onCreate();
    if (LeakCanary.isInAnalyzerProcess(this)) {
      // This process is dedicated to LeakCanary for heap analysis.
      // You should not init your app in this process.
      return;
    }

    refWatcher = LeakCanary.install(this);
    // Normal app init code...
  }

    private RefWatcher refWatcher;

    // get 方法返回 RefWatcher 對象
    public static RefWatcher getRefWatcher(Context context) {
        ExampleApplication application = (ExampleApplication) context.getApplicationContext();
        return application.refWatcher;
    }
}

然后在 Fragment 的 onDestroy 方法中調(diào)用 refWatcher 監(jiān)控

@Override
public void onDestroy() {
    super.onDestroy();
    RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
    refWatcher.watch(this);
}

可以使用 watch 來監(jiān)控任何你認為已經(jīng)銷毀的對象淤刃。

原理

  1. RefWatcher.watch() 為被監(jiān)控對象創(chuàng)建一個 KeyedWeakReference 弱引用對象晒他,它是 WeakReference 的子類,添加鍵值對钝凶,后面會根據(jù)指定 Key 找到弱引用對象仪芒。
  2. 在后臺線程 AndroidWatchExecutor 中,檢查 KeyedWeakReference 弱引用是否被清除耕陷,如果存在則觸發(fā)一次垃圾回收掂名。垃圾回收后,如果弱引用對象依然存在哟沫,說明已經(jīng)內(nèi)存泄漏饺蔑,會將 Heap 內(nèi)存導(dǎo)出到 .hprof 文件中,并將文件放在 APP 的文件目錄中嗜诀。
  3. 在一個獨立的進程中啟動 HeapAnalyzerService 服務(wù)猾警,解析 heap dump 信息孔祸。基于唯一的 reference key发皿,在 heap dump 中找到對應(yīng)的 KeyedWeakReference崔慧,并定位發(fā)生內(nèi)存泄漏的對象引用。HeapAnalyzer 會計算 GC Roots 的最短強引用路徑穴墅,并判斷是否存在泄漏惶室,并構(gòu)建出導(dǎo)致泄漏的對象引用鏈。

定制

RefWatcher 的自定義

由于 Release 版本使用的 leakcanary-android-no-op 庫玄货,若自定義 LeakCanary皇钞,需確保只影響 Debug 版本,因為可能引用到 leakcanary-android-no-op 中沒有的 API松捉。因此需要將 Release 和 Debug 部分的代碼分離夹界。例如定義 ExampleApplication 用于 Release 版本,DebugExampleApplication 用于 Debug 版本隘世,繼承 ExampleApplication可柿。

public class ExampleApplication extends Application {
    public static RefWatcher getRefWatcher(Context context) {
        ExampleRefWatcher application = (ExampleRefWatcher) context.getApplicationContext();
        return application.refWatcher();
    }

    private RefWatcher refWatcher;

    @Override
    public void onCreate() {
        super.onCreate();
        ...
        // 不再是調(diào)用 install 方法
        refWatcher = installLeakCanary();
        ...
    }

    protected RefWatcher installLeakCanary() {
        return RefWatcher.DISABLED;
    }
}

新建 src/debug/java 文件夾,在其中創(chuàng)建 DebugExampleApplication:

// Debug 版本的 Application 類
public class DebugExampleApplication extends ExampleApplication {
    protected RefWatcher installLeakCanary() {
        RefWatcher refWatcher = LeakCanary.install(this);
        return refWatcher;
    }
}

在 src/debug 中新建 AndroidManifest.xml 文件:

<?xml version="1.0 encoding="utf-8" ?>
<manifest ...>
    <application
        tools:replace="android:name"
        android:name=".DebugExampleApplication" />
</manifest>

Gradle 構(gòu)建時以舒,如果是 debug 版本趾痘,會將 src/debug/AndroidManifest.xml 的內(nèi)容合并入 src/main/AndroidManifest.xml 文件中。同時由于使用了 tools:replace屬性蔓钟,所以 android:name 的值 DebugExampleApplication 會替換 ExampleApplication永票。

通知頁面樣式的自定義

內(nèi)存泄漏通知頁面 DisplayLeakActivity 默認的圖標和標簽兩個值,可以進行覆蓋滥沫。

圖標定義在 res 下的 drawable-hdpi/drawable-mdpi/drawable-xhdpi/drawable-xxhdpi/drawable-xxxhdpi 里侣集,名為 __leak_canary_icon.png

標簽定義在:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="__leak_canary_display_activity_label">MyLeaks</string>
</resources>

內(nèi)存泄漏堆棧信息保存?zhèn)€數(shù)的自定義

默認情況下兰绣,DisplayLeakActivity 在 APP 目錄中最多保存 7 個 HeapDump 文件和泄漏堆棧信息世分,可以在 APP 中定義 R.integer.__leak_canary_max_stored_leaks 來修改。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="__leak_canary_max_stored_leaks">20</string>
</resources>

Watcher 的延時

通過定義 R.integer.leak_canary_watch_delay_millis 來修改弱引用對象被認為出現(xiàn)內(nèi)存泄漏的延時時間缀辩,默認 5 秒臭埋,下面修改為 1.5 秒:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="leak_canary_watch_delay_millis">1500</string>

自定義堆棧信息和 heap dump 的處理方式

可以通過繼承 DisplayLeakService 并重寫其中的 afterDefaultHandling 函數(shù)來實現(xiàn)定制化操作,例如將 heap dump 文件發(fā)送到服務(wù)端:

public class LeakUploadService extends DisplayLeakService {
    @Override
    protected void afterDefaultHandling(HeapDump headDump, AnalysisResult result, String leakInfo) {
        if (!result.leakFound || result.excludedLeak) {
            return;
        }
        myServer.uploadLeakBlocking(heapDump.headDumpFile, leakInfo);
    }
}

public class DebugExampleApplication extends ExampleApplication {
    protected RefWatcher installLeakCanary() {
        return LeakCanary.install(app, LeakUploadService.class, AndroidExcludedRefs.createAppDefaults().build());
    }
}

為了使 LeakUploadService 生效臀玄,需要在 AndroidManifest.xml 中注冊瓢阴。

忽略特定的弱引用

實現(xiàn)自己的 ExcludedRefs 忽略某些特定的弱引用對象,不對其進行內(nèi)存泄漏的監(jiān)視健无。

public class DebugExampleApplication extends ExampleApplication {
    protected RefWatcher installLeakCanary() {
        ExcludedRefs excludedRefs = AndroidExcludedRefs.createAppDefaults()
            .instanceField("com.example.Example.class", "exampleField")
            .build();
        return LeakCanary.install(this, DisplayLeakService.class, excludedRefs);
    }
}

不監(jiān)視特定 Activity

默認會監(jiān)視所有 Activity 的內(nèi)存泄漏荣恐,默認只支持 Android 4.0 以上的系統(tǒng),如果 4.0 以下需要在 onDestroy 中主動 watch。

public class DebugExampleApplication extends ExampleApplication {
    @Override
    protected RefWatcher installLeakCanary() {
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return RefWatcher.DISABLED;
        } else {
            ExcludedRefs excludedRefs = AndroidExcludedRefs.createAppDefaults().build();
            LeakCanary.enableDisplayLeakActivity(this);
            ServiceHeapDumpListener heapDumpListener = new ServiceHeapDumpListener(this, DisplayLeakService.class);
            final RefWatcher refWatcher = LeakCanary.androidWathcer(this, heapDumpListener, exlcudedRefs);
            registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
                public void onActivityDestroyed(Activity activity) {
                    if (activity instanceof MainActivyt) { // 排除某些 Activity
                        return;
                    }
                    refWatcher.watch(activity);
                }
            });
            return refWatcher;
        }
    }
}

內(nèi)存優(yōu)化

  • 使用軟/弱/虛引用

  • 使用 ArrayMap 代替 HashMap

  • 使用 SparseArray叠穆,SparseBooleanArray少漆,SparseLongArray 和 SparseIntArray 替換 HashMap,以減少裝箱帶來的內(nèi)存占用硼被,也避免了拆箱示损。

  • @IntDef,@StringDef 代替枚舉

  • zipalign 優(yōu)化 apk

  • 節(jié)制使用 Service

    如果需要使用 Service 來執(zhí)行后臺任務(wù)嚷硫,一定要任務(wù)正在執(zhí)行的時候才啟動 Service屎媳。另外,當(dāng)任務(wù)執(zhí)行完之后去停止 Service 的時候论巍,要小心停止失敗導(dǎo)致內(nèi)存泄漏的情況。

    可以使用 IntentService风响,后臺任務(wù)結(jié)束后會自動停止嘉汰,從而極大程度上避免了 Service 內(nèi)存泄漏的可能性。

  • 當(dāng)界面不可見時釋放內(nèi)存

    Activity 中重寫 onTrimMemory()状勤,當(dāng)處于 TRIM_MEMORY_UI_HIDDEN 這個級別時鞋怀,表明用戶已經(jīng)離開了程序,所有界面都不可見持搜,此時可以進行一些資源釋放操作密似。

    @Override  
    public void onTrimMemory(int level) {  
        super.onTrimMemory(level);  
        switch (level) {  
        case TRIM_MEMORY_UI_HIDDEN:  
            // 釋放資源
            break;  
        }  
    }
    

圖片優(yōu)化

  • 設(shè)置位圖規(guī)格

    ARGB_8888 占用內(nèi)存最高,是系統(tǒng)默認葫盼。

    RGB_565 會損失較多的圖片數(shù)據(jù)残腌,但除了大圖,一般看不出什么區(qū)別贫导。但它不支持 PNG 圖片的透明通道抛猫。

    ARGB_4444 減少一半的數(shù)據(jù),但保留了透明通道孩灯,視覺差異變化較大闺金,一般用于用戶頭像,特別是圓角頭像峰档。

    Aplha_8 主要用于 Alpha 通道模板败匹,相當(dāng)于做一個染色。圖像要渲染兩次讥巡,雖然減少內(nèi)存掀亩,但增加了 繪制的開銷。

    在 Android 的基本文件結(jié)構(gòu)中不支持 PNG尚卫、JPEG 和 WEBP 格式归榕,因此需要通過 inPreferredConfig 參數(shù)來實現(xiàn)不同的位圖規(guī)格

    BitmapFactory.Options options = new BitmapFactory.Options(); 
    options.inPreferredConfig = Bitmap.Config.RGB_565; 
    BitmapFactory.decodeStream(is, null, options);
    
  • 設(shè)置采樣率

    BitmapFactory.Options options = new BitmapFactory.Options(); 
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(getResource(), R.drawable.ic, options);
    int height = options.outHeight;
    int width = options.outWidth;
    String imageType = options.outMimeType;
    options.inSampleSize = 2; 
    options.inJustDecodeBounds = false;
    BitmapFactory.decodeResource(getResource(), R.drawable.ic, options)
    
  • inScaled,inDensity 和 inTargetDensity

    BitmapFactory.Options options = new BitmapFactory.Options(); 
    options.inScaled = true; 
    options.inDensity = srcWidth; 
    options.inTargetDensity = dstWidth; 
    BitmapFactory.decodeStream(is, null, options); 
    

    當(dāng) inScaled 設(shè)為 true 時吱涉,系統(tǒng)會按照現(xiàn)有的密度來劃分目標密度刹泄,通過 派生綻放數(shù)來應(yīng)用到位圖上外里,使用這個方法會重設(shè)圖片大小,并對它應(yīng)用一個新的過濾特石。

    雖然這些方法都非常好用盅蝗,并且減少圖片顯示需要的內(nèi)存,但因為過多的算法姆蘸,導(dǎo)致圖片顯示的過程需要更多的時間開銷墩莫,如果圖片很多的話,就影響到圖片的顯示效果逞敷。

    最好的方案是結(jié)合這兩個方法狂秦,首先使用 inSampleSize 處理圖片,轉(zhuǎn)換為接近目標的 2 次冪推捐,然后用 inDensity 和 inTargetdensy 生成最終想要的準確大小裂问,因為 inSamplesize 會減少像素的數(shù)量,而 基于輸出密度的需要對像素重新過濾牛柒。

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(is, null, options); 
    options.inScaled = true; 
    options.inDensity = options.outWidth; 
    options.inSampleSize = 4; 
    options.inTargetDensity = dstWith * options.inSampleSize; 
    options.inJustDecodeBounds = false; 
    BitmapFactory.decodeStream(is, null, options);
    
  • inBitmap

    Android 3.0(API 11)引進了 BitmapFactory.Options.inBitmap 字段堪簿,設(shè)置該屬性后,當(dāng)使用 了帶有該 Options 參數(shù)的 decode 方法加載內(nèi)容時皮壁,decode 方法會嘗試重用一個已經(jīng)存在的位圖椭更。這意味著位圖內(nèi)存被重用,從而改善性能蛾魄,并且沒有內(nèi)存的分配和釋放過程虑瀑。

    常見的使用方案可以結(jié)合 LruCache 來實現(xiàn),在 LruCache 移除超出 cache size 的圖片時畏腕,暫時緩存 Bitmap 到一個軟引用集合缴川,需要創(chuàng)建新的 Bitmap 時,可以從這個軟引用集合中找到最適合重用的 Bitmap 來重用它的內(nèi)存區(qū)域描馅。

    新申請 Bitmap 與舊的 Bitmap 必須有相同的解碼格式把夸,并且在 Android 4.4 之前,只能重用相同大小的 Bitmap 的內(nèi)存區(qū)域铭污,Android 4.4 后可以重用任何 bitmap 的內(nèi)存區(qū)域恋日。

  • drawable 目錄

    不同的目錄對應(yīng)不同的顯示密度

    目錄名稱 Density
    res/drawable 0
    res/drawable-hdpi 240
    res/drawable-ldpi 120
    res/drawable-mdpi 160
    res/drawable-xhdpi 320
    res/drawable-xxhdpi 480

    加載資源圖片時,會先算出屏幕密度嘹狞,然后再到對應(yīng)的資源目錄下尋找圖片岂膳,如果沒有,則到最近的目錄中尋找磅网。

    比如一張圖片只放在了 res/drawable-mdpi谈截,但當(dāng)前設(shè)備密度是 480,那么系統(tǒng)會將這張圖片放大 3 倍加載到內(nèi)存。

    res/drawable 在不同的設(shè)備下會被替換成不同的密度簸喂,即系統(tǒng)本身的默認密度毙死。

    所以抓不準該放到哪個目錄的圖片,就盡量問設(shè)計人員要高品質(zhì)圖片然后往高密度目錄下放喻鳄,這樣在低密屏上“放大倍數(shù)”是小于 1 的扼倘,在保證畫質(zhì)的前提下,內(nèi)存也是可控的除呵。

    拿不準的圖片再菊,使用 Drawable.createFromStream 替換 getResources().getDrawable 來加載,這樣就可以繞過 Android 的這套默認適配法則颜曾。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纠拔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子泛豪,更是在濱河造成了極大的恐慌绿语,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件候址,死亡現(xiàn)場離奇詭異,居然都是意外死亡种柑,警方通過查閱死者的電腦和手機岗仑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來聚请,“玉大人荠雕,你說我怎么就攤上這事∈簧停” “怎么了炸卑?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長煤傍。 經(jīng)常有香客問我盖文,道長,這世上最難降的妖魔是什么蚯姆? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任五续,我火速辦了婚禮,結(jié)果婚禮上龄恋,老公的妹妹穿的比我還像新娘疙驾。我一直安慰自己,他們只是感情好郭毕,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布它碎。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扳肛。 梳的紋絲不亂的頭發(fā)上傻挂,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音敞峭,去河邊找鬼踊谋。 笑死,一個胖子當(dāng)著我的面吹牛旋讹,可吹牛的內(nèi)容都是我干的殖蚕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼沉迹,長吁一口氣:“原來是場噩夢啊……” “哼睦疫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鞭呕,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蛤育,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后葫松,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓦糕,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年腋么,在試婚紗的時候發(fā)現(xiàn)自己被綠了咕娄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡珊擂,死狀恐怖圣勒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情摧扇,我是刑警寧澤圣贸,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站扛稽,受9級特大地震影響吁峻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜在张,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一锡搜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瞧掺,春花似錦耕餐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽夏跷。三九已至,卻和暖如春明未,著一層夾襖步出監(jiān)牢的瞬間槽华,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工趟妥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留猫态,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓披摄,卻偏偏與公主長得像亲雪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子疚膊,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

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