一篇文章基本看懂gradle

Hi,大家好啊。笨鳥之旅已經(jīng)很久都沒有更新了六孵,感謝大家這么久以來還把我留在列表里。這么久以來不更新的原因主要是我本人進(jìn)入了一個(gè)迷茫期幅骄,對(duì)于工作和生活都提不起興致來劫窒。加上每天工作的時(shí)間太長,一回家就癱在床上了拆座。這也導(dǎo)致了很久之前的文章計(jì)劃一直都擱淺主巍,做的計(jì)劃一次一次的delay冠息。于是生活進(jìn)入了一個(gè)負(fù)循環(huán)。但是生活還是要向上看煤禽,還是要堅(jiān)持去努力做點(diǎn)事情铐达,努力去變得更好,所以我又來了檬果。以后一定會(huì)努力的學(xué)習(xí)瓮孙,努力的發(fā)文。

這段時(shí)間來學(xué)習(xí)了gradle选脊,也體會(huì)到了gradle從初步理解到基本熟悉杭抠,再到深入源碼這樣一個(gè)過程中的一些曲折。這篇文章主要是gradle的基礎(chǔ)知識(shí)篇恳啥∑樱看完這篇文章,你可以:

  • 清楚gradle的定義和解決的痛點(diǎn)
  • 基本理解Android gradle的運(yùn)作機(jī)制
  • 基本理解gradle的大部分語法
  • 學(xué)會(huì)基本的groovy開發(fā)

本篇文章預(yù)計(jì)學(xué)習(xí)時(shí)間30分鐘
如果你想關(guān)注gradle更深入的一些知識(shí)钝的,請(qǐng)繼續(xù)關(guān)注后續(xù)gradle文章翁垂。

what is gradle?

先來看一段維基百科上對(duì)于gradle的解釋。

Gradle是一個(gè)基于Apache Ant和Apache Maven概念的項(xiàng)目自動(dòng)化構(gòu)建工具硝桩。它使用一種基于Groovy的特定領(lǐng)域語言來聲明項(xiàng)目設(shè)置沿猜,而不是傳統(tǒng)的XML。當(dāng)前其支持的語言限于Java碗脊、Groovy和Scala啼肩,計(jì)劃未來將支持更多的語言。

可能剛接觸gradle的同學(xué)都不是很了解gradle的這個(gè)定義衙伶∑碜梗可能就只會(huì)跟著網(wǎng)上的教程copy一點(diǎn)配置,但是不理解這些配置背后的原理矢劲。那么怎么來理解這句話呢赦拘,我們可以把握到三個(gè)要點(diǎn):首先,它是一種構(gòu)建工具芬沉,其次躺同,gradle是基于maven概念的,最后花嘶,使用groovy這種語言來聲明。要理解這幾句話蹦漠,我們先考慮幾個(gè)場景椭员。

1.渠道管理:國內(nèi)手機(jī)市場有大大小小數(shù)十個(gè),大的手機(jī)廠商也有五六個(gè)笛园,每個(gè)廠商可能又有不同的定制rom隘击。如果我們要為不同市場和廠商進(jìn)行適配侍芝,那就需要寫這樣的代碼

if(isHuawei) {
    // dosomething
} else if(isOppo) {
    // dosomething
}

這樣的話,繁瑣不說埋同,對(duì)單個(gè)手機(jī)而言大量的無用代碼被編譯進(jìn)apk中州叠,包體積和運(yùn)行速度都會(huì)受影響。為了解決這個(gè)問題凶赁,gradle引進(jìn)了productFlavor和buildType的能力咧栗,能根據(jù)情況來進(jìn)行打包。所以說他是一個(gè)自動(dòng)化構(gòu)建工具虱肄≈掳澹可以看官方文檔

2.依賴管理:我們通常會(huì)在項(xiàng)目中引入各種三方庫進(jìn)行代碼復(fù)用。比如咏窿,直接手動(dòng)把jar或者aar copy到項(xiàng)目中斟或,然后添加依賴。這種方法缺陷很明顯集嵌,首先配置和刪除流程很繁瑣萝挤,其次,同一個(gè)jar可能會(huì)被多個(gè)項(xiàng)目所引用根欧,導(dǎo)致不知不覺就copy了多個(gè)jar怜珍。最后,版本管理艱難咽块。為了解決這個(gè)問題绘面,gradle是基于maven倉庫,配置和刪除的時(shí)候僅需要對(duì)倉庫的坐標(biāo)進(jìn)行操作侈沪,所有的庫都會(huì)被gradle統(tǒng)一管理揭璃,大多數(shù)情況下每個(gè)庫只會(huì)有一個(gè)版本存在于項(xiàng)目中,并且每個(gè)庫只會(huì)有一個(gè)副本存在于項(xiàng)目中亭罪。

