Android 手把手帶你搭建一個(gè)組件化項(xiàng)目架構(gòu)

一掏导、組件化

作為一個(gè)單工程擼到底的開發(fā)人員享怀,想試著將項(xiàng)目進(jìn)行組件化改造,說動就動趟咆。畢竟技術(shù)都是寫出來的添瓷,看著文章感覺懂了,但是實(shí)際開發(fā)中還是能遇到各種各樣的問題值纱,開始搞起來鳞贷。

1.1 為什么使用組件化

一直使用單工程擼到底,項(xiàng)目越來越大導(dǎo)致出現(xiàn)了不少的問題:

  • 查找問題慢:定位問題虐唠,需要在多個(gè)代碼混合的模塊中尋找和跳轉(zhuǎn)搀愧。

  • 開發(fā)維護(hù)成本增加:避免代碼的改動影響其它業(yè)務(wù)的功能,導(dǎo)致開發(fā)和維護(hù)成本不斷增加疆偿。

  • 編譯時(shí)間長:項(xiàng)目工程越大咱筛,編譯完整代碼所花費(fèi)的時(shí)間越長。

  • 開發(fā)效率低:多人協(xié)作開發(fā)時(shí)杆故,開發(fā)風(fēng)格不一迅箩,又很難將業(yè)務(wù)完全分割,大家互相影響处铛,導(dǎo)致開發(fā)效率低下饲趋。

  • 代碼復(fù)用性差:寫過的代碼很難抽離出來再次利用。

1.2 模塊化與組件化

1.2.1 模塊

一個(gè)程序按照其功能做拆分撤蟆,分成相互獨(dú)立的模塊奕塑,以便于每個(gè)模塊只包含與其功能相關(guān)的內(nèi)容,比如登錄模塊枫疆、首頁模塊等等爵川。

1.2.2 組件

組件指的是單一的功能組件,如登錄組件息楔、視頻組件寝贡、支付組件 等扒披,每個(gè)組件都可以以一個(gè)單獨(dú)的 module 開發(fā),并且可以單獨(dú)抽出來作為 SDK 對外發(fā)布使用圃泡〉福可以說往往一個(gè)模塊包含了一個(gè)或多個(gè)組件显拜。

1.3 組件化的優(yōu)勢

組件化基于可重用的目的坎藐,將應(yīng)用拆分成多個(gè)獨(dú)立組件,以減少耦合:

  • 加快編譯速度:每個(gè)業(yè)務(wù)功能都是一個(gè)單獨(dú)的工程胚鸯,可獨(dú)立編譯運(yùn)行风秤,拆分后代碼量較少鳖目,編譯自然變快。

  • 解耦:通過關(guān)注點(diǎn)分離的形式缤弦,將App分離成多個(gè)模塊领迈,每個(gè)模塊都是一個(gè)組件。

  • 提高開發(fā)效率:多人開發(fā)中碍沐,每個(gè)組件模塊由單人負(fù)責(zé)狸捅,降低了開發(fā)之間溝通的成本,減少因代碼風(fēng)格不一而產(chǎn)生的相互影響累提。

  • 代碼復(fù)用:類似我們引用的第三方庫尘喝,可以將基礎(chǔ)組件或功能組件剝離。在新項(xiàng)目微調(diào)或直接使用斋陪。

1.4 組件化需要解決的問題

  • 組件分層:怎么將一個(gè)項(xiàng)目分成多個(gè)組件朽褪、組件間的依賴關(guān)系是怎么樣的?

  • 組件單獨(dú)運(yùn)行和集成調(diào)試:組件是如何獨(dú)立運(yùn)行和集成調(diào)試的?

  • 組件間通信:主項(xiàng)目與組件鳍贾、組件與組件之間如何通信就變成關(guān)鍵?

二鞍匾、組件分層

組件依賴關(guān)系是上層依賴下層,修改頻率是上層高于下層骑科。先上一張圖:

image

2.1 基礎(chǔ)組件

