React Native 原生模塊開(kāi)發(fā) (Android)

這里有一個(gè)引導(dǎo) native-modules-setup,該引導(dǎo)適用于 android/iOS余黎,其實(shí)就是自動(dòng)創(chuàng)建原生模塊的模板哩簿,之后再去實(shí)現(xiàn)自己的邏輯就好了。

可以查看 native-modules-android翻譯 】耕赘、native-modules-ios翻譯】 來(lái)了解 android 、IOS 下模塊開(kāi)發(fā)的一些 API 和 開(kāi)發(fā)流程膳殷。

官網(wǎng)給了一個(gè) Toasts 的簡(jiǎn)單示例操骡,這里就不用這個(gè)了,直接實(shí)戰(zhàn)化赚窃,以集成友盟 SDK 作為例子(備注:以下過(guò)程是在 rn 0.61 版本下進(jìn)行的册招,版本不同,可能過(guò)程也有不同)勒极。

友盟的產(chǎn)品有很多是掰,統(tǒng)計(jì)/推送/分享/廣告監(jiān)測(cè)等等,分享和廣告監(jiān)測(cè)不太是剛需辱匿,且會(huì)和其他功能交叉(比如分享冀惭,集成微信分享的同時(shí)一般還會(huì)集成支付功能)所有就不集成了,這里僅集成 統(tǒng)計(jì)/推送 功能掀鹅。

目前行業(yè)現(xiàn)狀:

統(tǒng)計(jì),目前國(guó)內(nèi)用的較多的是 友盟騰訊移動(dòng)分析媒楼,二者在功能上乐尊,后者略有勝出,但也僅僅是略有勝出划址;

推送扔嵌,最好用的當(dāng)然是手機(jī)廠商的系統(tǒng)級(jí)推送通道限府,目前華為/小米/OPPO/vivo/魅族 都支持,這基本可以覆蓋國(guó)內(nèi) 70% (市場(chǎng)份額) 以上的機(jī)器了痢缎,第三方推送主要是處理剩余那 30% 的胁勺,據(jù)我個(gè)人了解,國(guó)內(nèi)口碑相對(duì)可以的是 極光 和 友盟独旷,況且友盟從 CNZZ 起家就開(kāi)始做統(tǒng)計(jì)了署穗,技術(shù)積累還是可以的。最后再考慮到注冊(cè)一個(gè)平臺(tái)就能用兩個(gè)功能了嵌洼,不用在不同平臺(tái)切換案疲,所以推送功能也選友盟。

一麻养、創(chuàng)建項(xiàng)目

// 安裝 create-react-native-module 
yarn global add create-react-native-module

// 創(chuàng)建 rn 項(xiàng)目
react-native init sample

// 進(jìn)入 rn 目錄創(chuàng)建 module
cd sample
create-react-native-module uapp

打開(kāi) sample 文件夾可以看到已創(chuàng)建了一個(gè) react-native-uapp 文件夾褐啡,打開(kāi)這個(gè)文件夾看看目錄結(jié)構(gòu)

- android
- index.js
- ios
- package.json
- react-native-uapp.podspec
- README.md

很貼心,連 readme 都幫忙創(chuàng)建好了鳖昌,不過(guò)我看了一下备畦,介紹主要還是針對(duì) rn6.0 以下版本的,后面還是要自己寫(xiě)點(diǎn)使用方法的许昨,暫且不管了懂盐,下面的主要工作就是在 android 和 ios 兩個(gè)文件夾進(jìn)行了,最后搞一下 index.js 對(duì)外提供接口车要。

二允粤、Android

1、修改命名空間

先來(lái)做 android 版本的翼岁,進(jìn)入 android 文件夾类垫,還有一個(gè) readme, 打開(kāi)看一下琅坡,是介紹如何打包為 maven 依賴的悉患,這個(gè)就沒(méi)必要了,可以刪了榆俺。按照慣例和自己準(zhǔn)備用名稱售躁,先改一下 android 的命名空間。

修改
src/main/java/com/reactlibrary/UappModule.java
src/main/java/com/reactlibrary/UappPackage.java
頭部從 package com.reactlibrary; 改為 package com.umreact.uapp;

移動(dòng)文件夾:
src/main/java/com/reactlib/ -> src/main/java/com/umreact/uapp

最后修改
build.gradlesrc/main/AndroidManifest.xml茴晋,查找 com.reactlibrary 改為 com.umreact.uapp

2陪捷、安裝本地包