所以gradle其實(shí)不是什么神秘的東西瘦馍,只是基于某種語言(groovy, java, kotlin)的一種構(gòu)建工具而已。只要我們大概掌握了基本的用法和他的內(nèi)部原理应役,日常工作中就會(huì)知道自己網(wǎng)上搜到的命令是什么意思啦情组。skr~

小試牛刀-android中的gradle

image

咱們先看看日常工作中經(jīng)常用到的幾個(gè)gradle文件÷嵯椋可以看到主要有有三個(gè)文件:
1.build.gradle
根文件下放的通常放的是針對(duì)整個(gè)工程的通用配置院崇,每個(gè)module下面的build.gradle文件是針對(duì)每個(gè)module自身的配置。

buildscript {
    ext.kotlin_version = '1.2.71'
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}
allprojects {
    repositories {
        google()
        jcenter()
    }
}

這是一個(gè)默認(rèn)的配置袍祖,我們可以看到有buildscript底瓣,allprojects,repositories蕉陋,dependencies幾個(gè)配置項(xiàng)捐凭,這些配置項(xiàng)是干嘛的呢拨扶,很多的同學(xué)在剛學(xué)gradle的時(shí)候都是一臉懵逼的。這些其實(shí)是gradle的一種特定的語法茁肠,我們稱之為DSL(domain-specific language)患民。可以參考官網(wǎng)垦梆。這里可以看到allprojects代理的是每個(gè)project匹颤,可以理解成我們的每個(gè)module,也就是對(duì)我們所寫的每個(gè)module的配置奶赔。buildscript主要配置的是打包相關(guān)的東西惋嚎,比如gradle版本,gradle插件版本等站刑,這些都是針對(duì)構(gòu)建工具自己的配置另伍。repositories,dependencies是三方庫的倉庫和坐標(biāo)绞旅。所以根目錄的build.gradle相當(dāng)于是整體的配置摆尝。

而module下的build.gradle主要是android,dependencies等配置項(xiàng)因悲。

apply plugin: 'com.android.application'

android{
    ...
}
dependencies{
    ...
}

可能有些同學(xué)會(huì)感到奇怪堕汞,為啥我們?cè)?a target="_blank">官網(wǎng)沒有看到android這個(gè)配置項(xiàng)呢?這個(gè)主要是因?yàn)樗⒉皇莋radle的DSL晃琳,某種意義上說應(yīng)該算是android特有的讯检,是通過Android的插件'com.android.application'帶進(jìn)來的配置項(xiàng)。我們?nèi)绻训谝恍袆h掉卫旱,就會(huì)發(fā)現(xiàn)android{}這個(gè)配置項(xiàng)找不到了人灼。

所以形病,我們可以發(fā)現(xiàn)球及,build.gradle里面的配置項(xiàng),要么是gradle自帶的绒北,要么是各種插件定義的适贸。有不認(rèn)識(shí)的配置項(xiàng)灸芳,就去官網(wǎng)查詢一下就好了,授人以魚不如授人以漁嘛拜姿。我們后面也會(huì)講解到引進(jìn)插件的方式和怎么定義插件和配置項(xiàng)烙样。

2.settings.gradle
這個(gè)文件主要是決定每個(gè)module是否參與構(gòu)建。我們可以這樣去理解蕊肥,settings.gradle相當(dāng)于是每個(gè)module的開關(guān)谒获,關(guān)上了這個(gè)module就不能使用了,別的依賴到它的module也都會(huì)出問題。

3.gradle.properties
這里主要是增加和修改一些可以在構(gòu)建過程中直接使用的參數(shù)究反。不只是可以添加自定義參數(shù),還可以修改系統(tǒng)的參數(shù)哦~

總結(jié)一下儒洛,就是說根目錄下有一個(gè)build.gradle精耐,處理整個(gè)工程的配置項(xiàng),根目錄下的settings.gradle配置整個(gè)工程中參與構(gòu)建的module琅锻,每個(gè)module自己有一個(gè)build.gradle卦停,處理自己模塊的配置。這就是android構(gòu)建的一個(gè)大概情況恼蓬。當(dāng)然惊完,看了這一部分肯定還是不懂怎么去寫的,接下來我們走進(jìn)代碼層面处硬。