基礎(chǔ)公共模塊橡淑,最底層的庫:

  • 封裝公用的基礎(chǔ)組件;
  • 網(wǎng)絡(luò)訪問框架、圖片加載框架等主流的第三方庫;
  • 各種第三方SDK咆爽。

2.2 common組件(lib_common)

  • 支撐業(yè)務(wù)組件梁棠、功能組件的基礎(chǔ)(BaseActivity/BaseFragment等基礎(chǔ)能力;
  • 依賴基礎(chǔ)組件層;
  • 業(yè)務(wù)組件、功能組件所需的基礎(chǔ)能力只需要依賴common組件即可獲得斗埂。

2.3 功能組件

  • 依賴基礎(chǔ)組件層;
  • 對一些公用的功能業(yè)務(wù)進(jìn)行封裝與實(shí)現(xiàn);
  • 業(yè)務(wù)組件可以在library和application之間切換符糊,但是最后打包時(shí)必須是library ;

2.4 業(yè)務(wù)組件

  • 可直接依賴基礎(chǔ)組件層;同時(shí)也能依賴公用的一些功能組件;
  • 各組件之間不存在依賴關(guān)系,通過路由進(jìn)行通信;
  • 業(yè)務(wù)組件可以在library和application之間切換呛凶,但是最后打包時(shí)必須是library ;

2.5 主工程(app)

  • 只依賴各業(yè)務(wù)組件;

  • 除了一些全局的配置和主Activity之外男娄,不包含任何業(yè)務(wù)代碼,是應(yīng)用的入口;

2.6 完成后項(xiàng)目

image

這只是個(gè)大概,并不是說必須這樣模闲,可以按照自己的方式來建瘫。比如:你覺得基礎(chǔ)組件比較多導(dǎo)致project里面的項(xiàng)目太多,那么你可以創(chuàng)建一個(gè)lib_base尸折,然在lib_base里面再創(chuàng)建其他基礎(chǔ)組件即可啰脚。

三、組件單獨(dú)調(diào)試

3.1 創(chuàng)建組件(收藏)

image
  • library和application之間切換:選擇第一項(xiàng)实夹。

  • 始終是library:選擇第二項(xiàng)

這樣盡可能的減少變動項(xiàng)橄浓,當(dāng)然這僅僅是個(gè)建議,看個(gè)人習(xí)慣吧亮航。

因?yàn)樵蹅儎?chuàng)建的是一個(gè)module荸实,所以在AndridManifest中添加android:exported="true"屬性可直接構(gòu)建一個(gè)APK。下面咱們看看如何生成不同的工程類型塞赂。

3.2 動態(tài)配置組件的工程類型

在 AndroidStudio 開發(fā) Android 項(xiàng)目時(shí)泪勒,使用的是 Gradle 來構(gòu)建,具體來說使用的是 Android Gradle 插件來構(gòu)建宴猾,Android Gradle 中提供了三種插件,在開發(fā)中可以通過配置不同的插件來構(gòu)建不同的工程叼旋。

3.2.1 build.gradle(module)

//構(gòu)建后輸出一個(gè) APK 安裝包
apply plugin: 'com.android.application'
//構(gòu)建后輸出 ARR 包
apply plugin: 'com.android.library'
//配置一個(gè) Android Test 工程
apply plugin: 'com.android.test'

獨(dú)立調(diào)試:設(shè)置為 Application 插件仇哆。

集成調(diào)試:設(shè)置為 Library 插件。

3.2.2 設(shè)置gradle.properties

image

isDebug = true 獨(dú)立調(diào)試

3.2.3 動態(tài)配制插件(build.gradle)

//注意gradle.properties中的數(shù)據(jù)類型都是String類型夫植,使用其他數(shù)據(jù)類型需要自行轉(zhuǎn)換
if(isDebug.toBoolean()){
    //構(gòu)建后輸出一個(gè) APK 安裝包
    apply plugin: 'com.android.application'
}else{
    //構(gòu)建后輸出 ARR 包
    apply plugin: 'com.android.library'
}

