從Android開發(fā)的角度去認(rèn)識Kotlin

在2017的Google I/O大會上绸吸,Google宣布倦淀,這門誕生于俄羅斯的年輕語言侈贷,即日起成為最新的一級安卓編程語言毕泌,并在Android Studio 3.0 已加入對其的支持环鲤。Kotlin是JetBrains設(shè)計(jì)并開源(最新開源版本為1.1.4)的一門靜態(tài)編程語言纯趋,由于設(shè)計(jì)者是IDE著名開發(fā)商JetBrains公司,Kotlin從一開始就自帶IDE 支持冷离。在Intellij IDEA 15和Android Studio3.0之前的版本需要安裝Kotlin插件吵冒,之后的版本自帶Kotlin插件。

Kotlin想給我們展現(xiàn)什么西剥?

1. 互操作與互兼容

談起Kotlin與Java痹栖,很多人估計(jì)會聯(lián)想起Swift與OC,Swift是蘋果于2014年WWDC(蘋果開發(fā)者大會)發(fā)布的新開發(fā)語言瞭空,可與Objective-C共同運(yùn)行于Mac OS和iOS平臺揪阿。根據(jù)了解,現(xiàn)在國內(nèi)一些大公司依然使用OC或者Swift與OC混編的方式開發(fā)iOS咆畏。這其中涉及很多原因:

  1. 早期版本的Swift編譯速度和運(yùn)行速度慢图甜,導(dǎo)致用戶覺得應(yīng)用卡;
  2. 用Swift開發(fā)打包后的安裝包比用OC的大;
  3. 再比如Swift的版本更新太快鳖眼,不太穩(wěn)定黑毅,開發(fā)者不得不花時(shí)間去適配到最新的Swift。
  4. Swift與OC并不能完全互操作钦讳,存在兼容性問題矿瘦,除此以外雖然Swift調(diào)用OC比較簡單枕面,但OC里用Swift比較麻煩。(源自簡書作者LingoGuo

但是Kotlin缚去,卻與Java有著100%的互操作和互兼容性潮秘,并在編譯速度和運(yùn)行速度上,與Java相比并未有劣勢可言:

  • 兼容性(Compatibility)—— Kotlin能兼容JDK 6易结,確保Kotlin的應(yīng)用程序在老版本的Android設(shè)備上運(yùn)行
  • 運(yùn)行速度(Performance)—— Kotlin 應(yīng)用程序的運(yùn)行速度與 Java 差不多枕荞,但是隨著Kotlin對內(nèi)聯(lián)函數(shù)的支持,使用 lambda 表達(dá)式的代碼通常比用 Java 寫的代碼運(yùn)行得更快
  • 互操作性(Interoperability)—— 用Java寫的類庫和代碼可以繼續(xù)在Kotlin的代碼中繼續(xù)沿用搞动,并支持Kotlin和Java兩種語言的混合編程
  • 占用空間(Footprint)—— Kotlin擁有一個(gè)緊密的runtime library躏精,在ProGuard的作用下減小了更多內(nèi)存的占用,在實(shí)際應(yīng)用程序中鹦肿,Kotlin 開發(fā)的apk比Java開發(fā)的apk增加不到 100K 的大小矗烛。
  • 編譯時(shí)間(Compilation Time)—— Kotlin 支持高效的增量編譯,所以對于清理構(gòu)建會有額外的開銷箩溃,增量構(gòu)建通常與 Java 一樣快或者更快(增量編譯只重新編譯代碼中更改的部分)
2. 易表現(xiàn)(簡潔)
  • 常量與變量

在Kotlin中瞭吃,變量用var聲明,常量用val聲明涣旨,val聲明的對象意味著它在實(shí)例化之后就不能再去改變它的狀態(tài)了歪架。如果你需要一個(gè)這個(gè)對象修改之后的版本,那就會再創(chuàng)建一個(gè)新的對象霹陡。這個(gè)讓編程更加具有健壯性和預(yù)估性:

val s = "Example" // A String
val actionBar = supportActionBar // An ActionBar in an Activity context
var i = 23 // An Int
var m = 23.4 // An Double

而在Java中和蚪,聲明一個(gè)對象不可變需要加final屬性,間接性一目明了:

private final String s = "Example"
  • 基本類型

在Kotlin中穆律,基本類型自帶轉(zhuǎn)化方法:

val i:Int = 7
val d:Double = i.toDouble()
val c:Char = 'c'
val i:Int = c.toInt()
  • 函數(shù)

Kotlin的函數(shù)可以給參數(shù)指定一個(gè)默認(rèn)值使得它們變得可選,如下导俘,第二個(gè)參數(shù)( length) 指定了一個(gè)默認(rèn)值峦耘,意味著調(diào)用的時(shí)候可以傳入第二個(gè)值或者不傳,這樣可以避免你需要的重載函數(shù):

fun toast(message: String, length: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this, message, length).show()
}
toast("Hello")
toast("Hello", Toast.LENGTH_LONG)

