關(guān)于Android架構(gòu)惠奸,你是否還在生搬硬套?

前言

關(guān)于Android架構(gòu)恰梢,可能在很多人心里一直都是虛無縹緲的存在佛南,似懂非懂、為了用而用嵌言、處處生搬硬套嗅回,這種情況使用的意義真的很有限。本人有多個(gè)項(xiàng)目重構(gòu)的經(jīng)驗(yàn)摧茴,恰好對(duì)設(shè)計(jì)領(lǐng)域較為感興趣绵载,今天我將毫無保留的將自己對(duì)架構(gòu)、設(shè)計(jì)的理解分享給大家苛白。

本文不會(huì)具體去講什么是MVC娃豹、MVP、MVVM购裙,但我描述的點(diǎn)應(yīng)該都是這些模式的基石懂版,從本質(zhì)上講明白為什么這樣做,這樣做的好處是什么躏率,有了這些底層思想的支持再去看對(duì)應(yīng)的架構(gòu)模式躯畴,相信會(huì)讓你有一種煥然一新的感覺民鼓。

知識(shí)儲(chǔ)備:需掌握Java面向?qū)ο蟆⒘笤O(shè)計(jì)原則私股,如果不理解也無妨摹察,我盡量將用到的設(shè)計(jì)原則加以詳細(xì)描述

目錄

  • 1. 模塊化的意義何在?
    • 1.1 基本概念以及底層思想
    • 1.2 我們要基于哪些特性去做模塊化劃分倡鲸?
    • 1.3 Android如何做分層處理供嚎?
    • 1.4 Data Mapper或許是解藥
    • 1.5 無處安放的業(yè)務(wù)邏輯
  • 2. 合理分層是給 數(shù)據(jù)驅(qū)動(dòng)UI 做鋪墊
    • 2.1 什么是 控制反轉(zhuǎn)?
    • 2.2 什么是數(shù)據(jù)驅(qū)動(dòng)UI峭状?
    • 2.3 為什么說數(shù)據(jù)驅(qū)動(dòng)UI底層思想是控制反轉(zhuǎn)克滴?
    • 2.4 為什么引入Diff?
  • 3. 為什么我建議使用 函數(shù)式編程
    • 3.1 什么是 函數(shù)式編程优床?
    • 3.2 Android視圖開發(fā)可以借鑒函數(shù)式編程思想

1. 模塊化的意義何在劝赔?

1.1 基本概念以及底層思想

所有的模塊化都是為了滿足單一設(shè)計(jì)原則 (字面意思理解即可),一個(gè)函數(shù)或者一個(gè)類再或者一個(gè)模塊胆敞,職責(zé)越單一復(fù)用性就越強(qiáng)着帽,同時(shí)能夠間接降低耦合性

在軟件工程的背景下,改動(dòng)就會(huì)有出錯(cuò)的可能移层,不要說"我注意一點(diǎn)就不會(huì)出錯(cuò)"這種話仍翰,因?yàn)槿瞬皇菣C(jī)器。我們能做的就是盡可能讓模塊更加單一观话,職責(zé)越單一影響到外層模塊的可能性就越小予借,這樣出錯(cuò)的概率也就越低。

所以模塊化核心思想即:單一設(shè)計(jì)原則

1.2 我們要基于哪些特性去做模塊化劃分频蛔?

做模塊化處理的時(shí)候盡量基于兩種特性進(jìn)行功能特性灵迫、業(yè)務(wù)特性

功能特性

網(wǎng)絡(luò)、圖片加載等等都可稱之為功能特性晦溪。比如網(wǎng)絡(luò):我們可以將網(wǎng)絡(luò)框架的集成瀑粥、封裝等等寫到同一個(gè)模塊(module、package等)當(dāng)中三圆,這樣可以增強(qiáng)可讀性(同一目錄一目了然)狞换、降低誤操作概率,方便于維護(hù)也更加安全嫌术。同時(shí)也可將模塊托管至遠(yuǎn)程如maven庫哀澈,可供多個(gè)項(xiàng)目使用牌借,進(jìn)一步提升復(fù)用性

業(yè)務(wù)特性

業(yè)務(wù)特性字面意思理解即可度气,就是我們常常編寫的業(yè)務(wù),需要以業(yè)務(wù)的特性進(jìn)行模塊劃分

為什么說業(yè)務(wù)特性優(yōu)先級(jí)要高于功能特性膨报?

舉個(gè)例子如下圖:


image.png

