是時(shí)候開始重構(gòu)Android Http網(wǎng)絡(luò)層框架了(一個(gè)現(xiàn)代化的Http網(wǎng)絡(luò)框架源碼分析)

橫觀歷史

image.png

一點(diǎn)感概

記得當(dāng)年剛?cè)胄蠥ndroid迅皇,讓我記憶猶新的框架android-async-http昧辽,當(dāng)時(shí)用的不亦樂乎,隨著時(shí)間的變遷登颓,官方的新寵Volley誕生搅荞,不久的不久官方宣布自己放棄,坑爹框咙,Android 4.4后取具,HttpURLConnection底層實(shí)現(xiàn)改為OkHttp,隨即OkHttp是各個(gè)大牛封裝的根基扁耐,Retrofit最為知名暇检,可以說幾乎沒有人沒用過,后來不知道誰刮起了RxJava大風(fēng)婉称,變成了Retrofit+RxJava+OkHttp块仆,到目前為止我都很反感RxJava,框架固然很好王暗,但我們不合適悔据,不愛就是不愛,沒必要牽強(qiáng)俗壹。自從有了Coroutines協(xié)程科汗,算是找到了最優(yōu)解,為什么這么說呢绷雏?我們先來分析下這幾種實(shí)現(xiàn)方式

android-async-http

基于Apache的HttpClient庫構(gòu)建的Android異步網(wǎng)絡(luò)框架头滔,大致用法如下:

 private AsyncHttpClient asyncHttpClient = new AsyncHttpClient() {

        @Override
        protected AsyncHttpRequest newAsyncHttpRequest(DefaultHttpClient client, HttpContext httpContext, HttpUriRequest uriRequest, String contentType, ResponseHandlerInterface responseHandler, Context context) {
            AsyncHttpRequest httpRequest = getHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context);
            return httpRequest == null
                    ? super.newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context)
                    : httpRequest;
        }
    };

  @Override
    public RequestHandle executeSample(AsyncHttpClient client, String URL, Header[] headers, HttpEntity entity, ResponseHandlerInterface responseHandler) {
        return client.get(this, URL, headers, null, responseHandler);
    }

    @Override
    public String getDefaultURL() {
        return "https://httpbin.org/get";
    }

 @Override
    public ResponseHandlerInterface getResponseHandler() {
        return new AsyncHttpResponseHandler() {

            @Override
            public void onStart() {
                clearOutputs();
            }

            @Override
            public void onSuccess(int statusCode, Header[] headers, byte[] response) {
                debugHeaders(LOG_TAG, headers);
                debugStatusCode(LOG_TAG, statusCode);
                debugResponse(LOG_TAG, new String(response));
            }

            @Override
            public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
                debugHeaders(LOG_TAG, headers);
                debugStatusCode(LOG_TAG, statusCode);
                debugThrowable(LOG_TAG, e);
                if (errorResponse != null) {
                    debugResponse(LOG_TAG, new String(errorResponse));
                }
            }

            @Override
            public void onRetry(int retryNo) {
                Toast.makeText(GetSample.this,
                        String.format(Locale.US, "Request is retried, retry no. %d", retryNo),
                        Toast.LENGTH_SHORT)
                        .show();
            }
        };
    }

我們暫且不提現(xiàn)在官方現(xiàn)在已經(jīng)不用HttpClient,框架本身有很多可以借鑒的優(yōu)秀設(shè)計(jì)涎显,放在當(dāng)初可謂是功能豐富坤检,非常穩(wěn)定,且bug極少期吓。我少啰嗦幾句早歇,直接看下一個(gè)實(shí)現(xiàn),最終我們?cè)俸暧^的看看,到底網(wǎng)絡(luò)框架的前世今生是什么走向箭跳。

VolleyPlus

VolleyPlus庫對(duì)Volley進(jìn)行的項(xiàng)目改進(jìn)以及完整的圖像緩存晨另,涉及使用RequestQueue,RequestTickle和Request

RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());

StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        ....
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        ....
    }
});

mRequestQueue.add(stringRequest);

比起AsyncHttpClient谱姓,加入了請(qǐng)求隊(duì)列(AsyncHttpClient也有線程池)拯刁,線程調(diào)度,緩存DiskLruCache逝段,支持的緩存類型:

  • 網(wǎng)絡(luò)緩存
  • 資源緩存
  • 文件緩存
  • 視頻緩存
  • 內(nèi)容URI緩存