這個(gè)與下面的Java代碼是一樣的旅薄,明顯Kotlin更加易于表現(xiàn):

void toast(String message){
    Toast.makeText(this, message, Toast.LENGTH_LONG).show();
} 

void toast(String message, int length){
    Toast.makeText(this, message, length).show();
}

在Java中辅髓,如果我們要典型的數(shù)據(jù)類,我們需要去編寫(至少生成) 這些代碼:

public class Artist {
    private long id;
    private String name;
    private String url;
    private String mbid;

    public long getId() {
        return id;
    } 

    public void setId(long id) {
        this.id = id;
    } 

    public String getName() {
        return name;
    }
  
    public void setName(String name) {
        this.name = name;
    } 

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    } 

    @Override 
    public String toString() {
        return "Artist{" + "id=" + id + ", name='" + name + '\'' + ", url='" + url + '\'' + '}';
    }
}

使用Kotlin少梁,我們只需要通過數(shù)據(jù)類洛口,這個(gè)數(shù)據(jù)類,它會自動生成所有屬性和它們的訪問器凯沪,以及一些有用的方法第焰,比如toString():

data class Artist(
    var id: Long,
    var name: String,
    var url: String,
    var mbid: String)

如果我們使用不可修改的對象,假如我們需要修改這個(gè)對象狀態(tài)妨马,必須要創(chuàng)建一個(gè)新的一個(gè)或者多個(gè)屬性被修改的實(shí)例挺举。這個(gè)任務(wù)在java里是非常重復(fù)且不簡潔的杀赢,然后Kotlin可以這樣實(shí)現(xiàn):

val f1 = Forecast(Date(), 27.5f, "Shiny day")
val f2 = f1.copy(temperature = 30f)

而且Kotlin還支持映射對象的每一個(gè)屬性到一個(gè)變量中:

val f1 = Forecast(Date(), 27.5f, "Shiny day")
val (date, temperature, details) = f1

在Java中我們需要這樣去實(shí)現(xiàn):

Forecast f1 = new Forecast(Date(), 27.5f, "Shiny day");
Date date = f1.getDate();
float temperature = f1.getTemperature();
String details = f1.getDetails();
  • 操作符重載

在Kotlin中,每個(gè)操作符都有對應(yīng)的操作方法湘纵,如:

操作符 對應(yīng)方法
a + b a.plus(b)
a - b a.minus(b)
a * b a.times(b)
a / b a.div(b)

正如這兩個(gè)表達(dá)式是一樣的意思:

var num:Int = 1
num = num.plus(1)      // 與num = num + 1效果一樣

這也就提供了重載操作符(擴(kuò)展操作符)的方法:

fun main(args: Array<String>){
    var num1 = Number(1, 1)
    var num2 = Number(1, 1)
    println((num1 + num2).toString())
}

operator fun Number.plus(other: Number):Number{
    this.one = this.one + other.one
    this.two = this.two + other.two
    return this
}

data class Number(
        var one: Int,
        var two: Int
)

輸出結(jié)果為

Number(one=2, two=2)

  • 強(qiáng)大的list
val list = listOf(1, 2, 3, 4, 5, 6)
list.any { it % 2 == 0 }         // 如果至少有一個(gè)元素符合給出的判斷條件脂崔,則返回true
list.all { it % 2 == 0 }         // 如果全部的元素符合給出的判斷條件,則返回true
list.count { it % 2 == 0 }       // 返回符合給出判斷條件的元素總數(shù)
list.forEach { println(it) }     // 遍歷所有元素梧喷,并執(zhí)行給定的操作
list.forEachIndexed { index, value -> println("position $index contains a $value") }
                                 // 與 forEach 砌左,但是我們同時(shí)可以得到元素的index