相信很多人見過或者正在使用這種分包方式磷籍,在業(yè)務(wù)層把所有的Adapter适荣、PresenterActivity等等都放在對(duì)應(yīng)的包中院领,這種方式合理嗎弛矛?先說答案不合理,首先這已經(jīng)是在業(yè)務(wù)層比然,我們做的所有事情其實(shí)都在為業(yè)務(wù)層服務(wù)丈氓,所以業(yè)務(wù)的優(yōu)先級(jí)應(yīng)該是最高的,我們應(yīng)當(dāng)優(yōu)先根據(jù)業(yè)務(wù)特性將對(duì)應(yīng)的類放入到同一個(gè)包中强法。

功能模塊核心是功能万俗,應(yīng)當(dāng)以功能進(jìn)行模塊劃分。業(yè)務(wù)模塊核心是業(yè)務(wù)饮怯,應(yīng)當(dāng)優(yōu)先以業(yè)務(wù)進(jìn)行模塊劃分闰歪,其次再以功能進(jìn)行模塊劃分。

1.3 Android如何做分層處理蓖墅?

前端開發(fā)其實(shí)就是做數(shù)據(jù)搬運(yùn)库倘,再展示到視圖中。數(shù)據(jù)視圖是兩個(gè)不同的概念论矾,為了提高復(fù)用性以及可維護(hù)性教翩,我們應(yīng)當(dāng)根據(jù)單一設(shè)計(jì)原則我們應(yīng)當(dāng)將二者進(jìn)行分層處理,所以無論是MVC拇囊、MVP還是MVVM最核心的點(diǎn)都是將數(shù)據(jù)視圖進(jìn)行分層迂曲。

絆腳石:

通常來講,我們通過網(wǎng)絡(luò)請(qǐng)求拿到數(shù)據(jù)結(jié)構(gòu)都是后端定義的寥袭,這也就意味著視圖層不得不直接使用后端定義的字段路捧,一旦后端進(jìn)行業(yè)務(wù)調(diào)整會(huì)迫使我們前端從數(shù)據(jù)層-->視圖層都會(huì)進(jìn)行對(duì)應(yīng)的改動(dòng),如下偽代碼所示:

//原始邏輯
數(shù)據(jù)層
Model{
    title
}
UI層
View{
    textView = model.title
}

//后端調(diào)整后
數(shù)據(jù)層
Model{
    title
    prefix
}
UI層
View{
    textView = model.prefix + model.title
}

起初我們的textView顯示的是model中的title传黄,但后端調(diào)整后我們需要在model中加一個(gè)prefix字段杰扫,同時(shí)textView顯示內(nèi)容也要做一次字符串拼接。視圖層因?yàn)閿?shù)據(jù)層的改動(dòng)而被動(dòng)做了修改膘掰。既然做了分層我們想要的肯定是視圖章姓、數(shù)據(jù)互不干擾,如何解決识埋?往下看...

1.4 Data Mapper或許是解藥

Data Mapper是后端常用的一個(gè)概念凡伊,一般情況下他們是不會(huì)直接使用數(shù)據(jù)庫里面的字段,而是加一個(gè)Data Mapper(數(shù)據(jù)映射)將數(shù)據(jù)庫表轉(zhuǎn)按需換成Java Bean,這樣做的好處也很明顯窒舟,表結(jié)構(gòu)甭管怎么折騰都不會(huì)影響到業(yè)務(wù)層代碼系忙。

對(duì)于前端我覺得可以適當(dāng)引入Data Mapper,將后端數(shù)據(jù)轉(zhuǎn)換成本地模型惠豺,本地模型只與設(shè)計(jì)圖對(duì)應(yīng)银还,將后端業(yè)務(wù)視圖完全隔離风宁。這也就解決了 1.3 面臨的問題,具體方式如下:

數(shù)據(jù)層
Model{
    title
    prefix
}
本地模型(與設(shè)計(jì)圖一一對(duì)應(yīng))
LocalModel{
    //將后端模型轉(zhuǎn)換為本地模型
    title = model.prefix + model.title
}
UI層
View{
    textView = localModel.title
}

LocalModel相當(dāng)于一個(gè)中間層蛹疯,通過適配器模式將數(shù)據(jù)層與視圖層做隔離戒财。

