Android 組件化實(shí)踐-最簡Demo

背景

  1. 什么是組件化?
    組件化就是模塊化,在Android工程實(shí)踐中可以實(shí)現(xiàn)單獨(dú)編譯搂橙、運(yùn)行澎怒、調(diào)試。 --個(gè)人見解

  2. 為什么要組件化宦棺?
    A. 解耦
    B. 代碼隔離
    C. 團(tuán)隊(duì)協(xié)作

  3. 組件化的具體操作?
    請(qǐng)看下文

組件化的具體操作

Git 倉庫地址:
https://github.com/Wangct23/Component

Demo整體架構(gòu)簡要說明

先介紹下整個(gè)Demo,分為應(yīng)用層踪宠,組件層,基礎(chǔ)層妈嘹。

應(yīng)用層為App柳琢,將所有組件結(jié)合起來。

組件層包含兩個(gè)組件:Login和Share〖砹常基礎(chǔ)層包含公共依賴他去。

Login組件負(fù)責(zé)用戶登錄,并記錄用戶的登錄狀態(tài)和用戶信息倒堕;

Share組件負(fù)責(zé)分享到第三方平臺(tái)灾测,在分享之前需要調(diào)用Login組件提供的服務(wù)來驗(yàn)證用戶是否登錄。

整個(gè)App的Module結(jié)構(gòu)是如下圖這樣的垦巴,詳細(xì)可以參看Demo中的build.gradle查看具體依賴關(guān)系:

在實(shí)際項(xiàng)目中在基礎(chǔ)層還應(yīng)包含網(wǎng)絡(luò)庫媳搪、圖片庫等基礎(chǔ)Library,本Demo旨在演示組件相關(guān)的基本操作骤宣,期望本文能夠把組件化的核心內(nèi)容表達(dá)清楚蛾号,相信明白組件化的核心思想和操作后,再往上進(jìn)階是水到渠成的事情涯雅。網(wǎng)上也流傳著不同大廠各自的實(shí)踐鲜结,均可以參考。實(shí)際使用的時(shí)候活逆,還是要以項(xiàng)目的業(yè)務(wù)特點(diǎn)以及團(tuán)隊(duì)的配置為基礎(chǔ)靈活選型和設(shè)計(jì)精刷,切忌生搬硬套。因此本文沒有將網(wǎng)絡(luò)請(qǐng)求之類的基礎(chǔ)庫包含在內(nèi)蔗候。

將Module單獨(dú)編譯運(yùn)行 V.S. 作為Library供其他Module依賴使用

背景:為什么需要單獨(dú)編譯運(yùn)行一個(gè)Module怒允?

當(dāng)團(tuán)隊(duì)規(guī)模達(dá)到一定數(shù)量時(shí),就要根據(jù)情況劃分為不同的小團(tuán)隊(duì)各自負(fù)責(zé)自己的業(yè)務(wù)锈遥。這種情況下纫事,不同Module之間定義好清晰的接口,各團(tuán)隊(duì)獨(dú)立互不影響的開發(fā)自己負(fù)責(zé)的業(yè)務(wù)所灸,并單獨(dú)編譯丽惶、運(yùn)行、調(diào)試爬立,以及Test钾唬,就顯得尤為重要了。

下面演示如何操作侠驯。

我們通過Android Studio在Project中先創(chuàng)建兩個(gè)Module:login-impl抡秆,以及share-impl。

以share-impl為例吟策,創(chuàng)建過程如下圖所示:

|

image

創(chuàng)建順序在上圖中以箭頭為標(biāo)示儒士,分別為圖1 -> 圖2 -> 圖3 -> 圖4,需要注意每一步中高亮選中的選項(xiàng)和Module Type檩坚。特別是圖2着撩,沒有選擇在創(chuàng)建普通Android Module時(shí)常用的Android Library诅福,而是選擇了Phone & Tablet Module,兩者的區(qū)別在于下圖:

|

image