list.max()                       // 返回最大的一項(xiàng),如果沒有則返回null
list.maxBy { -it }               // 根據(jù)給定的函數(shù)返回最大的一項(xiàng)铺敌,如果沒有則返回null
list.min()                       // 返回最小的一項(xiàng)汇歹,如果沒有則返回null
list.minBy { -it }               // 根據(jù)給定的函數(shù)返回最小的一項(xiàng),如果沒有則返回null
list.sumBy { it % 2 }            // 返回所有每一項(xiàng)通過函數(shù)轉(zhuǎn)換之后的數(shù)據(jù)的總和

list.drop(4)                     // 返回包含去掉前n個(gè)元素的所有元素的列表
list.filter { it % 2 == 0 }      // 過濾所有符合給定函數(shù)條件的元素

list.contains(2)                 // 如果指定元素可以在集合中找到适刀,則返回true
list.elementAt(1)                // 返回給定index對應(yīng)的元素
list.first { it % 2 == 0 }       // 返回符合給定函數(shù)條件的第一個(gè)元素
list.indexOf(4)                  // 返回指定元素的第一個(gè)index秤朗,如果不存在,則返回 -1 
list.indexOfFirst { it % 2 == 0 }             
                                 // 返回第一個(gè)符合給定函數(shù)條件的元素的index笔喉,如果沒有符合則返回 -1 
list.indexOfLast{ it % 2 == 0 }  // 返回最后一個(gè)符合給定函數(shù)條件的元素的index取视,如果沒有符合則返回 -1

list.reverse()                   // 返回一個(gè)與指定list相反順序的list
list.sort()                      // 返回一個(gè)自然排序后的list
list..sortBy { it % 3 }          // 返回一個(gè)根據(jù)指定函數(shù)排序后的list

......
  • 流程控制

if表達(dá)式可實(shí)現(xiàn)賦值操作:

val z = if (condition) x else y

when表達(dá)式代替switch/case

when (x){
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> {
        print("I'm a block")
        print("x is neither 1 nor 2")
    }
}

val result = when (x) {
    0, 1 -> "binary"
    else -> "error"
}

when(view) {
    is TextView -> view.setText("I'm a TextView")
    is EditText -> toast("EditText value: ${view.getText()}")
    is ViewGroup -> toast("Number of children: ${view.getChildCount()} ")
    else -> view.visibility = View.GONE
}

val cost = when(x) {
    in 1..10 -> "cheap"
    in 10..100 -> "regular"
    in 100..1000 -> "expensive"
    in specialValues -> "special value!"
    else -> "not rated"
}

val res = when{
    x in 1..10 -> "cheap"
    s.contains("hello") -> "it's a welcome!"
    v is ViewGroup -> "child count: ${v.getChildCount()}"
    else -> ""
}

Range 表達(dá)式使用一個(gè) .. 操作符,它是被定義實(shí)現(xiàn)了一個(gè) RangTo 方法常挚。Ranges 幫助我們使用很多富有創(chuàng)造性的方式去簡化我們的代碼作谭。比如我們可以把它:

if(i >= 0 && i <= 10) println(i)

轉(zhuǎn)化成:

if (i in 0..10) println(i)
3. 空安全

我們有時(shí)候的確需要去定義一個(gè)變量包不包含一個(gè)值。在Java中盡管注解和IDE在這方面幫了我們很多奄毡,但是我們?nèi)匀豢梢赃@么做:

Forecast forecast = null;
forecast.toString();

這個(gè)代碼可以被完美地編譯( 你可能會從IDE上得到一個(gè)警告) 折欠,然后正常地執(zhí)行,但是顯然它會拋一個(gè)NullPointerException 吼过。這個(gè)相當(dāng)不安全的锐秦。當(dāng)然,在Kotlin中盗忱,也可以有一個(gè)可null的對象(用酱床?標(biāo)記):

val a: Int? = null

然而一個(gè)可null類型,你在沒有進(jìn)行檢查之前你是不能直接使用它趟佃,這個(gè)代碼不能被編譯:

val a: Int? = null
a.toString()        // 編譯失敗
a?.toString()       // 編譯成功
if(a!=null){        // 編譯成功
    a.toString()
}
4. 可擴(kuò)展方法

Kotlin允許我們給任何類添加函數(shù)扇谣。它比那些我們項(xiàng)目中典型的工具類更加具有可讀性。舉個(gè)例子闲昭,我們可以給fragment增加一個(gè)顯示toast的函數(shù):