前端引入Data Mapper后可以脫離后端進(jìn)行開發(fā),只要需求明確就可以做視圖層的開發(fā)捺弦,完全不需要擔(dān)心后端返回什么結(jié)構(gòu)饮寞、字段。并且這種做法是一勞永逸的列吼,比如后端需要對(duì)某些字段做調(diào)整骂际,我們可以不暇思索直奔數(shù)據(jù)層,涉及到的調(diào)整100%不會(huì)影響到視圖層

注意點(diǎn):

當(dāng)下有一部分公司為了將前后端分離更徹底冈欢,由前端開發(fā)人員提供Java Bean(相當(dāng)于LocalModel)的結(jié)構(gòu)歉铝,好處也很明顯,更多的業(yè)務(wù)內(nèi)聚到后端凑耻,很大程度提升了業(yè)務(wù)的靈活性太示,畢竟App發(fā)一次版成本還是比較大的。面對(duì)這種情況我們其實(shí)沒必要再編寫Data Mapper香浩。所以任何架構(gòu)設(shè)計(jì)都要結(jié)合實(shí)際情況类缤,適合自己的才是最好的。

1.5 無處安放的業(yè)務(wù)邏輯

關(guān)于業(yè)務(wù)邏輯其實(shí)是一個(gè)很籠統(tǒng)的概念邻吭,甚至可以將任意一行代碼稱之為業(yè)務(wù)邏輯餐弱,如此寬泛的概念我們?cè)撊绾稳ダ斫猓课蚁却笾聦⑺譃閮蓚€(gè)方面:

  • 界面交互邏輯:視圖層的交互邏輯囱晴,比如手勢(shì)控制膏蚓、吸頂懸浮等等都是根據(jù)業(yè)務(wù)需要實(shí)現(xiàn)的,所以嚴(yán)格來說這部分也屬于業(yè)務(wù)邏輯畸写。但這部分業(yè)務(wù)邏輯一般在視圖層實(shí)現(xiàn)驮瞧。
  • 數(shù)據(jù)邏輯:這部分是大家常說的業(yè)務(wù)邏輯,屬于強(qiáng)業(yè)務(wù)邏輯枯芬,比如根據(jù)不同用戶類型獲取不同數(shù)據(jù)论笔、展示不同界面,加上Data Mapper一系列操作其實(shí)就是給后端兜底千所,幫他們補(bǔ)全剩余邏輯而已狂魔。為了方便大家理解下文我將數(shù)據(jù)邏輯統(tǒng)稱為業(yè)務(wù)邏輯

前面我們說到淫痰,Android開發(fā)應(yīng)該具備數(shù)據(jù)層視圖層最楷,那業(yè)務(wù)邏輯放在哪一層比較合適呢?比如MVVM模式下大家都說將業(yè)務(wù)邏輯放到ViewModel處理,這么說也沒有太大的問題管嬉,但如果一個(gè)界面足夠復(fù)雜那對(duì)應(yīng)的ViewModel代碼可能會(huì)有成百上千行,看起來會(huì)很臃腫可讀性也非常差朗鸠。最重要的一點(diǎn)這些業(yè)務(wù)很難編寫單元測(cè)試用例蚯撩。

關(guān)于業(yè)務(wù)邏輯我建議單獨(dú)寫一個(gè)use case處理。

use case通常放在ViewModel/Presenter數(shù)據(jù)層之間烛占,業(yè)務(wù)邏輯以及Data Mapper都應(yīng)該放在use case中胎挎,每一個(gè)行為對(duì)應(yīng)一個(gè)use case。這樣就解決了ViewModel/Presenter臃腫的問題忆家,同時(shí)更方便編寫測(cè)試用例犹菇。

注意點(diǎn):

好的設(shè)計(jì)都是特定場(chǎng)景解決特定問題,過度設(shè)計(jì)不僅解決不了任何問題反而會(huì)增加開發(fā)成本芽卿。以我目前經(jīng)驗(yàn)來看Android開發(fā)至少一半的場(chǎng)景都很簡(jiǎn)單:請(qǐng)求-->拿數(shù)據(jù)-->渲染視圖最多再加個(gè)Data Mapper揭芍,流程很單一并且后期改動(dòng)的可能也不太大,這種情況就沒必要寫一個(gè)use case卸例,Data Mapper扔到數(shù)據(jù)層即可称杨。

2. 合理分層是給 數(shù)據(jù)驅(qū)動(dòng)UI 做鋪墊

先說結(jié)論:數(shù)據(jù)驅(qū)動(dòng)UI的本質(zhì)是控制反轉(zhuǎn)

2.1 什么是 控制反轉(zhuǎn)?