groovy-學(xué)gradle的密鑰

gradle可以使用groovy小槐,kotlin,java等語言進(jìn)行書寫荷辕,但是groovy相對(duì)來說是目前比較流行的gradle配置方式凿跳,下面我們講解一點(diǎn)groovy基礎(chǔ)。不講太多疮方,夠用就行控嗜。

1.字符串

groovy的字符串分為兩種java.lang.String和groovy.lang.GString。其中單引號(hào)和三引號(hào)是String類型的骡显。雙引號(hào)是GString類型的疆栏。支持占位插值操作。和kotlin一樣惫谤,groovy的插值操作也是用${}或者$來標(biāo)示壁顶,${}用于一般替代字串或者表達(dá)式,$主要用于A.B的形式中石挂。

def number = 1 
def eagerGString = "value == ${number}"
def lazyGString = "value == ${ -> number }"

println eagerGString
println lazyGString

number = 2 
println eagerGString 
println lazyGString

2.字符Character

Groovy沒有明確的Character博助。但是可以強(qiáng)行聲明。

char c1 = 'A' 
assert c1 instanceof Character

def c2 = 'B' as char 
assert c2 instanceof Character

def c3 = (char)'C' 
assert c3 instanceof Character

4.List

Groovy的列表和python的很像痹愚。支持動(dòng)態(tài)擴(kuò)展富岳,支持放置多種數(shù)據(jù)。使用方法支持def和直接定義拯腮。還可以像python那樣索引

//List中存儲(chǔ)任意類型
def heterogeneous = [1, "a", true]

//判斷List默認(rèn)類型
def arrayList = [1, 2, 3]
assert arrayList instanceof java.util.ArrayList

//使用as強(qiáng)轉(zhuǎn)類型
def linkedList = [2, 3, 4] as LinkedList    
assert linkedList instanceof java.util.LinkedList

//定義指定類型List
LinkedList otherLinked = [3, 4, 5]          
assert otherLinked instanceof java.util.LinkedList

// 像python一樣索引
assert letters[1] == 'b'
//負(fù)數(shù)下標(biāo)則從右向左index
assert letters[-1] == 'd'    
assert letters[-2] == 'c'
//指定item賦值判斷
letters[2] = 'C'             
assert letters[2] == 'C'
//給List追加item
letters << 'e'               
assert letters[ 4] == 'e'
assert letters[-1] == 'e'
//獲取一段List子集
assert letters[1, 3] == ['b', 'd']         
assert letters[2..4] == ['C', 'd', 'e'] 

5.Map

//定義一個(gè)Map
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']   
//獲取一些指定key的value進(jìn)行判斷操作
assert colors['red'] == '#FF0000'    
assert colors.green  == '#00FF00'

6.運(yùn)算符

  • **: 次方運(yùn)算符窖式。
  • ?.:安全占位符。和kotlin一樣避免空指針異常动壤。
  • .@:直接域訪問操作符萝喘。因?yàn)镚roovy自動(dòng)支持屬性getter方法,但有時(shí)候我們有一個(gè)自己寫的特殊getter方法,當(dāng)不想調(diào)用這個(gè)特殊的getter方法則可以用直接域訪問操作符阁簸。這點(diǎn)跟kotlin的
  • .&:方法指針操作符爬早,因?yàn)殚]包可以被作為一個(gè)方法的參數(shù),如果想讓一個(gè)方法作為另一個(gè)方法的參數(shù)則可以將一個(gè)方法當(dāng)成一個(gè)閉包作為另一個(gè)方法的參數(shù)启妹。
  • ?::二目運(yùn)算符筛严。與kotlin中的類似。
  • *.展開運(yùn)算符饶米,一個(gè)集合使用展開運(yùn)算符可以得到一個(gè)元素為原集合各個(gè)元素執(zhí)行后面指定方法所得值的集合桨啃。
cars = [
    new Car(make: 'Peugeot', model: '508'),
   null,                                              
   new Car(make: 'Renault', model: 'Clio')]
assert cars*.make == ['Peugeot', null, 'Renault']     
assert null*.make == null 

7.閉包
groovy里比較重要的是閉包的概念。官方定義是“Groovy中的閉包是一個(gè)開放檬输,匿名的代碼塊照瘾,可以接受參數(shù),返回值并分配給變量”丧慈。
其實(shí)閉包跟kotlin的lambda函數(shù)很像析命,都是先定義后執(zhí)行。但是又有一些細(xì)微的區(qū)別逃默。接下來我們細(xì)講講gradle的閉包碳却。