3.3 動態(tài)配置組件的 ApplicationId 和 AndroidManifest 文件

  • 一個(gè) APP 是只有一個(gè) ApplicationId 讹剔,所以在單獨(dú)調(diào)試集成調(diào)試組件的 ApplicationId 應(yīng)該是不同的。

  • 單獨(dú)調(diào)試時(shí)也是需要有一個(gè)啟動頁详民,當(dāng)集成調(diào)試時(shí)主工程和組件的AndroidManifest文件合并會產(chǎn)生多個(gè)啟動頁延欠。

根據(jù)上面動態(tài)配制插件的經(jīng)驗(yàn),我們也需要在build.gradle中動態(tài)配制ApplicationId 和 AndroidManifest 文件沈跨。

3.3.1 準(zhǔn)備兩個(gè)不同路徑的 AndroidManifest 文件

image

有什么不同由捎?咱們一起看看具體內(nèi)容。

3.3.2 src/main/debug/AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.scc.module.collect">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.SccMall">
        <activity android:name=".CollectActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

3.3.3 src/main/AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.scc.module.collect">
    <application
        android:allowBackup="true"
        android:supportsRtl="true"
        >
        <activity android:name=".CollectActivity"/>
    </application>

</manifest>

3.3.4 動態(tài)配制(build.gradle)

    defaultConfig {
        if(isDebug.toBoolean()){
            //獨(dú)立調(diào)試的時(shí)候才能設(shè)置applicationId
            applicationId "com.scc.module.collect"
        }
    }
    sourceSets {
        main {
            if (isDebug.toBoolean()) {
                //獨(dú)立調(diào)試
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            } else {
                //集成調(diào)試
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }

3.4 實(shí)現(xiàn)效果

3.4.1 獨(dú)立調(diào)試

isDebug = true

image

3.4.2 集成調(diào)試

isDebug = false

image

四饿凛、Gradle配置統(tǒng)一管理

4.1 config.gradle

當(dāng)我們需要進(jìn)行插件版本狞玛、依賴庫版本升級時(shí),項(xiàng)目多的話改起來很麻煩涧窒,這時(shí)就需要我們對Gradle配置統(tǒng)一管理心肪。如下:

image

具體內(nèi)容

ext{
    //組件獨(dú)立調(diào)試開關(guān), 每次更改值后要同步工程
    isDebug = true
    android = [
            // 編譯 SDK 版本
            compileSdkVersion: 31,
            // 最低兼容 Android 版本
            minSdkVersion    : 21,
            // 最高兼容 Android 版本
            targetSdkVersion : 31,
            // 當(dāng)前版本編號
            versionCode      : 1,
            // 當(dāng)前版本信息
            versionName      : "1.0.0"
    ]
    applicationid = [
            app:"com.scc.sccmall",
            main:"com.scc.module.main",
            webview:"com.scc.module.webview",
            login:"com.scc.module.login",
            collect:"com.scc.module.collect"
    ]
    dependencies = [
            "appcompat"         :'androidx.appcompat:appcompat:1.2.0',
            "material"          :'com.google.android.material:material:1.3.0',
            "constraintlayout"  :'androidx.constraintlayout:constraintlayout:2.0.1',
            "livedata"          :'androidx.lifecycle:lifecycle-livedata:2.4.0',
            "viewmodel"         :'androidx.lifecycle:lifecycle-viewmodel:2.4.0',
            "legacyv4"          :'androidx.legacy:legacy-support-v4:1.0.0',
            "splashscreen"      :'androidx.core:core-splashscreen:1.0.0-alpha01'
    ]
    libARouter= 'com.alibaba:arouter-api:1.5.2'
    libARouterCompiler = 'com.alibaba:arouter-compiler:1.5.2'
    libGson = 'com.google.code.gson:gson:2.8.9'
}


4.2 添加配制文件build.gradle(project)

apply from:"config.gradle"

4.3 其他組件使用

//build.gradle
//注意gradle.properties中的數(shù)據(jù)類型都是String類型,使用其他數(shù)據(jù)類型需要自行轉(zhuǎn)換
if(isDebug.toBoolean()){
    //構(gòu)建后輸出一個(gè) APK 安裝包
    apply plugin: 'com.android.application'
}else{
    //構(gòu)建后輸出 ARR 包
    apply plugin: 'com.android.library'
}
android {
    compileSdkVersion 31

    defaultConfig {
        if(isDebug.toBoolean()){
            //獨(dú)立調(diào)試的時(shí)候才能設(shè)置applicationId
            applicationId "com.scc.module.collect"
        }
        minSdkVersion 21
        targetSdkVersion 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets {
        main {
            if (isDebug.toBoolean()) {
                //獨(dú)立調(diào)試
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            } else {
                //集成調(diào)試
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
//    implementation root.dependencies.appcompat
//    implementation root.dependencies.material
//    implementation root.dependencies.constraintlayout
//    implementation root.dependencies.livedata
//    implementation root.dependencies.viewmodel
//    implementation root.dependencies.legacyv4
//    implementation root.dependencies.splashscreen
//    implementation root.libARouter
    //上面內(nèi)容在lib_common中已經(jīng)添加咱們直接依賴lib_common
    implementation project(':lib_common')

    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

五纠吴、組件間界面跳轉(zhuǎn)(ARouter)

5.1 介紹

Android 中的界面跳轉(zhuǎn)那是相當(dāng)簡單硬鞍,但是在組件化開發(fā)中,由于不同組件式?jīng)]有相互依賴的,所以不可以直接訪問彼此的類固该,這時(shí)候就沒辦法通過顯式的方式實(shí)現(xiàn)了锅减。

所以在這里咱們采取更加靈活的一種方式,使用 Alibaba 開源的 ARouter 來實(shí)現(xiàn)蹬音。

一個(gè)用于幫助 Android App 進(jìn)行組件化改造的框架 —— 支持模塊間的路由上煤、通信、解耦

文檔介紹的蠻詳細(xì)的著淆,感興趣的可以自己實(shí)踐一下劫狠。這里做個(gè)簡單的使用。

5.2 使用

5.2.1 添加依賴

先在統(tǒng)一的config.gradle添加版本等信息

ext{
    ...
    libARouter= 'com.alibaba:arouter-api:1.5.2'
    libARouterCompiler = 'com.alibaba:arouter-compiler:1.5.2'
}

因?yàn)樗械墓δ芙M件和業(yè)務(wù)組件都依賴lib_common永部,那么咱們先從lib_common開始配制

lib_common

dependencies {
    api root.libARouter
    ...
}

其他組件(如collect)

android {
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
                //如果項(xiàng)目內(nèi)有多個(gè)annotationProcessor独泞,則修改為以下設(shè)置
                //arguments += [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
}

dependencies {
    //arouter-compiler的注解依賴需要所有使用 ARouter 的 module 都添加依賴
    annotationProcessor root.libARouterCompiler
    ...
}

5.2.2 添加注解

你要跳轉(zhuǎn)的Activity

// 在支持路由的頁面上添加注解(必選)
// 這里的路徑需要注意的是至少需要有兩級,/xx/xx
@Route(path = "/collect/CollectActivity")
public class CollectActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_collect);
    }
}

5.2.3 初始化SDK(主項(xiàng)目Application)

public class App extends BaseApplication {
    @Override
    public void onCreate() {
        super.onCreate();
        if (isDebug()) {           // 這兩行必須寫在init之前苔埋,否則這些配置在init過程中將無效
            ARouter.openLog();     // 打印日志
            ARouter.openDebug();   // 開啟調(diào)試模式(如果在InstantRun模式下運(yùn)行懦砂,必須開啟調(diào)試模式!線上版本需要關(guān)閉,否則有安全風(fēng)險(xiǎn))
        }
        ARouter.init(this); // 盡可能早组橄,推薦在Application中初始化
    }
    private boolean isDebug() {
        return BuildConfig.DEBUG;
    }
}

5.3 發(fā)起路由操作

5.3.1 應(yīng)用內(nèi)簡單的跳轉(zhuǎn)

ARouter.getInstance().build("/collect/CollectActivity").navigation();

這里是用module_main的HomeFragment跳轉(zhuǎn)至module_collect的CollectActivity界面荞膘,兩個(gè)module中不存在依賴關(guān)系。"/collect/CollectActivity"在上面已注冊就不多描述了玉工。

效果如下:

image

5.3.2 跳轉(zhuǎn)并攜帶參數(shù)

這里是用module_main的MineFragment的Adapter跳轉(zhuǎn)至module_webview的WebViewActivity界面羽资,兩個(gè)module中同樣不存在依賴關(guān)系。

啟動方

        ARouter.getInstance().build("/webview/WebViewActivity")
                .withString("url", bean.getUrl())
                .withString("content",bean.getName())
                .navigation();

這里傳了兩個(gè)參數(shù)urlname到WebViewActivity遵班,下面咱們看看WebViewActivity怎么接收屠升。

接收方

//為每一個(gè)參數(shù)聲明一個(gè)字段,并使用 @Autowired 標(biāo)注
//URL中不能傳遞Parcelable類型數(shù)據(jù)狭郑,通過ARouter api可以傳遞Parcelable對象
//添加注解(必選)
@Route(path = "/webview/WebViewActivity")
public class WebViewActivity extends BaseActivity<ActivityWebviewBinding, WebViewViewModel> {
    //發(fā)送方和接收方定義的key名稱相同則無需處理
    @Autowired
    public String url;
    //通過name來映射URL中的不同參數(shù)
    //發(fā)送方定義key為content腹暖,我們用title來接收
    @Autowired(name = "content")
    public String title;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //注入?yún)?shù)和服務(wù)(這里用到@Autowired所以要設(shè)置)
        //不使用自動注入,可不寫,如CollectActivity沒接收參數(shù)就沒有設(shè)置
        ARouter.getInstance().inject(this);
        binding.btnBoom.setText(String.format("%s,你來啦", title));
        //加載鏈接
        initWebView(binding.wbAbout, url);
    }
}