等等吧垛玻,也是給我們網(wǎng)絡(luò)層提供了不少的遍歷,接下來看看Retrofit+RxJava

Retrofit+RxJava

public interface ApiService {
    @GET("demo")
    Observable<Demo> getDemo(@Query("start") int start, @Query("count") int count);
}
        // 使用例子
        apiService.getDemo(0, 10)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Demo>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        Log.d(TAG, "onSubscribe: ");
                    }

                    @Override
                    public void onNext(Demo demo) {
                        Log.d(TAG, "onNext: " + demo.getTitle());
                        List<Subjects> list = demo.getSubjects();
                        for (Subjects sub : list) {
                            Log.d(TAG, "onNext: " + sub.getId() + "," + sub.getYear() + "," + sub.getTitle());
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.e(TAG, "onError: " + e.getMessage());
                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "onComplete: Over!");
                    }
                });

這種似乎是很多很多App目前的使用方式奶躯,毋庸置疑帚桩,它解決了開發(fā)中網(wǎng)絡(luò)層的很多問題,那為什么它會(huì)這么火呢嘹黔?它背后的本質(zhì)是什么账嚎?我這里啰嗦幾句哈,大家應(yīng)該都聽說過后端的開發(fā)框架Spring儡蔓,說到Spring郭蕉,你肯定會(huì)想到它設(shè)計(jì)的兩個(gè)核心概念,IOC控制反轉(zhuǎn)喂江,AOP面向切面編程召锈,Retrofit本質(zhì)上用IOC控制反轉(zhuǎn),你只需要定義接口获询,對(duì)象由框架本身負(fù)責(zé)管理涨岁,接口有個(gè)特點(diǎn)就是不變,利用IOC使得你不得不定義接口吉嚣,這樣間接提高代碼的穩(wěn)定性梢薪,是不是很佩服大佬的思想,在這感謝這些大佬們的努力尝哆,讓我們慢慢養(yǎng)成一個(gè)好的編程習(xí)慣秉撇,當(dāng)然我們也更應(yīng)該關(guān)注他們?cè)O(shè)計(jì)的思想,這樣我們?cè)谧约鹤隹蚣艿臅r(shí)候秋泄,是不是可以借鑒(copy)一下呢琐馆。好了,今天的主角上場(chǎng)了印衔,來看下最新的實(shí)現(xiàn)

Net(okhttp+coroutines+lifecycle)

Get

        scopeNetLife {
            // 該請(qǐng)求是錯(cuò)誤的路徑會(huì)在控制臺(tái)打印出錯(cuò)誤信息
            Get<String>("error").await()
        }

Post

        scopeDialog {
            tv_fragment.text = Post<String>("dialog") {
                param("u_name", "drake")
                param("pwd", "123456")
            }.await()
        }

這么簡(jiǎn)單的實(shí)現(xiàn)蘊(yùn)含了什么啡捶?

  1. 生命周期監(jiān)控
  2. 加載中顯示框
  3. 自動(dòng)序列化
  4. 異常處理
  5. 協(xié)程同步
  6. 非線程阻塞

優(yōu)勢(shì)很明顯,代碼的確很簡(jiǎn)潔奸焙,讓他們閱讀代碼不吃力,功能更完善,無需切換線程与帆,消除類似Rxjava的observeOn了赌,subscribe匿名內(nèi)部類的模板代碼,讓人更加注重業(yè)務(wù)邏輯的編寫玄糟。想必這就是我認(rèn)為的最優(yōu)解的一些理由吧勿她。你肯定也會(huì)說Rxjava也有這些功能(如:lifecycle),是的阵翎,你想怎么用是你的權(quán)利逢并,我不能左右。我只想說

郭卫,你餓了嗎砍聊?哈哈
50555D97-51B2-4722-B334-55B80055E84D-6692-000003D06E6D4B5B.jpg

小結(jié)

經(jīng)過幾個(gè)版本的代碼對(duì)比,你是不是已經(jīng)察覺到贰军,網(wǎng)絡(luò)的前世今生玻蝌,讓我們一起總結(jié)下進(jìn)化的特點(diǎn)

  1. 簡(jiǎn)單化
  2. 生命感知力
  3. 更輕量級(jí)的調(diào)度
  4. 自我管理
  5. 高內(nèi)聚(隱藏實(shí)現(xiàn)細(xì)節(jié))
  6. 高可定制