閉包是可以用作方法參數(shù)的代碼塊,Groovy的閉包更象是一個(gè)代碼塊或者方法指針笑旺,代碼在某處被定義然后在其后的調(diào)用處執(zhí)行昼浦。一個(gè)閉包實(shí)際上就是一個(gè)Closure類型的實(shí)例。寫法和kotlin的lambda函數(shù)很像筒主。

我們常見的閉包是這樣的

//最基本的閉包
{ item++ }                                          
//使用->將參數(shù)與代碼分離
{item -> item++ }                                       
//使用隱含參數(shù)it
{ println it }                               
//使用顯示的名為參數(shù)
{ name -> println name }

// 調(diào)用方法
a.call()
a()

// Groovy的閉包支持最后一個(gè)參數(shù)為不定長可變長度的參數(shù)关噪。
def multiConcat = { int n, String... args ->                
    args.join('')*n
}

大家要注意,如果我們單純的只是寫成 a = { item++ }, 這只是定義了一個(gè)閉包乌妙,是不能運(yùn)行的使兔。必須調(diào)用a.call()才能運(yùn)行出來。所以大家可以理解了藤韵,閉包就是一段代碼塊而已虐沥。當(dāng)我們有需要的時(shí)候,可以去運(yùn)行它泽艘,這么一想是不是和lambda函數(shù)很像欲险?

如果你看了官網(wǎng),你會(huì)發(fā)現(xiàn)有一些這樣的說法匹涮,

image

什么叫做delegate天试?這里涉及到閉包內(nèi)部的三種對(duì)象。

  • this 對(duì)應(yīng)于定義閉包的那個(gè)類然低,如果在內(nèi)部類中定義喜每,指向的是內(nèi)部類
  • owenr 對(duì)應(yīng)于定義閉包的那個(gè)類或者閉包务唐,如果在閉包中定義,對(duì)應(yīng)閉包带兜,否則同this一致
  • delegate 默認(rèn)是和owner一致枫笛,或者自定義delegate指向

this和owner都比較好理解。我們可以用閉包的getxxx方法獲取

def thisObject = closure.getThisObject()
def ownerObject = closure.getOwner()
def delegate = closure.getDelegate()

重頭戲還是delegate這個(gè)對(duì)象刚照。閉包可以設(shè)置delegate對(duì)象崇堰,設(shè)置delegate的意義就是將閉包和一個(gè)具體的對(duì)象關(guān)聯(lián)起來
我們先來看個(gè)例子涩咖,這里以自定義android閉包為例。

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"

    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }
}

這個(gè)閉包對(duì)應(yīng)的實(shí)體類是兩個(gè)繁莹。

# Android.groovy
class Android {
    private int mCompileSdkVersion
    private String mBuildToolsVersion
    private ProductFlavor mProductFlavor

    Android() {
        this.mProductFlavor = new ProductFlavor()
    }

    void compileSdkVersion(int compileSdkVersion) {
        this.mCompileSdkVersion = compileSdkVersion
    }

    void buildToolsVersion(String buildToolsVersion) {
        this.mBuildToolsVersion = buildToolsVersion
    }

    void defaultConfig(Closure closure) {
        closure.setDelegate(mProductFlavor)
        closure.setResolveStrategy(Closure.DELEGATE_FIRST)
        closure.call()
    }
    
    @Override
     String toString() {
        return "Android{" +
                "mCompileSdkVersion=" + mCompileSdkVersion +
                ", mBuildToolsVersion='" + mBuildToolsVersion + '\'' +
                ", mProductFlavor=" + mProductFlavor +
                '}'
    }
}

# ProductFlavor.groovy
class ProductFlavor {
    private int mVersionCode
    private String mVersionName
    private int mMinSdkVersion
    private int mTargetSdkVersion

    def versionCode(int versionCode) {
        mVersionCode = versionCode
    }

    def versionName(String versionName) {
        mVersionName = versionName
    }

    def minSdkVersion(int minSdkVersion) {
        mMinSdkVersion = minSdkVersion
    }


    def targetSdkVersion(int targetSdkVersion) {
        mTargetSdkVersion = targetSdkVersion
    }

    @Override
    String toString() {
        return "ProductFlavor{" +
                "mVersionCode=" + mVersionCode +
                ", mVersionName='" + mVersionName + '\'' +
                ", mMinSdkVersion=" + mMinSdkVersion +
                ", mTargetSdkVersion=" + mTargetSdkVersion +
                '}'
    }
}