上效果圖:

image

搞定翰萨,更多高級玩法可自行探索脏答。

5.3.3 小記(ARouter目標(biāo)不存在)

W/ARouter::: ARouter::There is no route match the path

這里出現(xiàn)個(gè)小問題,配置注釋都好好的缨历,但是發(fā)送發(fā)無論如何都找不到設(shè)置好的Activity以蕴。嘗試方案:

  • Clean Project
  • Rebuild Project
  • 在下圖也能找到ARouter內(nèi)容。

后來修改Activity名稱好了辛孵。

image

六丛肮、組件間通信(數(shù)據(jù)傳遞)

界面跳轉(zhuǎn)搞定了,那么數(shù)據(jù)傳遞怎么辦魄缚,我在module_main中使用懸浮窗宝与,但是需要判斷這個(gè)用戶是否已登錄焚廊,再執(zhí)行后續(xù)邏輯,這個(gè)要怎么辦习劫?這里我們可以采用 接口 + ARouter 的方式來解決咆瘟。

在這里可以添加一個(gè) componentbase 模塊,這個(gè)模塊被所有的組件依賴诽里。

這里我們通過 module_main組件 中調(diào)用 module_login組件 中的方法來獲取登錄狀態(tài)這個(gè)場景來演示袒餐。

6.1 通過依賴注入解耦:服務(wù)管理(一) 暴露服務(wù)