fun Fragment.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(getActivity(), message, duration).show()
}

我們現(xiàn)在可以這么做:

fragment.toast("Hello world!")

注:擴(kuò)展函數(shù)并不是真正地修改了原來的類罐寨,它是以靜態(tài)導(dǎo)入的方式來實(shí)現(xiàn)的。擴(kuò)展函數(shù)可以被聲明在任何文件中序矩,因此有個(gè)通用的實(shí)踐是把一系列有關(guān)的函數(shù)放在一個(gè)新建的文件里鸯绿。

5. 函數(shù)式支持(lambda)

一個(gè)lambda表達(dá)式通過參數(shù)的形式被定義在箭頭的左邊( 被圓括號包圍) ,然后在箭頭的右邊返回結(jié)果值。在這個(gè)例子中楞慈,我們接收一個(gè)View幔烛,然后返回一個(gè)Unit( 沒有東西) 。所以根據(jù)這種思想囊蓝,我們可以把前面的代碼簡化成這樣:

view.setOnClickListener({ view -> toast("Click")})

這是非常棒的簡化饿悬!當(dāng)我們定義了一個(gè)方法,我們必須使用大括號包圍聚霜,然后在箭頭的左邊指定參數(shù)狡恬,在箭頭的右邊返回函數(shù)執(zhí)行的結(jié)果。如果左邊的參數(shù)沒有使用到蝎宇,我們甚至可以省略左邊的參數(shù):

view.setOnClickListener({ toast("Click") })

如果這個(gè)函數(shù)的最后一個(gè)參數(shù)是一個(gè)函數(shù)弟劲,我們可以把這個(gè)函數(shù)移動到圓括號外面:

view.setOnClickListener() { toast("Click") }

并且,最后姥芥,如果這個(gè)函數(shù)只有一個(gè)參數(shù)兔乞,我們可以省略這個(gè)圓括號:

view.setOnClickListener { toast("Click") }

比原始的Java代碼簡短了5倍多,并且更加容易理解它所做的事情凉唐,非常讓人影響深刻庸追。

Kotlin Android Extensions有什么用?

Kotlin Android Extensions是Kotlin團(tuán)隊(duì)研發(fā)的可以讓開發(fā)更簡單的插件台囱,Kotlin Android Extensions自動創(chuàng)建了屬性讓開發(fā)者直接訪問XML中的view淡溯,而不需要在開始使用之前明確地從布局中去找到這些views。

注:屬性的名字就是來自對應(yīng)view的id簿训,所以取id的時(shí)候要十分小心咱娶,因?yàn)樗鼈儗俏覀冾愔蟹浅V匾囊徊糠帧_@些屬性的類型也是來自XML中的强品,所以不需要去進(jìn)行額外的類型轉(zhuǎn)換膘侮。

在使用的時(shí)候需要我們需要使用import 語句,以 kotlin.android.synthetic 開頭的榛,然后加上需要綁定到Activity的布局XML的名字:

import kotlinx.android.synthetic.activity_main.*

然后就可以直接使用View對象了:

class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        textView.setText("Hello, world!")
        // Instead of findViewById(R.id.textView) as TextView
    }
}

那它背后是怎么工作的琼了?其實(shí)正如Kotlin支持?jǐn)U展方法一樣,Kotlin也支持擴(kuò)展屬性困曙,通過獲取布局文件的控件id表伦,以其為名添加相應(yīng)控件作為該Activity的擴(kuò)展屬性谦去,并與布局中的控件通過findViewById等方式獲取控件實(shí)例(映射)慷丽。

該插件會代替任何屬性調(diào)用函數(shù),比如獲取到view鳄哭,并具有緩存功能要糊,以免每次屬性被調(diào)用都會去重新獲取這個(gè)view。需要注意的是這個(gè)緩存裝置只會在 Activity 或者 Fragment 中才有效妆丘。如果它是在一個(gè)擴(kuò)展函數(shù)中增加的锄俄,這個(gè)緩存就會被跳過局劲,因?yàn)樗梢员挥迷?Activity 中但是插件不能被修改,所以不需要再去增加一個(gè)緩存功能奶赠。

Anko鱼填!強(qiáng)大?方便毅戈?

Anko是JetBrains開發(fā)的一個(gè)強(qiáng)大的庫苹丸。它主要的目的是用來替代以前XML的方式來使用代碼生成UI布局。如:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_test)

    linearLayout {
        button("Login") {
            textSize = dip(16)
            onclick{
                clickButton()
            }
        }.lparams(width = wrapContent) {
             horizontalMargin = dip(5)
             topMargin = dip(10)
        }
    }
}