然后定義的時(shí)候就寫成

//閉包定義
def android = {
        compileSdkVersion 25
        buildToolsVersion "25.0.2"
        defaultConfig {
            minSdkVersion 15
            targetSdkVersion 25
            versionCode 1
            versionName "1.0"
        }
    }

//調(diào)用
Android bean = new Android()
android.delegate = bean
android.call()
println bean.toString()

//打印結(jié)果
Android{mCompileSdkVersion=25, mBuildToolsVersion='25.0.2', mProductFlavor=ProductFlavor{mVersionCode=1, mVersionName='1.0', mMinSdkVersion=15, mTargetSdkVersion=25}}

這樣就能將閉包中聲明的值檩互,賦給兩個(gè)對(duì)象Android和ProductFlavor來處理了。

上面官網(wǎng)的圖里咨演,說ScriptHandler被設(shè)置成buildscript的delegate闸昨。意思就是buildscript定義的參數(shù)被ScriptHandler拿來使用了。大家有興趣的可以去看看ScriptHandler的源碼~

Project與Task-gradle構(gòu)建體系

上面我們講完了基本的用法薄风,大家可能懂gradle的配置和寫法了饵较。但是可能還是不懂gradle的構(gòu)建體系到底是怎么樣的。這里我們就要深入進(jìn)gradle的構(gòu)建體系Project和Task了遭赂。下面的東西看著就要?jiǎng)觿?dòng)腦筋了循诉。

1.Task
Task是gradle腳本中的最小可執(zhí)行單元。類圖如下:


image

值得注意的是因?yàn)镚radle構(gòu)建腳本默認(rèn)的名字是build.gradle撇他,當(dāng)在shell中執(zhí)行g(shù)radle命令時(shí)茄猫,Gradle會(huì)去當(dāng)前目錄下尋找名字是build.gradle的文件。所以只有定義在build.gradle中的Task才是有效的困肩。

可以通過三種方式來聲明task划纽。我們可以根據(jù)自己的項(xiàng)目需要去定義Task。比如自定義task接管gradle的編譯過程

task myTask2 << {
    println "doLast in task2"
}

//采用 Project.task(String name) 方法來創(chuàng)建
project.task("myTask3").doLast {
    println "doLast in task3"
}

//采用 TaskContainer.create(String name) 方法來創(chuàng)建
project.tasks.create("myTask4").doLast {
    println "doLast in task4"
}

TaskContianer 是用來管理所有的 Task 實(shí)例集合的锌畸,可以通過 Project.getTasks() 來獲取 TaskContainer 實(shí)例勇劣。
常見接口:

findByPath(path: String): Task
getByPath(path: String): Task
getByName(name: String): Task
withType(type: Class): TaskCollection
matching(condition: Closure): TaskCollection

//創(chuàng)建task
create(name: String): Task
create(name: String, configure: Closure): Task 
create(name: String, type: Class): Task
create(options: Map<String, ?>): Task
create(options: Map<String, ?>, configure: Closure): Task

//當(dāng)task被加入到TaskContainer時(shí)的監(jiān)聽
whenTaskAdded(action: Closure)

Gradle支持增量編譯。了解過編譯profile文件的朋友都知道潭枣,里面有大量的task都是up-to-date比默。那么這種up-to-date是什么意思呢。Gradle的Task會(huì)把每次運(yùn)行的結(jié)果緩存下來盆犁,當(dāng)下次運(yùn)行時(shí)退敦,會(huì)檢查一個(gè)task的輸入輸出有沒有變更。如果沒有變更就是up-to-date蚣抗,跳過編譯侈百。

image

2.Project
先從Project對(duì)象講起瓮下,Project是與Gradle交互的主接口。android開發(fā)中最為我們所熟悉的就是build.gradle文件钝域,這個(gè)文件與Project是一對(duì)一的關(guān)系讽坏,build.gradle文件是project對(duì)象的委托,腳本中的配置都是對(duì)應(yīng)著Project的Api例证。Gradle構(gòu)建進(jìn)程啟動(dòng)的時(shí)候會(huì)根據(jù)build.gradle去實(shí)例化Project類路呜。也就是說,構(gòu)建的時(shí)候织咧,每個(gè)build.gradle文件會(huì)生成一個(gè)Project對(duì)象胀葱,這個(gè)對(duì)象負(fù)責(zé)當(dāng)前module的構(gòu)建。