左側(cè)是選擇Phone & Tablet Module后的默認(rèn)build.gradle 文件睹酌,右側(cè)是選擇 Android Library后默認(rèn)的build.gradle 文件权谁,兩者的主要區(qū)別在于紅框內(nèi)字段配置不一樣剩檀。左側(cè)可以作為一個(gè)獨(dú)立app單獨(dú)編譯運(yùn)行憋沿,不能作為library被其他Module依賴;右側(cè)可以作為一個(gè)library被其他Module依賴沪猴,不能作為獨(dú)立的app單獨(dú)編譯運(yùn)行辐啄。

下面我們也會(huì)通過條件變量的方式將當(dāng)前Module動(dòng)態(tài)設(shè)置為Library供其他Module依賴,或者設(shè)置為Application运嗜,單獨(dú)編譯運(yùn)行壶辜。

以login-impl為例:

|

image

如上圖所示,在login-impl目錄下担租,創(chuàng)建gradle.properties文件砸民,然后打開文件,添加 isRunAlone變量(名字隨意)奋救,并設(shè)置為true岭参。

然后修改login-impl目錄下的build.gradle 文件,如下圖所示尝艘,左側(cè)為修改前演侯,右側(cè)為修改后。

|

image

修改分為兩處:

  1. 在文件頭部背亥,將引入的plugin通過條件變量來控制秒际,當(dāng)isRunAlone為true時(shí),執(zhí)行plugins.apply('com.android.application')狡汉,將當(dāng)前Module作為Application單獨(dú)編譯運(yùn)行娄徊;當(dāng)isRunAlone為false時(shí),執(zhí)行plugins.apply('com.android.library')盾戴,將當(dāng)前Module作為Library供其他Module依賴使用嵌莉。
  2. 動(dòng)態(tài)設(shè)置是否添加 ?applicationId "com.wct.login_impl"?,作為Library時(shí)去掉捻脖,作為Application時(shí)執(zhí)行锐峭。

我們通過Android Studio可以很容易看出不同設(shè)置的區(qū)別,參看下圖:

|

image

  • (1)如上圖可婶,當(dāng)gradle.properties文件中isRunAlone設(shè)置為true時(shí)沿癞,通過Android Studio可以看到login-impl Module和app Module一樣,可以選中矛渴,并且可以運(yùn)行

|

image

(2)如上圖椎扬,當(dāng)gradle.properties文件中isRunAlone設(shè)置為false時(shí)惫搏,通過Android Studio可以看到login-impl Module和被標(biāo)記了×號(hào),這種情況下就不能單獨(dú)編譯運(yùn)行了蚕涤。我們點(diǎn)擊圖片頂部右側(cè)綠色運(yùn)行按鈕筐赔,會(huì)彈出Edit Configuration彈窗,并提示Error揖铜,如下圖:

|

image

動(dòng)態(tài)加載不同的AndroidManifest.xml 文件

首先茴丰,為什么要?jiǎng)討B(tài)加載不同的AndroidManifest.xml文件呢?

因?yàn)楫?dāng)一個(gè)Module作為Application時(shí)與作為Library時(shí)的構(gòu)造是不同的天吓。如下圖所示:

|

image

簡單來說贿肩,當(dāng)一個(gè)Module作為Library時(shí),不需要生命<application></application>內(nèi)的諸多必要元素龄寞,而作為Application汰规,這些都是不可或缺的,不然編譯器就會(huì)報(bào)錯(cuò)物邑。

接著溜哮,演示一下動(dòng)態(tài)加載不同AndroidManifest.xml文件的方式之一:

  1. 在login-impl目錄下創(chuàng)建manifest目錄,然后在剛才創(chuàng)建的manifest目錄下創(chuàng)建AndroidManifest.xml文件色解,我們期望將此文件作為獨(dú)立編譯運(yùn)行時(shí)會(huì)加載的文件茂嗓。因?yàn)槭仟?dú)立編譯運(yùn)行,因此和app Module下面的AndroidManifest.xml文件的結(jié)構(gòu)應(yīng)該是一致的冒签,因此可以直接將其拷貝過來在抛,然后修改下內(nèi)容就可以了,如下圖所示:

|

image

  1. 在build.gradle文件中根據(jù)我們之前在gradle.properties文件中設(shè)置的isRunAlone參數(shù)萧恕,動(dòng)態(tài)加載不同路徑的AndroidManifext.xml刚梭。如下代碼所示:
android {
    ...
    defaultConfig {
       ...
        sourceSets {
            main {
                // 單獨(dú)調(diào)試與集成調(diào)試時(shí)使用不同的 AndroidManifest.xml 文件
                if (isRunAlone.toBoolean()) {
                    manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
                } else {
                    manifest.srcFile 'src/main/AndroidManifest.xml'
                }
            }
        }
       ...
    }
    ...
}

以上,便是動(dòng)態(tài)加載不同AndroidManifest.xml文件的一種方法票唆。詳細(xì)內(nèi)容朴读,可以參見Demo源碼。

依賴隔離&代碼隔離

在Module內(nèi)部使用的類或方法走趋,很有可能會(huì)改動(dòng)或者刪除衅金,因此當(dāng)我們能直接訪問到其他Module 內(nèi)部使用的類和方法時(shí),會(huì)非常危險(xiǎn)簿煌。當(dāng)對(duì)方的類或方法內(nèi)部邏輯改動(dòng)時(shí)氮唯,很可能會(huì)導(dǎo)致我們自己的Module運(yùn)行結(jié)果出錯(cuò)。

在多團(tuán)隊(duì)協(xié)作的場(chǎng)景姨伟,為了避免在Moudle內(nèi)部使用的方法被外部調(diào)用惩琉,我們需要實(shí)現(xiàn)依賴隔離&代碼隔離 。在這部分文字中還將介紹一下整個(gè)組件化的樞紐:ServiceManager

下面介紹一種實(shí)現(xiàn)方法:

我們將每個(gè)組件分為 api 和 impl 兩個(gè)Module夺荒。其中api僅包含對(duì)外開放的接口和對(duì)應(yīng)的空實(shí)現(xiàn)瞒渠,impl包含接口的實(shí)現(xiàn)和其內(nèi)部邏輯良蒸,如下圖:

|

image

其中IAccountService是login組件對(duì)外提供的服務(wù),所有方法均在接口中聲明伍玖。需要獲取用戶的登錄信息的Module只允許依賴 api嫩痰,不允許依賴 impl,這從代碼的層級(jí)隔離了impl中的內(nèi)容窍箍。因此串纺,對(duì)于impl來說,只要保障api中對(duì)外接口的運(yùn)算結(jié)果不發(fā)生變化仔燕,我們可以根據(jù)業(yè)務(wù)需求自由的調(diào)整impl的內(nèi)部邏輯造垛,不用擔(dān)心會(huì)對(duì)外產(chǎn)生影響魔招。

再來看下具體怎么操作:

  1. 在分享組件中晰搀,我們需要用到用戶的登錄信息,因此需要依賴登錄組件的api Module办斑,即 login-api:

|

image

  1. 判斷用戶的登錄狀態(tài)外恕,當(dāng)用戶處于登錄狀態(tài)時(shí)允許分享,當(dāng)用戶處于未登錄狀態(tài)時(shí)禁止分享乡翅。

|[圖片上傳中...(image-81344c-1604741808414-1)]

如上圖所示鳞疲,我們?cè)赟hareService中,通過ServiceManager獲取到IAccountService的一個(gè)實(shí)例蠕蚜,然后調(diào)用其方法實(shí)現(xiàn)分享的邏輯尚洽。

接下來介紹一下各個(gè)組件之間的樞紐:ServiceManager