使用 rn 一般都是用 yarn 來(lái)管理包的,所以我這里用 yarn 來(lái)安裝本地包诺擅,如果你使用 npm 自行搜索一下市袖,應(yīng)該都是差不多的,在 sample 目錄執(zhí)行

yarn add file:./react-native-uapp

這樣就把這個(gè)本地包裝上了烁涌,對(duì)于 android 而言苍碟,由于 rn 0.60 之后酒觅,是 autolink 的,所以無(wú)需其他操作了微峰,可以先 react-native run-android 試一下舷丹,看能不能編譯成功。

【注意】: 此時(shí)真正使用的 react-native-uapp 目錄是在 node_modules 下蜓肆,yarn add 命令自動(dòng)復(fù)制了颜凯,所以后續(xù)的原生開(kāi)發(fā)應(yīng)該是在 node_modules/react-native-uapp 目錄下。但實(shí)際使用的 js 模塊卻還在 file:./react-native-uapp 目錄下

3症杏、設(shè)置友盟 sdk

第一步是修改 android/build.gradle装获, 先來(lái)看看這個(gè)文件的結(jié)構(gòu)

// 內(nèi)置函數(shù), 用于安全獲取當(dāng)前使用的 android sdk 版本
// 嘗試使用主工程版本, 若無(wú), 則指定一個(gè)默認(rèn)版本
// 在 `android` 塊的配置中有使用
def safeExtGet(prop, fallback) {
   ..
}

// 把當(dāng)前包作為獨(dú)立工程的編譯配置
// 就是直接在當(dāng)前插件目錄執(zhí)行 `yarn install`,然后編譯當(dāng)前目錄
buildscript {
   ...
}

apply plugin: 'com.android.library'
apply plugin: 'maven'

// 當(dāng)前插件的編譯配置
android {
    ....
}

// 也是作為獨(dú)立工程的配置
// 關(guān)于 buildscript 和 repositories 和參考
// http://www.reibang.com/p/ee57e4de78a3
repositories {
   ....
}

// 當(dāng)前插件的依賴
dependencies {
  ...
}

// 在 afterEvaluate 中用到了
// 解析 package.json 中的包信息(名稱厉颤,作者穴豫,協(xié)議等)
def configureReactNativePom(def pom) {
  ....
}

// Gradle 構(gòu)建生命周期的鉤子
// 對(duì) android 不是特別熟悉,看這部分代碼像是打包逼友,可以發(fā)布到線上 lib 庫(kù)的
afterEvaluate { project ->
   ....
}

了解這個(gè)文件結(jié)構(gòu)后精肃,來(lái)改一下,就拿來(lái)做插件的帜乞,不需要做獨(dú)立工程司抱,可以去除相關(guān)代碼;也不需要發(fā)布為lib黎烈,所以也可以去除相關(guān)代碼习柠;當(dāng)然,這個(gè)修改不是必須的照棋,保留這些代碼也沒(méi)什么副作用资溃。

根據(jù)友盟的文檔 基礎(chǔ)組件集成U-Push 集成 烈炭、U-App 集成 可以看出溶锭,集成 sdk 有自動(dòng)和手動(dòng)兩種模式,自動(dòng)使用 https://dl.bintray.com/umsdk/release 作為 maven 中心庫(kù)符隙,手動(dòng)則是在 SDK下載 頁(yè)面下載 sdk趴捅,我這里選擇自動(dòng)化集成,后期維護(hù)升級(jí)也必將方便霹疫;找到需要集成的 sdk拱绑,有:

com.umeng.umsdk:utdid:version
com.umeng.umsdk:common:version
com.umeng.umsdk:analytics:version
com.umeng.umsdk:push:version

可以在 https://dl.bintray.com/umsdk/release/ 查看當(dāng)下的最新版本,最終修改 build.gradle 的代碼為

def safeExtGet(prop, fallback) {
    rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}

def safeCfgGet(prop, fallback) {
    return project.hasProperty(prop) ? project.getProperties().get(prop) : fallback
}

apply plugin: 'com.android.library'

android {
    compileSdkVersion safeExtGet('compileSdkVersion', 28)
    buildToolsVersion safeExtGet('buildToolsVersion', "28.0.3")
    defaultConfig {
        minSdkVersion safeExtGet('minSdkVersion', 16)
        targetSdkVersion safeExtGet('targetSdkVersion', 28)
        versionCode 1
        versionName "1.0"
        
        # 創(chuàng)建一個(gè)文件設(shè)置 與模塊相關(guān)的混淆規(guī)則
        consumerProguardFiles "proguard.pro"

        // +新增丽蝎,下面會(huì)進(jìn)行說(shuō)明
        buildConfigField("int", "UMENG_DEVICE_TYPE", safeCfgGet("UMENG_DEVICE_TYPE", "1"))
        buildConfigField("String", "UMENG_PUSH_SECRET", safeCfgGet("UMENG_PUSH_SECRET", "\"\""))
    }
    lintOptions {
        abortOnError false
    }
}