控制即對(duì)程序流程的控制筷转,一般由我們開發(fā)者承擔(dān)姑原,此過程為控制。但開發(fā)者是人所以不可避免出現(xiàn)錯(cuò)誤呜舒,此時(shí)可以將角色做一個(gè)反轉(zhuǎn)由成熟的框架負(fù)責(zé)整個(gè)流程锭汛,程序員只需要在框架預(yù)留的擴(kuò)展點(diǎn)上,添加跟自己的業(yè)務(wù)代碼袭蝗,就可以利用框架來驅(qū)動(dòng)整個(gè)程序流程的執(zhí)行唤殴,此過程為反轉(zhuǎn)

控制反轉(zhuǎn)概念和設(shè)計(jì)原則中的依賴倒置很相似到腥,只是少了一個(gè)依賴抽象眨八。

打個(gè)比方:

現(xiàn)有一個(gè)HTTP請(qǐng)求的需求,如果想自己維護(hù)HTTT鏈接左电、自己管理TCP Socket廉侧、自己處理HTTP緩存.....就是整個(gè)HTTP協(xié)議全部自己封裝,先不說這個(gè)工程能不能靠個(gè)人實(shí)現(xiàn)篓足,就算實(shí)現(xiàn)也是漏洞百出段誊,此時(shí)可以換個(gè)思路:通過OkHttp去實(shí)現(xiàn),OkHttp是一個(gè)成熟的框架用它基本上不會(huì)出錯(cuò)栈拖。個(gè)人封裝HTTP協(xié)議到使用OkHttp框架连舍,這個(gè)過程在控制HTTP的角色上發(fā)生了一個(gè)反轉(zhuǎn)個(gè)人--->成熟的框架OkHttp即控制反轉(zhuǎn)涩哟,好處也很明顯索赏,框架出錯(cuò)的概率遠(yuǎn)低于個(gè)人盼玄。

2.2 什么是數(shù)據(jù)驅(qū)動(dòng)UI?

通俗一點(diǎn)說就是當(dāng)數(shù)據(jù)改變時(shí)對(duì)應(yīng)的UI也要跟著變潜腻,反過來說當(dāng)需要改變UI只需要改變對(duì)應(yīng)的數(shù)據(jù)即可“6現(xiàn)在比較流行的UI框架如FlutterCompose融涣、Vue其本質(zhì)都是基于函數(shù)式編程實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)UI童番,它們共同的目的都是為了解決數(shù)據(jù),UI一致性問題威鹿。

在當(dāng)前的Android中可以使用DataBinding實(shí)現(xiàn)同樣的效果剃斧,以Jetpack MVVM為例:ViewModelRepository拿到數(shù)據(jù)暫存到ViewModel對(duì)應(yīng)的ObservableFiled即可實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)UI,但前提是從Repository拿到的數(shù)據(jù)可以直接用忽你,如果在Activity或者Adapter做數(shù)據(jù)二次處理再notify UI幼东,已經(jīng)違背數(shù)據(jù)驅(qū)動(dòng)UI核心思想。所以想實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)UI必須要有合理的分層(UI層拿到的數(shù)據(jù)無需處理科雳,可以直接用)筋粗,Data Mapper恰好解決這一問題,同時(shí)也可規(guī)避大量編寫BindAdapter的現(xiàn)狀炸渡。

DataBinding并非函數(shù)式編程娜亿,它只是通過AbstractProcessor生成中間代碼,將數(shù)據(jù)映射到XML中

2.3 為什么說數(shù)據(jù)驅(qū)動(dòng)UI底層思想是控制反轉(zhuǎn)蚌堵?

當(dāng)前Android生態(tài)能實(shí)現(xiàn)數(shù)據(jù)綁定UI的框架只有兩個(gè):DataBinding买决、Compose(暫不討論)

在引入DataBinding之前渲染一條數(shù)據(jù)通常需要兩步,如下:

var title = "iOS"
fun setTitle(){
     //第一步更改數(shù)據(jù)源
     title = "Android"
     //第二個(gè)更改UI
     textView = title
}

共需要兩步更改數(shù)據(jù)源吼畏、更改UI督赤,數(shù)據(jù)源UI有一個(gè)忘記修改便會(huì)出現(xiàn)BUG,千萬不要說:“兩個(gè)我都不會(huì)忘記修改”泻蚊,當(dāng)面臨復(fù)雜的邏輯以及十幾個(gè)甚至幾十個(gè)的數(shù)據(jù)源很難保證不出錯(cuò)躲舌。這種問題可以通過DataBinding解決,只需更改對(duì)應(yīng)的ObservableFiledUI便會(huì)同步修改性雄,控制UI狀態(tài)也從個(gè)人反轉(zhuǎn)到的DataBinding没卸,個(gè)人疏忽的事情DataBinding可不會(huì)。