Project本質(zhì)上是包含多個(gè)Task的容器笙蒙,所有的Task存在TaskContainer中抵屿。我們從名字可以看出


image

可以看到dependencies, configuration, allprojects, subprojects, beforeEvaluate, afterEvaluate這些都是我們常見的配置項(xiàng),在build.gradle文件中接收一個(gè)閉包Closure捅位。

好了轧葛,現(xiàn)在我們已經(jīng)聊了build.gradle了,但是大家都知道艇搀,我們項(xiàng)目中還有一個(gè)settings.gradle呢尿扯,這個(gè)是拿來干嘛的呢?這就要說到Project的Lifecycle了焰雕,也就是Gradle構(gòu)建Project的步驟衷笋,看官網(wǎng)原文:

  • Create a Settings instance for the build.
  • Evaluate the settings.gradle script, if present, against the Settings object to configure it.
  • Use the configured Settings object to create the hierarchy of Project instances.
  • Finally, evaluate each Project by executing its build.gradle file, if present, against the project. The projects are evaluated in breadth-wise order(寬度搜索), such that a project is evaluated before its child projects. This order can be overridden by calling Project.evaluationDependsOnChildren() or by adding an explicit evaluation dependency using Project.evaluationDependsOn(java.lang.String).

也就是說,Project對(duì)象依賴Settings對(duì)象的構(gòu)建矩屁。我們常在settings.gradle文件中配置需要引入的module右莱,就是這個(gè)原因。

3.Property
看完了build.gradle和settings.gradle档插,接下來我們講講gradle.properties慢蜓。這個(gè)文件存放的鍵值對(duì)形式的屬性,這些屬性能被項(xiàng)目中的gradle腳本使用ext.xxx所訪問郭膛。

我們也可以使用Properties類來動(dòng)態(tài)創(chuàng)建屬性文件晨抡。如:

def defaultProps = new Properties()
defaultProps.setProperty("debuggable", 'true')
defaultProps.setProperty("groupId", GROUP)

并且屬性可以繼承,在一個(gè)項(xiàng)目中定義的屬性可以自動(dòng)被子項(xiàng)目繼承则剃。所以在哪個(gè)子項(xiàng)目都可以使用project.ext.xxx訪問耘柱。不同子項(xiàng)目間采用通用的配置插件來配置

apply from: rootProject.file('library.gradle')

總結(jié)

通過上面的學(xué)習(xí),大家應(yīng)該已經(jīng)了解了gradle的基本配置棍现,寫法和比較淺顯的內(nèi)部原理了调煎。因?yàn)槠颍钊氲膬?nèi)容我們放在下一篇己肮。敬請(qǐng)期待《一篇文章深入gradle》

參考:
官網(wǎng)
[Android Gradle] 搞定Groovy閉包這一篇就夠了

我是Android笨鳥之旅士袄,一個(gè)陪著你慢慢變強(qiáng)的公眾號(hào)悲关。

微信公眾號(hào)二維碼.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市娄柳,隨后出現(xiàn)的幾起案子寓辱,更是在濱河造成了極大的恐慌,老刑警劉巖赤拒,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秫筏,死亡現(xiàn)場離奇詭異,居然都是意外死亡挎挖,警方通過查閱死者的電腦和手機(jī)这敬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蕉朵,“玉大人崔涂,你說我怎么就攤上這事∧乖欤” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵锚烦,是天一觀的道長觅闽。 經(jīng)常有香客問我,道長涮俄,這世上最難降的妖魔是什么蛉拙? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮彻亲,結(jié)果婚禮上孕锄,老公的妹妹穿的比我還像新娘。我一直安慰自己苞尝,他們只是感情好畸肆,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宙址,像睡著了一般轴脐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抡砂,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天大咱,我揣著相機(jī)與錄音,去河邊找鬼注益。 笑死碴巾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的丑搔。 我是一名探鬼主播厦瓢,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼提揍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了旷痕?” 一聲冷哼從身側(cè)響起碳锈,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎欺抗,沒想到半個(gè)月后售碳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绞呈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年贸人,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佃声。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡艺智,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出圾亏,到底是詐尸還是另有隱情十拣,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布志鹃,位于F島的核電站夭问,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏曹铃。R本人自食惡果不足惜缰趋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望陕见。 院中可真熱鬧秘血,春花似錦、人聲如沸评甜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽忍坷。三九已至谋竖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間承匣,已是汗流浹背蓖乘。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留韧骗,地道東北人嘉抒。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像袍暴,于是被迫代替她去往敵國和親些侍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子隶症,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

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