# 添加友盟的線上倉(cāng)庫(kù)地址
rootProject.allprojects {
    repositories {
        maven { url 'https://dl.bintray.com/umsdk/release' }
    }
}

dependencies {
    implementation 'com.facebook.react:react-native:+'

    implementation 'com.umeng.umsdk:utdid:1.1.5.3'
    implementation 'com.umeng.umsdk:common:2.1.0'
    implementation 'com.umeng.umsdk:analytics:8.1.3'
    implementation 'com.umeng.umsdk:push:6.0.1'
}

4猎拨、開(kāi)發(fā)

src/main/java/com/reactlibrary/UappPackage.java 無(wú)需改動(dòng)
主要是修改 src/main/java/com/reactlibrary/UappModule.java

通過(guò) 統(tǒng)計(jì)集成推送集成RN 生命周期事件 可以了解到

  1. 載入的 sdk 需要在 application.onCreate 階段初始化

  2. 統(tǒng)計(jì)模塊需要在 Activity 的 onResume 和 onPause 觸發(fā)統(tǒng)計(jì) 按照文檔其實(shí)在 auto 模式下是無(wú)需這一步的迟几,但實(shí)測(cè)了一下,發(fā)現(xiàn) auto 模式有問(wèn)題)

3.推送模塊需要在Activity 的 onCreate 調(diào)用 PushAgent.getInstance(context).onAppStart();

  1. RN 可以通過(guò) addLifecycleEventListener 在 Module 中添加 active 生命周期的監(jiān)聽(tīng)函數(shù)
// context - application 上下文
// appkey - 友盟申請(qǐng)得到的 app key
// channel - 渠道栏笆,方便后期統(tǒng)計(jì)查看(比如小米商店类腮、華為商店),可以為不同市場(chǎng)打包不同apk
// deviceType - 設(shè)備類型蛉加,支持手機(jī)或平板
// pushSecret - 推送秘鑰
UMConfigure.init(Context context, String appkey, String channel, int deviceType, String pushSecret);

// 少了 appkey/channel蚜枢,使用這個(gè)調(diào)用的前提是
// 在AndroidManifest.xml中配置過(guò)appkey和channel值
UMConfigure.init(Context context, int deviceType, String pushSecret);

從初始化函數(shù)調(diào)用可以看到, appkey针饥、channel厂抽、deviceType、pushSecret 作為配置信息丁眼,肯定不能在模塊中定義的筷凤,需要在主工程中設(shè)置,主工程要如何傳遞參數(shù)呢苞七,找到了幾篇有幫助的文章:gradle-tips藐守、buildConfigFieldgradle.properties

從這三篇文章可以看出,傳遞參數(shù)可以通過(guò) android 項(xiàng)目根目錄的 build.gradlegradle.properties蹂风,這兩個(gè)都是頂級(jí)配置文件卢厂,任何子工程(插件)都可以讀取配置值,很明顯的惠啄, gradle.properties 作為 kv 配置文件慎恒,特別適合做這個(gè)。

看完這三篇文章撵渡,應(yīng)該很容易理解上面 build.gradlebuildConfigField() 的作用了融柬,就是為了可以在 gradle.properties 配置友盟參數(shù),但沒(méi)有設(shè)置 appkeychannel姥闭,因?yàn)檫@兩個(gè)可以直接在 AndroidManifest.xml 中設(shè)置丹鸿,并且這樣更容易進(jìn)行分渠道打包,如果有興趣棚品,可以看看 build-variants

結(jié)合上面的信息靠欢,最終修改 UappModule.java

package com.umreact.uapp;

import android.content.Context;

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;

import android.util.Log;
import com.umeng.message.PushAgent;
import com.umeng.commonsdk.UMConfigure;
import com.umeng.analytics.MobclickAgent;
import com.umeng.message.IUmengRegisterCallback;

public class UappModule extends ReactContextBaseJavaModule implements LifecycleEventListener {