以上代碼相當(dāng)于:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Login"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:layout_marginTop="10dp"/>

</LinearLayout>

這種通過代碼生成UI布局的方式雖然方便苇经,但是赘理,這種由代碼生成布局的方式,不利于控制器(Controller)與視圖(View)的分離扇单,處理不當(dāng)可能會造成Activity代碼臃腫商模;而且這種方式不支持視圖的預(yù)覽,必須運(yùn)行之后才能看清楚效果蜘澜。從個(gè)人的UI繪制經(jīng)驗(yàn)出發(fā)施流,使用XML會更容易一些。

但是兼都,Anko還是有其優(yōu)勢的嫂沉,最重要的一點(diǎn)就是上方提及的擴(kuò)展函數(shù)(方法),擴(kuò)展函數(shù)數(shù)是指在一個(gè)類上增加一種新的行為扮碧,甚至我們沒有這個(gè)類代碼的訪問權(quán)限趟章。另外,Anko也支持?jǐn)U展屬性:

public var TextView.text: CharSequence
    get() = getText()
    set(v) = setText(v)

所以慎王,當(dāng)你在Fragment中看見activity的引用時(shí)不要驚訝蚓土,那其實(shí)就是使用getActivity()的擴(kuò)展屬性,再如當(dāng)給ListView設(shè)置適配器時(shí):

listview.adapter = mAdapter;          // .adapter等同于set/getAdapter

還有赖淤,當(dāng)一個(gè)Listener有多個(gè)方法時(shí)蜀漆,Anko就顯得很方便了, 看下面的代碼(沒有使用Anko):

seekBar.setOnSeekBarChangeListener(object: OnSeekBarChangeListener {
    override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
        // Something
    }
    override fun onStartTrackingTouch(seekBar: SeekBar?) {
        // Just an empty method
    }
    override fun onStopTrackingTouch(seekBar: SeekBar) {
        // Another empty method
    }
})

使用了Anko之后:

seekBar {
    onSeekBarChangeListener {
        onProgressChanged { seekBar, progress, fromUser ->
            // Something
        }
    }
}

我相信咱旱,當(dāng)越來越多的Android開發(fā)者認(rèn)識到Kotlin的魅力后确丢,Kotlin會真正成為Android開發(fā)的主流語言,以上的分享也只是簡單的認(rèn)識吐限,如果讀者有發(fā)現(xiàn)Kotlin更加誘人的魅力鲜侥,希望能夠多多分享,小牧在此先謝過啦(真誠臉)诸典。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末描函,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌舀寓,老刑警劉巖胆数,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異互墓,居然都是意外死亡必尼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門篡撵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胰伍,“玉大人,你說我怎么就攤上這事酸休÷钭猓” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵斑司,是天一觀的道長渗饮。 經(jīng)常有香客問我,道長宿刮,這世上最難降的妖魔是什么互站? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮僵缺,結(jié)果婚禮上胡桃,老公的妹妹穿的比我還像新娘。我一直安慰自己磕潮,他們只是感情好翠胰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著自脯,像睡著了一般之景。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上膏潮,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天锻狗,我揣著相機(jī)與錄音,去河邊找鬼焕参。 笑死轻纪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的叠纷。 我是一名探鬼主播刻帚,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼讲岁!你這毒婦竟也來了我擂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤缓艳,失蹤者是張志新(化名)和其女友劉穎校摩,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阶淘,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡衙吩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了溪窒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坤塞。...
    茶點(diǎn)故事閱讀 40,115評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖澈蚌,靈堂內(nèi)的尸體忽然破棺而出摹芙,到底是詐尸還是另有隱情,我是刑警寧澤宛瞄,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布浮禾,位于F島的核電站,受9級特大地震影響份汗,放射性物質(zhì)發(fā)生泄漏盈电。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一杯活、第九天 我趴在偏房一處隱蔽的房頂上張望匆帚。 院中可真熱鬧,春花似錦旁钧、人聲如沸吸重。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽晤锹。三九已至,卻和暖如春彤委,著一層夾襖步出監(jiān)牢的瞬間鞭铆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工焦影, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留车遂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓斯辰,卻偏偏與公主長得像舶担,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子彬呻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評論 2 355

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