這就是我們網(wǎng)絡(luò)框架的今生,你不重構(gòu)一下嗎词疼?親俯树。

老師說經(jīng)常教我們知其然知其所以然,那么我就帶你走進(jìn)Net框架實(shí)現(xiàn)的底層贰盗,讓我們看看作者都做了些什么的封裝许饿。

源碼分析

首先來看下項(xiàng)目的目錄結(jié)構(gòu)

image.png

項(xiàng)目主要分為兩個(gè)Lib

Kalle的源碼我們后期再分析米辐,雖然已經(jīng)一年零兩個(gè)月未維護(hù)了,但畢竟大佬做的书释,有很多可以學(xué)習(xí)的地方翘贮,這期我們針對(duì)強(qiáng)哥的框架Net做深入的分析,由于本人最近兩年一直在做Rom相關(guān)爆惧,對(duì)Jetpack相關(guān)的使用也不是特別的熟悉狸页,有什么不對(duì)的還請(qǐng)各位手下留情,強(qiáng)哥說他還對(duì)Kalle的源碼做了變更扯再,所以才拿到項(xiàng)目里維護(hù)芍耘,后期還會(huì)升級(jí)一下okhttp的版本,因?yàn)閅anZhenjie沒有升級(jí)最新的okhttp熄阻,當(dāng)然這不是我們的重點(diǎn)斋竞,下面我們來開始分析源碼

再來看下強(qiáng)哥總結(jié)的框架特性

  • 協(xié)程
  • 并發(fā)網(wǎng)絡(luò)請(qǐng)求
  • 串行網(wǎng)絡(luò)請(qǐng)求
  • 切換線程
  • DSL編程
  • 方便的緩存處理
  • 自動(dòng)錯(cuò)誤信息吐司
  • 自動(dòng)異常捕獲
  • 自動(dòng)日志打印異常
  • 自動(dòng)JSON解析
  • 自動(dòng)處理下拉刷新和上拉加載
  • 自動(dòng)處理分頁加載
  • 自動(dòng)缺省頁
  • 自動(dòng)處理生命周期
  • 自動(dòng)處理加載對(duì)話框
  • 協(xié)程作用域支持錯(cuò)誤和結(jié)束回調(diào)
  • 支持先強(qiáng)制讀取緩存后網(wǎng)絡(luò)請(qǐng)求二次刷新
  • 附帶超強(qiáng)輪循器

最近強(qiáng)哥還加了個(gè)優(yōu)先隊(duì)列,同時(shí)發(fā)起10個(gè)請(qǐng)求秃殉,優(yōu)先取第一個(gè)回來的結(jié)果坝初,而且代碼極其簡(jiǎn)潔好用浸剩。

Net源碼目錄

image.png

22個(gè)文件,不多不少鳄袍,剛剛好绢要,整體看目錄,根本不用深入看代碼拗小,我們就能清晰的知道這是干什么的重罪,這就是一種好的目錄結(jié)構(gòu),值得學(xué)習(xí)

  • connect 真實(shí)的鏈接對(duì)象
  • convert 序列化
  • error 異常定義
  • scope 作用域
  • tag 日志tag
  • time 時(shí)間相關(guān)(結(jié)合它的特性哀九,輪訓(xùn)應(yīng)該在這里有相關(guān)實(shí)現(xiàn))
  • utils 工具類
  • Net.kt 網(wǎng)絡(luò)請(qǐng)求的核心實(shí)現(xiàn)
  • NetConfig.kt 全局配置文件

類圖

一張清晰可見的類圖剿配,是分析一個(gè)源碼有利的手段,隨我將其源碼類圖搞個(gè)明白阅束,請(qǐng)看下圖

Net類圖.jpg

通過類圖呼胚,我們可以清晰看到,該框架源碼的類結(jié)構(gòu)围俘,我大致分以下模塊砸讳,好統(tǒng)一分析

  • kalle擴(kuò)展支持部分:ConnectFactory、Converter界牡、NetException
  • 作用域部分:CoroutineScope結(jié)合Android Lifecycle的擴(kuò)展簿寂,以及Sate、Page宿亡、Dialog對(duì)NetCoroutineScope的實(shí)現(xiàn)
  • 動(dòng)態(tài)擴(kuò)展部分常遂,這是框架的核心:Net,組合協(xié)程挽荠、Kalle實(shí)現(xiàn)新的網(wǎng)絡(luò)框架
  • 配置克胳、工具部分:NetConfig、Utils(省略未畫)圈匆、Interval