前面介紹了代碼隔離,各個(gè)組件通過依賴對(duì)方的api來獲取對(duì)應(yīng)的實(shí)現(xiàn)靶累,然后使用其提供的服務(wù)腺毫,那么,在不能通過new來獲取對(duì)方實(shí)例的情況下挣柬,怎么獲取呢潮酒?其實(shí)現(xiàn)的原理是什么呢?

答案是:反射邪蛔。

接下來介紹下具體操作:

  1. 首先新建一個(gè)componenbase Module急黎,作為所有組件的公共依賴庫,在componentbase中侧到,我們?yōu)楦鱾€(gè)組件提供服務(wù)獲取的能力勃教。
  2. 在 componnetbase中,我們創(chuàng)建IService接口匠抗,以及ServiceManager類故源。其中,IService是所有組件對(duì)外接口類的公共基類戈咳,每個(gè)接口都要繼承心软;在ServiceManager中壕吹,我們提供一個(gè)Map用來存放服務(wù)接口和對(duì)應(yīng)實(shí)現(xiàn)的映射。在各個(gè)組件初始化的時(shí)候删铃,將自己的實(shí)現(xiàn)注冊(cè)進(jìn)來耳贬,這里稍后會(huì)說明。文字描述起來有點(diǎn)費(fèi)解猎唁,看下代碼和注釋:
public class ServiceManager {
    private static Application sApplication;
    /**
     * 用來存放接口類和其對(duì)應(yīng)實(shí)例
     */
    private static final ConcurrentHashMap<Class, Object> SERVICES = new ConcurrentHashMap<>();

    public static void init(Application application) {
        sApplication = application;
    }

    public static Application getApplication() {
        return sApplication;
    }

    /**
     * 獲取對(duì)應(yīng)的接口的實(shí)例
     * @param clazz 接口類
     * @param <T>
     * @return 接口類對(duì)應(yīng)的實(shí)例咒劲。對(duì)于未注冊(cè)的接口類,返回其對(duì)應(yīng)的空實(shí)現(xiàn)诫隅。
     */
    public static <T extends IService> T getService(Class<T> clazz) {
        T impl = (T) SERVICES.get(clazz);
        if (impl == null) {
            impl = getEmptyImpl(clazz);
            registerService(clazz, impl);
        }
        return impl;
    }

    /**
     * 注冊(cè)接口類和其對(duì)應(yīng)的實(shí)現(xiàn)
     * @param clazz
     * @param obj
     * @param <T> 接口類
     */
    public static <T extends IService> void registerService(Class<T> clazz, T obj) {
        SERVICES.put(clazz, obj);
    }

    /**
     * 獲取接口類對(duì)應(yīng)的空實(shí)現(xiàn)
     * @param klass
     * @param <T>
     * @param <C>
     * @return
     */
    @NonNull
    private static <T, C> T getEmptyImpl(Class<C> klass) {
        String fullPackage = klass.getPackage().getName();
        String name = klass.getSimpleName();
        name = name.substring(1); //去掉接口類名稱的首字母 I腐魂,eg: IAccountService --> AccountService
        final String implName = fullPackage + "." + name + "Empty"; // AccountService --> AccountServiceEmpty
        try {
            @SuppressWarnings("unchecked") final Class<T> aClass = (Class<T>) Class.forName(implName);
            return aClass.newInstance();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("cannot find implementation for "
                    + klass.getCanonicalName() + ". " + implName + " does not exist");
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Cannot access the constructor"
                    + klass.getCanonicalName());
        } catch (InstantiationException e) {
            throw new RuntimeException("Failed to create an instance of "
                    + klass.getCanonicalName());
        }
    }
}

各個(gè)組件在初始化的時(shí)候?qū)⒆约簩?duì)外服務(wù)的實(shí)現(xiàn)注冊(cè)進(jìn)ServiceManager,以login-impl為例:

public class LoginApp extends BaseApp {
    ...
    @Override
    public void init(Application application) {
        Log.i("Application", "LoginApp init");
        ServiceManager.registerService(IAccountService.class, new AccountService());
    }
}