6.1.1 創(chuàng)建 componentbase 模塊(lib)

image

6.1.2 創(chuàng)建接口并繼承IProvider

注意:接口必須繼承IProvider,是為了使用ARouter的實(shí)現(xiàn)注入谤狡。

image

6.1.3 在module_login組件中實(shí)現(xiàn)接口

lib_common

所有業(yè)務(wù)組件和功能組件都依賴lib_common灸眼,所以咱們直接在lib_common添加依賴即可

dependencies {
    ...
    api project(":lib_componentbase")
}

module_login

dependencies {
    ...
    implementation project(':lib_common')
}

實(shí)現(xiàn)接口

//實(shí)現(xiàn)接口
@Route(path = "/login/AccountServiceImpl")
public class AccountServiceImpl implements IAccountService {
    @Override
    public boolean isLogin() {
        MLog.e("AccountServiceImpl.isLogin");
        return true;
    }

    @Override
    public String getAccountId() {
        MLog.e("AccountServiceImpl.getAccountId");
        return "1000";
    }

    @Override
    public void init(Context context) {

    }
}

image

6.2 通過依賴注入解耦:服務(wù)管理(二) 發(fā)現(xiàn)服務(wù)

6.2.1 在module_main中調(diào)用調(diào)用是否已登入

public class HomeFragment extends BaseFragment<FragmentHomeBinding> {
    @Autowired
    IAccountService accountService;
    @Override
    public void onViewCreated(@NonNull @NotNull View view, @Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        ARouter.getInstance().inject(this);
        binding.frgmentHomeFab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MLog.e("Login:"+accountService.isLogin());
                MLog.e("AccountId:"+accountService.getAccountId());

            }
        });
    }
}