接下來漠另,詳細(xì)看下各個(gè)模塊的實(shí)現(xiàn)

kalle擴(kuò)展支持部分

ConnectFactory只有一個(gè)功能:就是創(chuàng)建一個(gè)Connection對(duì)象,參數(shù)是Request跃赚。這里為什么這樣做呢笆搓?其實(shí)是為了能兼容不同的URLConnection,強(qiáng)哥使用的okhttp纬傲,自然使用HttpClient

image.png

Converter就是大家熟悉的Response處理满败,這里有個(gè)巧妙的設(shè)計(jì),將接口返回的數(shù)據(jù)叹括,通過傳入的code作為key算墨,默認(rèn)是code,取到業(yè)務(wù)定義的code碼汁雷,與傳入的success參數(shù)對(duì)比净嘀,如果一樣就是成功报咳,然后parsebody,如果返回其他面粮,則拋出失敗的結(jié)果少孝。

image.png

NetException 異常對(duì)于一個(gè)框架來說幾乎不可避免继低,自定義的異常信息熬苍,有利于問題的歸類和追蹤。上圖中就是作者自定義的RequestParamsException請(qǐng)求參數(shù)異常袁翁、ServerResponseException服務(wù)器異常柴底,這個(gè)代碼就不用看了,大家肯定是明白了粱胜,下面來看作用域部分

作用域部分

作用域都繼承自CoroutineScope柄驻,CoroutineScope持有CoroutineContext上下文,其實(shí)不難理解焙压,就像Android的Context鸿脓,分別有Applicaition Context和Activity Context,其實(shí)就是標(biāo)示出不同的生命周期涯曲,那么順著這個(gè)理解就好說了野哭,作者抽象AndroidScope,其實(shí)就是為了監(jiān)聽Lifecycle的生命周期幻件,然后在默認(rèn)的ON_DESTROY函數(shù)回調(diào)中cancel掉協(xié)程拨黔,如下圖代碼所示

image.png
image.png

看到了吧,繼承CoroutineScope需要實(shí)現(xiàn)CoroutineContext對(duì)吧绰沥,那這里的context是啥呢篱蝇?

image.png

協(xié)程上下文包含當(dāng)前協(xié)程scope的信息,如CoroutineDispatcher徽曲,CoroutineExceptionHandler零截,Job,其實(shí)這三個(gè)都是繼承實(shí)現(xiàn)CoroutineContext.Element秃臣,而這個(gè)+號(hào)不是我們以為的加號(hào)涧衙,點(diǎn)擊后看到如下源碼,這里巧妙的運(yùn)用操作符重載甜刻,不理解概念可以點(diǎn)擊該鏈接:https://www.kotlincn.net/docs/reference/operator-overloading.html 學(xué)習(xí)绍撞。

image.png

我們先不研究這樣做的用意哈,這里你要知道的是得院,該協(xié)程上下文承載了三個(gè)對(duì)象

  • Dispatchers.Main 主線程
  • exceptionHandler 異常捕獲
  • SupervisorJob 需要執(zhí)行的協(xié)程

SupervisorJob 它類似于常規(guī)的 Job傻铣,唯一的不同是:SupervisorJob 的取消只會(huì)向下傳播∠榻剩看下官方的例子

import kotlinx.coroutines.*

fun main() = runBlocking {
    val supervisor = SupervisorJob()
    with(CoroutineScope(coroutineContext + supervisor)) {
        // 啟動(dòng)第一個(gè)子作業(yè)——這個(gè)示例將會(huì)忽略它的異常(不要在實(shí)踐中這么做7侵蕖)
        val firstChild = launch(CoroutineExceptionHandler { _, _ ->  }) {
            println("The first child is failing")
            throw AssertionError("The first child is cancelled")
        }
        // 啟動(dòng)第二個(gè)子作業(yè)
        val secondChild = launch {
            firstChild.join()
            // 取消了第一個(gè)子作業(yè)且沒有傳播給第二個(gè)子作業(yè)
            println("The first child is cancelled: ${firstChild.isCancelled}, but the second one is still active")
            try {
                delay(Long.MAX_VALUE)
            } finally {
                // 但是取消了監(jiān)督的傳播
                println("The second child is cancelled because the supervisor was cancelled")
            }
        }
        // 等待直到第一個(gè)子作業(yè)失敗且執(zhí)行完成
        firstChild.join()
        println("Cancelling the supervisor")
        supervisor.cancel()
        secondChild.join()
    }
}