    public static void init(Context appContext) {
        // 開(kāi)啟 debug
        UMConfigure.setLogEnabled(true);

        // 初始化友盟
        UMConfigure.init(appContext, BuildConfig.UMENG_DEVICE_TYPE, BuildConfig.UMENG_PUSH_SECRET);

        // 設(shè)置 session 時(shí)長(zhǎng) (這里為了調(diào)試設(shè)置的比較小, 可以不設(shè)置,保持默認(rèn))
        MobclickAgent.setSessionContinueMillis(1000);

        // 設(shè)置統(tǒng)計(jì)模式 (不使用 auto 模式, 手工觸發(fā))
        MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.MANUAL);

        // 打印 測(cè)試統(tǒng)計(jì)設(shè)備 id
        String[] devices = UMConfigure.getTestDeviceInfo(appContext);
        Log.i("UMLog", "did:" + devices[0]);
        Log.i("UMLog", "mac:" + devices[1]);
        Log.i(
                "UMLog",
                "info:" + "{\"device_id\":\""+devices[0]+"\",\"mac\":\""+devices[1]+"\"}"
        );

        // 注冊(cè)推送
        final PushAgent mPushAgent = PushAgent.getInstance(appContext);
        mPushAgent.register(new IUmengRegisterCallback() {
            @Override
            public void onSuccess(String deviceToken) {
                Log.i("UMLog","注冊(cè)成功:deviceToken:-------->  " + deviceToken);
            }
            @Override
            public void onFailure(String s, String s1) {
                Log.e("UMLog","注冊(cè)失斖堋:-------->  " + "s:" + s + ",s1:" + s1);
            }
        });
    }

    /**
     * 不做特殊開(kāi)發(fā), 一般 rn 就一個(gè) MainActivity, 但這里也留一個(gè)接口
     * 如果有多個(gè) active 的話, 在 active 的 onCreate 調(diào)用
     * https://developer.umeng.com/docs/66632/detail/98581
     * @param activity
     */
    public static void onAppStart(Context activity) {
        PushAgent.getInstance(activity).onAppStart();
    }

    @Override
    public String getName() {
        return "Uapp";
    }

    public UappModule(ReactApplicationContext reactContext) {
        super(reactContext);
        reactContext.addLifecycleEventListener(this);

        // 這里調(diào)用, 設(shè)備總是處于離線狀態(tài), 無(wú)法接收推送消息
        // https://developer.umeng.com/docs/66632/detail/98581
        // 官方文檔提到: 務(wù)必在 Application類的 onCreate() 方法中做SDK代碼初始化工作
        // init(reactContext.getApplicationContext());
    }

    @Override
    public void onHostResume() {
        MobclickAgent.onResume(getCurrentActivity());
    }

    @Override
    public void onHostPause() {
        MobclickAgent.onPause(getCurrentActivity());
    }

    @Override
    public void onHostDestroy() {
        // do nothing
    }
}

代碼中調(diào)用的 BuildConfig.UMENG_DEVICE_TYPEBuildConfig.UMENG_PUSH_SECRET 是在 gradle.properties 文件中配置的

UMENG_DEVICE_TYPE=1
UMENG_PUSH_SECRET="666"

appkey门怪、channel 則在主工程的 AndroidManifest.xml 中配置

<manifest ..>
   ....
   // 添加友盟所需基本權(quán)限
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>

    <application ...>
    ....
        <meta-data android:value="666" android:name="UMENG_APPKEY"/>
        <meta-data android:value="Test" android:name="UMENG_CHANNEL"/>
     ...
    </application>
...
</manifest>

還需要在 android/src/main/.../MainApplication.java 添加一行

...
import com.umreact.uapp.UappModule;

public class MainApplication extends Application implements ReactApplication {
  ...
  @Override
  public void onCreate() {
    ..
    // 添加這一行
    UappModule.init(this);
    ..
  }
  ...
}

配置完成后,運(yùn)行 app锅纺;

1掷空、 從 android 的 log 中獲取到 {"device_id":"xxx","mac":"xxx"}友盟測(cè)試設(shè)備 去添加一下,然后在 實(shí)時(shí)日志 查看是否成功統(tǒng)計(jì),沒(méi)統(tǒng)計(jì)的話坦弟,可以嘗試重新運(yùn)行一次 app护锤。

2、從 Log 中找到 deviceToken酿傍,到 友盟推送工具 查詢?cè)O(shè)備狀態(tài)烙懦,如果是在線狀態(tài),可以到 測(cè)試模式 這里添加測(cè)試設(shè)備赤炒,并發(fā)送一條推送試一下氯析。

獲取 android log 有很多辦法,我們需要 filter 的關(guān)鍵詞為 UMLog