image

運(yùn)行結(jié)果:

E/-SCC-: AccountServiceImpl.isLogin
E/-SCC-: Login:true
E/-SCC-: AccountServiceImpl.getAccountId
E/-SCC-: AccountId:1000

七、總結(jié)

本文介紹了組件化墓懂、組件分層焰宣、解決了組件的獨(dú)立調(diào)試、集成調(diào)試捕仔、頁面跳轉(zhuǎn)匕积、組件通信等。

其實(shí)會了這些后你基本可以搭建自己的組件化項(xiàng)目了榜跌。其實(shí)最大的問題還是分組分層闪唆、組件劃分。這個(gè)就需要根據(jù)你的實(shí)際情況來設(shè)置钓葫。

本項(xiàng)目比較糙苞氮,后面會慢慢完善。比如添加Gilde瓤逼、添加MMVK、添加Room等库物。

項(xiàng)目傳送門

本文轉(zhuǎn)自 https://juejin.cn/post/7033954652315975688霸旗,如有侵權(quán),請聯(lián)系刪除戚揭。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诱告,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子民晒,更是在濱河造成了極大的恐慌精居,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件潜必,死亡現(xiàn)場離奇詭異靴姿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)磁滚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門佛吓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宵晚,“玉大人,你說我怎么就攤上這事维雇∮偃校” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵吱型,是天一觀的道長逸贾。 經(jīng)常有香客問我,道長津滞,這世上最難降的妖魔是什么铝侵? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮据沈,結(jié)果婚禮上哟沫,老公的妹妹穿的比我還像新娘。我一直安慰自己锌介,他們只是感情好嗜诀,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著孔祸,像睡著了一般隆敢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上崔慧,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天拂蝎,我揣著相機(jī)與錄音,去河邊找鬼惶室。 笑死温自,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的皇钞。 我是一名探鬼主播悼泌,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼夹界!你這毒婦竟也來了馆里?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤可柿,失蹤者是張志新(化名)和其女友劉穎鸠踪,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體复斥,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡营密,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了永票。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卵贱。...
    茶點(diǎn)故事閱讀 40,505評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡滥沫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出键俱,到底是詐尸還是另有隱情兰绣,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布编振,位于F島的核電站缀辩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏踪央。R本人自食惡果不足惜臀玄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望畅蹂。 院中可真熱鬧健无,春花似錦、人聲如沸液斜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽少漆。三九已至臼膏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間示损,已是汗流浹背渗磅。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留检访,地道東北人始鱼。 一個(gè)月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像脆贵,于是被迫代替她去往敵國和親风响。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評論 2 359

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