這段代碼的輸出如下:

The first child is failing
The first child is cancelled: true, but the second one is still active
Cancelling the supervisor
The second child is cancelled because

其實(shí)我沒怎么理解向下傳播鸭限,哪位大佬能解釋解釋,我們接著往下分析两踏。?

image.png

作者抽象這些高級(jí)函數(shù)败京,用于實(shí)現(xiàn)dsl的效果如:

image.png
image.png

catch用于捕獲協(xié)程異常信息處理,finally是在invokeOnCompletion里觸發(fā)梦染,invokeOnCompletion在協(xié)程進(jìn)入完成狀態(tài)時(shí)觸發(fā)赡麦,包括異常和正常完成。

image.png

好滴帕识,AndroidScope大致就分析完了泛粹,我們來簡(jiǎn)單總結(jié)一下

  • AndroidScope 在lifecycle ON_DESTROY時(shí) 自動(dòng)cancel
  • 可以監(jiān)聽異常catch,可以實(shí)現(xiàn)finally
  • 主線程執(zhí)行肮疗,作用域內(nèi)可以刷新UI晶姊,不用切換線程

就這些,再來看下NetCoroutineScope伪货,它是網(wǎng)絡(luò)框架的擴(kuò)展重心叫搁,NetCoroutineScope同樣的具有自動(dòng)取消的邏輯欠橘,如:

image.png

看下面的變量,我們其實(shí)能知道,NetCoroutineScope主要是擴(kuò)展了網(wǎng)絡(luò)的緩存策略雹洗,需不需要緩存倾剿,是否緩存成功等等檀夹,還有個(gè)animate蛋逾,這里看作者注釋,是控制是否在緩存成功后依然顯示加載動(dòng)畫

image.png

然后覆蓋launch函數(shù)懊悯,將邏輯插入到里面蜓谋,其實(shí)這里違背了里氏替換原則,子類盡量不要重寫父類的方法炭分,繼承包含這樣一層含義:父類中凡是已經(jīng)實(shí)現(xiàn)好的方法(相對(duì)于抽象方法而言)桃焕,實(shí)際上是在設(shè)定一系列的規(guī)范和契約,雖然它不強(qiáng)制要求所有的子類必須遵從這些契約捧毛,但是如果子類對(duì)這些非抽象方法任意修改观堂,就會(huì)對(duì)整個(gè)繼承體系造成破壞。而里氏替換原則就是表達(dá)了這一層含義呀忧。

image.png

然后就是cache配置的實(shí)現(xiàn)

image.png

用例:

用起來簡(jiǎn)單的一批

image.png

接下來就是StateCoroutineScope师痕、PageCoroutineScope、DialogCoroutineScope而账,這三個(gè)我就不一一解讀了胰坟,同樣的套路,對(duì)實(shí)際業(yè)務(wù)中的高發(fā)場(chǎng)景做的高度抽象泞辐。如:StateCoroutineScope是對(duì)StateLayout的擴(kuò)展處理笔横,在StateLayout onViewDetachedFromWindow時(shí) ?自動(dòng)cancel()掉協(xié)程竞滓,等等吧。

Net的核心部分吹缔,動(dòng)態(tài)擴(kuò)展

不啰嗦商佑,直接看代碼哦

image.png
  • 擴(kuò)展CoroutineScope,添加get函數(shù)
  • 內(nèi)連函數(shù)厢塘,reified修飾M真泛型茶没,真泛型不理解的可以看大佬的掘金介紹:https://juejin.im/post/6844904030662033422
  • 執(zhí)行完后對(duì)uid做cancel或者remove處理,釋放緩存
  • 結(jié)合NetConfig的host+path俗冻,形成完整的請(qǐng)求路徑
  • SimpleUrlRequest創(chuàng)建新的請(qǐng)求
  • async(Dispatchers.IO) 子線程的網(wǎng)絡(luò)請(qǐng)求