以上逐纬,介紹了一種實(shí)現(xiàn)代碼隔離&實(shí)現(xiàn)隔離的方法蛔屹。

資源沖突

當(dāng)不同組件中出現(xiàn)名稱相同的資源文件時(shí),先加載的文件會(huì)被后加載的文件覆蓋豁生,這當(dāng)然不是我們想要的兔毒。比如:不同組件都有返回按鈕back.png文件,而各自的樣式不同甸箱,那么如果都命名為back.png育叁,那么先加載的文件就會(huì)被后加載的文件覆蓋,那么最終所有組件展現(xiàn)出的返回按鈕都會(huì)變成最后加載的back.png的樣式芍殖,這當(dāng)然不是我們想要的豪嗽。

不過,這種情況很容易解決豌骏,團(tuán)隊(duì)之間只需要協(xié)商好彼此的命名規(guī)范即可龟梦,比如:在所有資源名稱前都加上組件的名稱作為前綴。

Gradle 的Android插件還提供了一種自動(dòng)檢查的方法肯适,以login組件為例变秦,當(dāng)我們?cè)赽uild.gradle中加上如下配置,那么gradle自動(dòng)檢查 res 中 xml 文件的命名是否以 "login_"開頭框舔,如果不是蹦玫,會(huì)報(bào)錯(cuò)。不過這種方法僅限于res 中的 xml 文件刘绣,對(duì)于圖片等資源需要開發(fā)者人工檢查了樱溉。

android {
    resourcePrefix "login_"
    // 其他配置 ...
}

插播:剩下的內(nèi)容不多了,如果你能堅(jiān)持讀到這里纬凤,我真的是非常開心了福贞。

頁面跳轉(zhuǎn)

在Android中常用的頁面跳轉(zhuǎn)方式有兩種:顯示Intent和隱式Intent,以及路由(底層實(shí)現(xiàn)也是startActivity())停士。本文介紹通過顯示Intent實(shí)現(xiàn)頁面跳轉(zhuǎn)的方式挖帘。

因?yàn)榻M件之間的代碼隔離完丽,無法直接訪問其他Module的類,因此無法直接通過startActivity來實(shí)現(xiàn)跳轉(zhuǎn)拇舀。不過逻族,可以反過來調(diào)用來實(shí)現(xiàn)。

舉例:

在AccountService類中骄崩,實(shí)現(xiàn)如下方法

//AccountService.java

@Override
public void startLoginActivity(Context context) {
    Intent intent = new Intent(context, LoginActivity.class);
    context.startActivity(intent);
}

在要跳轉(zhuǎn)LoginActivity頁面的組件中聘鳞,調(diào)用此方法就可以了:

//ShareActivity.java
public void shareLogin(View view) {
//        ARouter.getInstance().build("/loginimpl/login").navigation(); // 通過路由的方式實(shí)現(xiàn)頁面跳轉(zhuǎn)

        IAccountService accountService = ServiceManager.getService(IAccountService.class);
        accountService.startLoginActivity(this); //通過接口的方式實(shí)現(xiàn)頁面跳轉(zhuǎn)
    }

雖然沒辦法直接方位對(duì)方Module的類,也可以通過上面這種方式迂回的startActivity()要拂。

在Demo中也提供了通過ARouter實(shí)現(xiàn)頁面跳轉(zhuǎn)的示例抠璃,網(wǎng)絡(luò)上流行很多關(guān)于ARouter的介紹,而ARouter不屬于本文的重點(diǎn)脱惰,在這里就不詳細(xì)說了搏嗡。

動(dòng)態(tài)配置Application

前面講到,理論上每個(gè)組件都支持單獨(dú)編譯運(yùn)行以及與其他組件一起集成調(diào)試枪芒。那么在這兩種不同場(chǎng)景彻况,一般情況下都需要加載不同的Application谁尸。

做法也很簡單舅踪,以login-impl為例,

  1. 在build.gradle中修改配置:
android {
    ...
    defaultConfig {
       ...
        sourceSets {
            main {
                // 單獨(dú)調(diào)試與集成調(diào)試時(shí)使用不同的 AndroidManifest.xml 文件
                if (isRunAlone.toBoolean()) {
                   java {
                       ///當(dāng)此Module作為單獨(dú)的Application運(yùn)行時(shí)良蛮,加載 "src/component"路徑下的文件
                        srcDirs "src/component"
                   }
                } else {
                    manifest.srcFile 'src/main/AndroidManifest.xml'
                }
            }
        }
       ...
    }
    ...
}
  1. 在src/component 目錄下創(chuàng)建單獨(dú)運(yùn)行時(shí)的Application

|

image

  1. 修改之前配置的單獨(dú)運(yùn)行時(shí)生效的AndroidManifest.xml文件
<?xml version="1.0" encoding="utf-8"?>
<manifest  xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.wct.login_impl">
 <application
        android:name=".ComponentApp"`
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/login_app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/login_AppTheme">

      <activity  android:name=".LoginActivity">
         <intent-filter>
             <action  android:name="android.intent.action.MAIN" />
             <category  android:name="android.intent.category.LAUNCHER" />
         </intent-filter>

      </activity>
  </application>
</manifest>

總結(jié)

至此抽碌,關(guān)于組件化的核心內(nèi)容就介紹差不多了,需要再次說明的是决瞳,本文所述并非組件化的標(biāo)準(zhǔn)模板货徙,而是組件化萬般形態(tài)中的一種,而且是比較初級(jí)的一種皮胡。

另外限于篇幅痴颊,諸如更多數(shù)據(jù)通信的方式、路由屡贺、公共基礎(chǔ)庫的設(shè)計(jì)蠢棱、公共組件的抽取等內(nèi)容沒在文中擴(kuò)展。

這只是組件化的一個(gè)開始甩栈,實(shí)際的組件化過程遠(yuǎn)沒有這么簡單泻仙。

Git 倉庫地址:
https://github.com/Wangct23/Component

條條大路通紐約,在了解清楚業(yè)務(wù)模型以及思考清晰架構(gòu)目標(biāo)后量没,我們可以自由選擇不同的實(shí)現(xiàn)方式玉转。

_ 感謝閱讀 _

參考

美團(tuán)平臺(tái)化架構(gòu)演進(jìn)

Android徹底組件化實(shí)踐

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市殴蹄,隨后出現(xiàn)的幾起案子究抓,更是在濱河造成了極大的恐慌猾担,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刺下,死亡現(xiàn)場(chǎng)離奇詭異垒探,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)怠李,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門圾叼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人捺癞,你說我怎么就攤上這事夷蚊。” “怎么了髓介?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵惕鼓,是天一觀的道長。 經(jīng)常有香客問我唐础,道長箱歧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任一膨,我火速辦了婚禮呀邢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘豹绪。我一直安慰自己价淌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布瞒津。 她就那樣靜靜地躺著蝉衣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪巷蚪。 梳的紋絲不亂的頭發(fā)上病毡,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音屁柏,去河邊找鬼啦膜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛前联,可吹牛的內(nèi)容都是我干的功戚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼似嗤,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼啸臀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤乘粒,失蹤者是張志新(化名)和其女友劉穎豌注,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灯萍,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡轧铁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旦棉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片齿风。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖绑洛,靈堂內(nèi)的尸體忽然破棺而出救斑,到底是詐尸還是另有隱情,我是刑警寧澤真屯,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布脸候,位于F島的核電站,受9級(jí)特大地震影響绑蔫,放射性物質(zhì)發(fā)生泄漏运沦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一配深、第九天 我趴在偏房一處隱蔽的房頂上張望携添。 院中可真熱鬧,春花似錦凉馆、人聲如沸薪寓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锥腻,卻和暖如春嗦董,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瘦黑。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國打工京革, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幸斥。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓匹摇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親甲葬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子廊勃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353