1莺褒、命令行: adb logcat *:S UMLog:V
2掩缓、打開(kāi) Android Studio 的 Logcat 窗口
3、使用 FB 的 flipper (這個(gè)可能需要 rn 版本大于0.60)

5遵岩、后記

最終的擴(kuò)展使用方法為

1你辣、在 gradle.properties 設(shè)置 UMENG_DEVICE_TYPEUMENG_PUSH_SECRET
2、在 android/src/main/AndroidManifest.xml 設(shè)置 UMENG_APPKEYUMENG_CHANNEL
3旷余、本想除友盟信息外零配置绢记,但奈何 android 開(kāi)發(fā)沒(méi)有經(jīng)驗(yàn),還需在 android/src/main/.../MainApplication.java 調(diào)用初始化方法

另外關(guān)于 MainApplication.java 中的配置正卧,可以看在 UappModule.javaUappModule() 函數(shù)的注釋蠢熄,一開(kāi)始是想在這里直接載入的,這樣就少了一個(gè)配置炉旷,但實(shí)測(cè)發(fā)現(xiàn)签孔,這樣做的話,可以注冊(cè)成功窘行,但設(shè)備總處于離線狀態(tài)饥追,無(wú)法接收推送消息变泄;并且在 推送接入文檔 中也有說(shuō)明:務(wù)必在工程的自定義Application類的 onCreate() 方法中做SDK代碼初始化工作幢码。所以最終也不再深究了,就先這么著吧(其實(shí)排查設(shè)備離線扶供,定位到是這里的問(wèn)題惶看,就花了我好幾個(gè)鐘頭捏顺,實(shí)在懶得繼續(xù)搞了)

上面的流程只是跑通了統(tǒng)計(jì)/推送功能,做一個(gè)能用的擴(kuò)展其實(shí)還有不少活要干纬黎,比如頁(yè)面統(tǒng)計(jì)幅骄、埋點(diǎn)統(tǒng)計(jì)、廠商推送通道集成本今、接收消息后的處理拆座、通過(guò) js 暴露接口給 rn jsx 使用主巍,本篇主要是為了記錄一下原生模塊開(kāi)發(fā)的流程,所有就不過(guò)多著墨了

三挪凑、iOS

待填坑....

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末孕索,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子躏碳,更是在濱河造成了極大的恐慌檬果,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,496評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件唐断,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡杭抠,警方通過(guò)查閱死者的電腦和手機(jī)脸甘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,187評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)偏灿,“玉大人丹诀,你說(shuō)我怎么就攤上這事∥檀梗” “怎么了铆遭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,091評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)沿猜。 經(jīng)常有香客問(wèn)我枚荣,道長(zhǎng),這世上最難降的妖魔是什么啼肩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,458評(píng)論 1 283
  • 正文 為了忘掉前任橄妆,我火速辦了婚禮,結(jié)果婚禮上祈坠,老公的妹妹穿的比我還像新娘害碾。我一直安慰自己,他們只是感情好赦拘,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,542評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布慌随。 她就那樣靜靜地躺著,像睡著了一般躺同。 火紅的嫁衣襯著肌膚如雪阁猜。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,802評(píng)論 1 290
  • 那天笋籽,我揣著相機(jī)與錄音蹦漠,去河邊找鬼。 笑死车海,一個(gè)胖子當(dāng)著我的面吹牛笛园,可吹牛的內(nèi)容都是我干的隘击。 我是一名探鬼主播,決...
    沈念sama閱讀 38,945評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼研铆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼埋同!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起棵红,我...
    開(kāi)封第一講書(shū)人閱讀 37,709評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤凶赁,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后逆甜,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體虱肄,經(jīng)...
    沈念sama閱讀 44,158評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,502評(píng)論 2 327
  • 正文 我和宋清朗相戀三年交煞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了咏窿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,637評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡素征,死狀恐怖集嵌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情御毅,我是刑警寧澤根欧,帶...
    沈念sama閱讀 34,300評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站端蛆,受9級(jí)特大地震影響凤粗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜今豆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,911評(píng)論 3 313
  • 文/蒙蒙 一侈沪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晚凿,春花似錦亭罪、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,744評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至燥筷,卻和暖如春箩祥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肆氓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,982評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工袍祖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谢揪。 一個(gè)月前我還...
    沈念sama閱讀 46,344評(píng)論 2 360
  • 正文 我出身青樓蕉陋,卻偏偏與公主長(zhǎng)得像捐凭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子凳鬓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,500評(píng)論 2 348

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