image.png

Post同理礁叔,其實(shí)差別就在請(qǐng)求參數(shù)牍颈,其他Head迄薄、Options 就不用分析嘍

分析到這里,我們?cè)倏偨Y(jié)一發(fā)

  • 作用域部分繼承擴(kuò)展自CoroutineScope
  • Net部分動(dòng)態(tài)擴(kuò)展自CoroutineScope

兩部分好像有關(guān)聯(lián)煮岁,其實(shí)沒有任何的代碼耦合讥蔽,這也是非常值得學(xué)習(xí)的地方。這樣做的好處其實(shí)就是限制了請(qǐng)求在作用域之外画机,造成代碼的不規(guī)范冶伞。如圖,在作用域外根本請(qǐng)求不了

image.png

好了步氏,這部分基本就結(jié)束了响禽,接下來看下源碼中最后一部分

配置、工具部分

NetConfig對(duì)象緩存網(wǎng)絡(luò)的配置荚醒,如域名芋类,app上下文,彈窗界阁,錯(cuò)誤侯繁,StateLayout的全局缺省頁

image.png
image.png
  • initNet 初始化
  • onError 全局錯(cuò)誤信息處理
  • onStateError 缺省頁處理

用法:

image.png

在Application onCreate函數(shù)中初始化就行了,就這樣就完了泡躯,沒沒沒贮竟,還有,來看個(gè)例子

image.png

為什么有個(gè)ScopeNetLife较剃,不應(yīng)該是NetCoroutineScope嗎咕别?對(duì)的其實(shí)就是它,實(shí)現(xiàn)在這里写穴,我們一起來看下

image.png

這個(gè)是Utils中的ScopeUtils類惰拱,同樣是熟悉的動(dòng)態(tài)擴(kuò)展,簡(jiǎn)單的擴(kuò)展确垫,實(shí)現(xiàn)DSL風(fēng)格就是這里的結(jié)果弓颈。

好了帽芽,基本上都講完了吧,經(jīng)過一系列的分析翔冀,你是不是已經(jīng)按耐不住自己要去體驗(yàn)了呢导街?

源碼地址

強(qiáng)哥首頁:https://github.com/liangjingkanji

Net源碼:https://github.com/liangjingkanji/Net

歡迎關(guān)注騷擾哦,聽說強(qiáng)哥最近有大動(dòng)作纤子,未來Net會(huì)更加的好用搬瑰,也希望你能喜歡這次講解,辛苦你點(diǎn)個(gè)贊唄控硼,感謝泽论。

作者

i校長(zhǎng)

簡(jiǎn)書 http://www.reibang.com/u/77699cd41b28

掘金 https://juejin.im/user/131597127135687

個(gè)人網(wǎng)站 http://jetpack.net.cnhttp://ibaozi.cn

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載卡乾,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者翼悴。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市幔妨,隨后出現(xiàn)的幾起案子鹦赎,更是在濱河造成了極大的恐慌,老刑警劉巖误堡,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件古话,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡锁施,警方通過查閱死者的電腦和手機(jī)陪踩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悉抵,“玉大人肩狂,你說我怎么就攤上這事』埽” “怎么了婚温?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)媳否。 經(jīng)常有香客問我栅螟,道長(zhǎng),這世上最難降的妖魔是什么篱竭? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任力图,我火速辦了婚禮,結(jié)果婚禮上掺逼,老公的妹妹穿的比我還像新娘吃媒。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布赘那。 她就那樣靜靜地躺著刑桑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪募舟。 梳的紋絲不亂的頭發(fā)上祠斧,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音拱礁,去河邊找鬼琢锋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛呢灶,可吹牛的內(nèi)容都是我干的吴超。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鸯乃,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鲸阻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起飒责,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤赘娄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后宏蛉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡性置,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年拾并,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹏浅。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嗅义,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出隐砸,到底是詐尸還是另有隱情之碗,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布季希,位于F島的核電站褪那,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏式塌。R本人自食惡果不足惜博敬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望峰尝。 院中可真熱鬧偏窝,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至硼补,卻和暖如春默赂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背括勺。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工缆八, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疾捍。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓奈辰,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親乱豆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奖恰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354