所以說數(shù)據(jù)驅(qū)動(dòng)UI底層思想是控制反轉(zhuǎn)

2.4 為什么引入Diff秒旋?

引入diff之前:

RecyclerView想要實(shí)現(xiàn)動(dòng)態(tài)刪除约计、添加、更新需要分別手動(dòng)更新數(shù)據(jù)和UI迁筛,這樣在中間插了一道并且分別更新數(shù)據(jù)和UI已經(jīng)違背了前面所說的數(shù)據(jù)驅(qū)動(dòng)UI煤蚌,而我們想要的是不管刪除、添加或者更新只有一個(gè)入口,只要改變數(shù)據(jù)源就會(huì)驅(qū)動(dòng)UI做更新尉桩,想要滿足這一原則只能改變數(shù)據(jù)源后對(duì)RecyclerView做全部刷新筒占,但這樣會(huì)造成性能問題,復(fù)雜的界面會(huì)感到明顯的卡頓蜘犁。

引入diff之后:

Diff算法通過對(duì)oldItemnewItem做差異化比對(duì)翰苫,會(huì)自動(dòng)更新改變的item,同時(shí)支持刪除沽瘦、添加的動(dòng)畫效果,這一特性解決了RecyclerView需要實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)UI的性能問題

3 為什么我建議使用 函數(shù)式編程

3.1 什么是 函數(shù)式編程农尖?

  • 一個(gè)入口析恋,一個(gè)出口。
  • 不在函數(shù)鏈內(nèi)部執(zhí)行與運(yùn)算本身無關(guān)的操作
  • 不在函數(shù)鏈內(nèi)部使用外部變量(實(shí)際上這一條很難遵守盛卡,可以適當(dāng)突破)

說的通俗點(diǎn)就是給定一個(gè)初始值助隧,經(jīng)過函數(shù)鏈的運(yùn)行會(huì)得到一個(gè)目標(biāo)值,運(yùn)算的過程中外部沒有插手的權(quán)限滑沧,同時(shí)不做與本身無關(guān)的操作并村,從根本上解決了不可預(yù)期錯(cuò)誤的產(chǎn)生。

舉個(gè)例子:

//Kotlin代碼

listOf(10, 20).map {
   it + 1
}.forEach {
   Log.i("list", "$it")
}

上面這種鏈?zhǔn)骄幊叹褪菢?biāo)準(zhǔn)的函數(shù)式編程滓技,輸入到輸出之間開發(fā)者根本沒有插手的機(jī)會(huì)(即Log.i(..)之前開發(fā)者沒有權(quán)限處理list)哩牍,所以整個(gè)流程是100%安全的,RxJava令漂、Flow膝昆、鏈?zhǔn)礁唠A函數(shù)都是標(biāo)準(zhǔn)的函數(shù)式編程,它們從規(guī)范層面解決數(shù)據(jù)安全問題叠必。所以我建議在Kotlin中 碰到數(shù)據(jù)處理盡量使用鏈?zhǔn)礁唠A函數(shù)(RxJava荚孵、Kotlin Flow亦然)

其實(shí)函數(shù)式編程的核心思想就是 門面模式 以及 迪米特法則

3.2 Android視圖開發(fā)可以借鑒函數(shù)式編程思想

Android視圖開發(fā)大都遵循如下流程:請(qǐng)求-->處理數(shù)據(jù)-->渲染UI纬朝,這一流程可以借鑒函數(shù)式編程收叶,將請(qǐng)求作為入口,渲染做為出口共苛,在這個(gè)流程中盡量不做與當(dāng)前行為無關(guān)的事(這也要求ViewModel,Repository中的函數(shù)要符合單一原則)判没。這樣說有點(diǎn)籠統(tǒng),下面舉個(gè)反例:

    View{
        //刷新
        fun refresh(){
            ViewModel.load(true)
        }
        //加載更多
        fun loadMore(){
            ViewModel.load(false)
        }
    }

    ViewModel{
        //加載數(shù)據(jù)
        load(isRefresh){
            if (isRefresh){
                //刷新
            }else{
                //加載更多
            }
        }
    }


View層有刷新隅茎、加載更多兩種行為哆致,load(isRefresh)一個(gè)入口,兩個(gè)出口患膛。面臨的問題很明顯摊阀,修改刷新加載更多都會(huì)對(duì)對(duì)方產(chǎn)生影響,違反開閉原則中的閉(對(duì)修改關(guān)閉:行為沒變不準(zhǔn)修改源代碼),導(dǎo)致存在不可預(yù)期的問題產(chǎn)生胞此〕伎В可以借鑒函數(shù)式編程思想對(duì)其進(jìn)行改進(jìn),將ViewModelload函數(shù)拆分成refreshloadMore漱牵,這樣刷新加載更多兩種行為夺蛇、兩個(gè)入口、兩個(gè)出口互不干涉酣胀,通過函數(shù)的銜接形成兩條獨(dú)立的業(yè)務(wù)鏈條刁赦。

函數(shù)式編程可以約束我們寫出規(guī)范的代碼,面對(duì)不能使用函數(shù)式編程的場(chǎng)景闻镶,我們可以嘗試自我約束往函數(shù)式編程方向靠攏甚脉,大致也能實(shí)現(xiàn)相同的效果。

綜上所述

  • 合理的分層可以提升復(fù)用性铆农、降低模塊間耦合性
  • Data Mapper 可以讓視圖層脫離于后端進(jìn)行開發(fā)
  • 復(fù)雜的業(yè)務(wù)邏輯應(yīng)該寫到use case中
  • 數(shù)據(jù)驅(qū)動(dòng)UI的本質(zhì)是控制反轉(zhuǎn)
  • 通過函數(shù)式編程可以寫出更加安全的代碼

如果大家對(duì)Jetpack MVVM感興趣歡迎留言牺氨,下篇文章我可以寫一下自己的看法..

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市墩剖,隨后出現(xiàn)的幾起案子猴凹,更是在濱河造成了極大的恐慌,老刑警劉巖岭皂,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件郊霎,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡爷绘,警方通過查閱死者的電腦和手機(jī)歹篓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揉阎,“玉大人庄撮,你說我怎么就攤上這事”凶眩” “怎么了洞斯?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)坑赡。 經(jīng)常有香客問我烙如,道長(zhǎng),這世上最難降的妖魔是什么毅否? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任亚铁,我火速辦了婚禮,結(jié)果婚禮上螟加,老公的妹妹穿的比我還像新娘徘溢。我一直安慰自己吞琐,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布然爆。 她就那樣靜靜地躺著站粟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪曾雕。 梳的紋絲不亂的頭發(fā)上奴烙,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音剖张,去河邊找鬼切诀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛搔弄,可吹牛的內(nèi)容都是我干的幅虑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼肯污,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼翘单!你這毒婦竟也來了吨枉?” 一聲冷哼從身側(cè)響起蹦渣,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎貌亭,沒想到半個(gè)月后柬唯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡圃庭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年锄奢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剧腻。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拘央,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出书在,到底是詐尸還是另有隱情灰伟,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布儒旬,位于F島的核電站栏账,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏栈源。R本人自食惡果不足惜挡爵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望甚垦。 院中可真熱鬧茶鹃,春花似錦涣雕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至男杈,卻和暖如春丈屹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伶棒。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工旺垒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肤无。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓先蒋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親宛渐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子竞漾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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

  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料窥翩? 從這篇文章中你...
    hw1212閱讀 12,693評(píng)論 2 59
  • Android開發(fā)架構(gòu) 如果開發(fā)過程中大家各自為戰(zhàn)业岁,沒有統(tǒng)一規(guī)范,久而久之寇蚊,項(xiàng)目代碼會(huì)變得混亂且后續(xù)難以維護(hù)笔时。當(dāng)使...
    我愛田Hebe閱讀 1,598評(píng)論 0 4
  • 用到的組件 1、通過CocoaPods安裝 2仗岸、第三方類庫安裝 3允耿、第三方服務(wù) 友盟社會(huì)化分享組件 友盟用戶反饋 ...
    SunnyLeong閱讀 14,601評(píng)論 1 180
  • MVC mvc model view controller 模式視圖控制器 M: 業(yè)務(wù)邏輯處理 V:處理數(shù)據(jù)顯示的...
    andpy閱讀 467評(píng)論 1 0
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險(xiǎn)厭惡者,不喜歡去冒險(xiǎn)扒怖,但是人生放棄了冒險(xiǎn)较锡,也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 6,033評(píng)論 0 4