Android Weekly Notes Issue #223

Android Weekly Issue #223

September 18th, 2016
Android Weekly Issue #223

本期內(nèi)容包括:
Offline時(shí)間戳處理; Accessibility的安全問(wèn)題可能并不是個(gè)問(wèn)題; 如何在單元測(cè)試和UI測(cè)試之間共享代碼; Android中的指紋認(rèn)證; 編譯時(shí)間Kotlin vs Java; MVP結(jié)合RxJava, 讓View來(lái)處理生命周期; RxJava2預(yù)覽; 內(nèi)存泄露處理; Gradle相關(guān)等等.

ARTICLES & TUTORIALS

Offline First: Introducing TrueTime for Android

TrueTime是一個(gè)NTP library for Swift and Android.

其中NTP是Network Time Protocol.

作者他們有一個(gè)購(gòu)物app, 但是時(shí)斷時(shí)續(xù)的網(wǎng)絡(luò)降低了用戶(hù)體驗(yàn), 所以他們進(jìn)行了離線(xiàn)遷移, 準(zhǔn)備出一系列文章分享相關(guān)的想法和在此過(guò)程中學(xué)到的東西.

本文是第一篇, 關(guān)于時(shí)間.

由于在設(shè)置里可以設(shè)置設(shè)備的日期和時(shí)間, 所以設(shè)備的時(shí)間并不一定是真實(shí)的時(shí)間, 我們?cè)诔绦蚶?code>new Date()得到的其實(shí)是設(shè)備時(shí)間.

關(guān)于真實(shí)時(shí)間的計(jì)算, 他們開(kāi)源了TrueTime庫(kù), Android和iOS都能用.

TrueTime如何計(jì)算真實(shí)時(shí)間的呢? 它其實(shí)是向NTP的server發(fā)了請(qǐng)求, 然后計(jì)算出的.

文中和庫(kù)都說(shuō)明了用法.

Android Security and Accessibility

之前有一個(gè)文章說(shuō)Accessiblity存在安全隱患, 這個(gè)服務(wù)可能可以訪(fǎng)問(wèn)到一些隱私信息, 比如密碼.

但是這篇文章的作者覺(jué)得前一篇文章作者的解決方案不是很好.

因?yàn)楫?dāng)用戶(hù)開(kāi)啟Accessibility權(quán)限的時(shí)候, Android就已經(jīng)給出了警告, 說(shuō)明敏感信息可能會(huì)被觀(guān)察到. 第三方的keyboard也可以訪(fǎng)問(wèn)這些信息, Android也是在開(kāi)啟的時(shí)候給出了警告.

另外對(duì)于前一篇文章作者提出的解決方案: View.IMPORTANT_FOR_ACCESSIBILITY_NO
這樣真正有視覺(jué)障礙的那部分用戶(hù)也無(wú)法看到密碼, 可能就無(wú)法登陸了.

所以本文作者建議的解決方案是, 可以彈一個(gè)對(duì)話(huà)框來(lái)提醒用戶(hù), 如果用戶(hù)允許了, 再繼續(xù)輸入.

Sharing code between UI & unit tests

Android的測(cè)試分兩種:

一種是Unit tests. 單元測(cè)試, 在JVM上跑.

另一種是UI測(cè)試, 需要Android設(shè)備.

在Android Studio中對(duì)應(yīng)testandroidTest文件夾.

這兩個(gè)測(cè)試文件夾之間是不共享代碼的, 即一個(gè)文件夾里不能訪(fǎng)問(wèn)另一個(gè)里面的代碼.

但是如果我們想要共用一些代碼, 是有辦法解決的.

首先在app/src下新建一個(gè)文件夾, 比如叫testShared. 里面添加要共享的代碼.

然后在app/build.gradle里面添加這個(gè):

android.sourceSets {  
    test {
        java.srcDirs += "$projectDir/src/testShared"
    }

    androidTest {
        java.srcDirs += "$projectDir/src/testShared"
    }
}

就可以在UI測(cè)試和單元測(cè)試中共享同一份代碼了.

Synchronously Animating Colors on Android

作者想做的一個(gè)效果是, 在切換tab的時(shí)候, 把Toolbar, TabLayout, FloatingActionButton還有StatusBar的顏色都動(dòng)畫(huà)地改變到另一個(gè)顏色.

實(shí)現(xiàn)很簡(jiǎn)單, 首先用當(dāng)前顏色和目標(biāo)顏色建立一個(gè)ValueAnimator, 然后addUpdateListener()在更新的過(guò)程中把值set給相應(yīng)的控件:

colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animator) {
        int color = (int) animator.getAnimatedValue();

        toolbar.setBackgroundColor(color);
        tabLayout.setBackgroundColor(color);
        floatingActionButton.setBackgroundTintList(ColorStateList.valueOf(color));

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            getWindow().setStatusBarColor(color);
        }
    }

});
colorAnimation.start();

其中FloatingActionButton要用setBackgroundTintList().

StatusBar在21及以上才支持getWindow().setStatusBarColor(color);

Android Fingerprint Authentication

其實(shí)用戶(hù)都不喜歡驗(yàn)證, 因?yàn)橛脩?hù)都比較懶, 不喜歡一次又一次地輸入密碼或者手勢(shì)pattern, 但是不鎖屏又不安全.

指紋驗(yàn)證Fingerprint Authentication是Android M (Android 6.0, API 23)引入的. 它就是為了解決這個(gè)問(wèn)題, 提升用戶(hù)體驗(yàn). 這種non-disturbing和easy的方式, 讓我們不用在安全和用戶(hù)體驗(yàn)之間做出妥協(xié).

如果你的應(yīng)用需要做一些關(guān)鍵操作, 比如支付, 你需要用戶(hù)在操作前授權(quán), 那么指紋驗(yàn)證會(huì)很有幫助.

然后作者介紹了實(shí)現(xiàn)的細(xì)節(jié).

最后作者附上了自己的相關(guān)庫(kù): fingerlock.

Kotlin vs Java: Compilation Speed

這是作者關(guān)于Kotlin的第三篇文章, 作者在這篇文章里測(cè)試了Kotlin和Java的編譯時(shí)間.

Clean build with No Gradle daemon
Java編譯比Kotlin快17%.

Clean build + Gradle daemon
org.gradle.daemon=true

Java編譯比Kotlin快13%.

Incremental builds
kotlin.incremental=true

在clean build的時(shí)候, Java可能快10-15%, 但是在增量build + gradle daemon時(shí), kotlin和Java一樣快, 甚至可能比Java更快一些.

Let the view handle the lifecycle in MVP by using RxJava

問(wèn)題:
作者舉了一個(gè)例子, 在Fragment作為View的MVP中, 如果P從service取一些數(shù)據(jù), 然后調(diào)用View的顯示方法, 則還需要知道onViewCreated()是不是已經(jīng)調(diào)用過(guò)了.

解決方案:

首先創(chuàng)建一個(gè)Lifecycle的BehaviorSubject, 在onViewCreated()的時(shí)候調(diào)用onNext(null).

把View的方法改成返回一個(gè)Observable, presenter的方法調(diào)用View的方法時(shí)實(shí)際上是subscribe了一下:

class ProductsFragment implements ProductsView {
  private ProductsPresenter presenter;
  //Lifecycle subject. It is BehaviourSubject because it can be subscribed after onViewCreated call.
  private final BehaviorSubject<Void> onViewCreatedSubject = BehaviorSubject.create();
  
  @Override
  public Observable<Void> showProducts(List<Product> productList) {
    return onViewCreatedSubject. // Wait for onViewCreated
        doOnNext(new Action1<Object>() {
          @Override
          public void call(Object o) {
            //Updates recyclerview adapter items
          }
        });
  }
  
  @Override
  public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    onViewCreatedSubject.onNext(null);
  }
}

Presenter:

class ProductsFragmentPresenter implements ProductsPresenter {
  private ProductsView view;
  
  public void loadProducts(){
    productsService.getProducts()
      .flatMap(new Func1<Object, Observable<Void>>() {
          @Override
          public Observable<Void> call(List<Product> productList) {
            //Return the view's observable to show products. 
            //No need to check if the view is created!
            return view.showProducts(productList); 
          }
        }) 
      .subscribe();
  }
}

當(dāng)然這并不是一個(gè)完整的例子, 完整的例子還需要考慮onDestroyView()還有注銷(xiāo)等情況的處理.

Nougat - GCM Network Manager

作者搞了一個(gè)message app來(lái)研究Android 7的新特性.

他用到了AutoValue.

關(guān)于Android 7的另一篇文章: Random Musings on the N Developer Preview

他們的應(yīng)用首先需要周期性地生產(chǎn)一些消息, 關(guān)于生產(chǎn)消息的實(shí)現(xiàn), 作者沒(méi)有用AlarmManager, 也沒(méi)有用JobScheduler(因?yàn)橹恢С諥PI 21及以上), 而是選用了GCMNetworkManager.

具體實(shí)現(xiàn)見(jiàn)原文, 有詳細(xì)說(shuō)明.
另: 代碼

這只是系列文章的第一篇, 后續(xù)應(yīng)該會(huì)寫(xiě)更多.

TransactionTooLargeException crashes on Nougat

作者自己的應(yīng)用在Activity轉(zhuǎn)換的時(shí)候遇到了一個(gè)crash: java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 700848 bytes.

之前應(yīng)用里有相關(guān)的Warning log, 但是
Android 7 Nougat (API 24)把它作為異常拋出來(lái)了.

產(chǎn)生這個(gè)問(wèn)題的原因是在onSaveInstanceState()里面存了太多數(shù)據(jù). 作者做了一個(gè)測(cè)試, 想看看這個(gè)限制大概是多少, 大概是500K左右.

所以這里是不應(yīng)該用來(lái)存儲(chǔ)太多數(shù)據(jù)的, 應(yīng)該只存狀態(tài).

底下回復(fù)說(shuō)每個(gè)進(jìn)程都有1M的buffer來(lái)接收transactions, 但是是在沒(méi)有任何其他IPC的情況下. 所以建議存儲(chǔ)的狀態(tài)數(shù)據(jù)少于100K或者50K, 當(dāng)然越少越好.

Building a blazing fast ETC2 compressor

作者是Google的, 以前做游戲的, 所以致力于Performances, GPU, 數(shù)據(jù)壓縮等內(nèi)容.

作者關(guān)注VR, 但是VR中要提升體驗(yàn), 必定會(huì)增加圖像的大小和質(zhì)量.

ETC textures 是OpenGLES 3.0的一種標(biāo)準(zhǔn)格式.

編碼一個(gè)高質(zhì)量的ETC2 texture會(huì)花費(fèi)很多時(shí)間.
以在游戲界最流行的壓縮工具Mali GPU Texture Compression tool為例, 作者做了實(shí)驗(yàn), 證明確實(shí)要花費(fèi)很多時(shí)間(平均10分鐘)來(lái)encode一個(gè)圖.

所以作者他們開(kāi)發(fā)了一個(gè)新的庫(kù): etc2comp, 一個(gè)很快的texture encoder.

然后和之前的工具做了比較, 平均時(shí)間提高到了10秒.

后來(lái)他說(shuō)的技術(shù)細(xì)節(jié)我就看不懂了. 文后還有其他圖像格式(JPG, PNG, WebP)相關(guān)的文章鏈接.

Low Coupling With Rx and Dagger2 in Android

作者舉例展示Android程序的解耦.

首先, 他展示一個(gè)高度耦合的Android程序, 然后加入Rx, 最后加入Dagger2, 從而一步一步地解耦這個(gè)項(xiàng)目.

項(xiàng)目的內(nèi)容是發(fā)現(xiàn)Network中的Services. 這里有官方的Training: Network Service Discovery.

RxJava2: An Early Preview

最近RxJava2有了第一個(gè)Release Candidate. 所以作者在這里先預(yù)覽一下有哪些有趣的更新和新加的功能:

New Dependency:
添加了依賴(lài): ReactiveStreams.

Imports:
RxJava2放在了一個(gè)不同的package下:

RxJava:

compile ‘io.reactivex:rxjava:1.0.y-SNAPSHOT’

RxJava2:

compile ‘io.reactivex.rxjava2:rxjava:x.y.z’

這意味著, 你可以同時(shí)用兩個(gè)版本的庫(kù). 如果你要完全遷移的話(huà), 你需要把所有的import都改到新包.

Null Emissions No Longer Permitted:
不允許再發(fā)送null值了, 會(huì)直接拋出空指針異常.

Observable.just(null); //don’t do this
subject.onNext(null); //don’t do this either

Under(Back)Pressure:

Backpressure是當(dāng)Observable發(fā)射值的速度比Observer能處理的速度快時(shí)發(fā)生的.

RxJava2引入了一個(gè)新的Observable類(lèi)Flowable, with backpressure support.

Single Old and New:
訂閱一個(gè)Single現(xiàn)在可以用這個(gè):
SingleObserver<T>.

Hit Me Maybe One More Type:
一個(gè)新的類(lèi)型叫Maybe, 它是SingleCompletable的混合體. 用來(lái)發(fā)射0或1個(gè)值.

New BackPressured Subject: Processor:
引入了一個(gè)新類(lèi)型, Processor, 它是一個(gè)有backpressure support的Subject.

New Names for Function and Action:

  • Func1 -> Function
  • Func2 -> BiFunction
  • FuncN -> Function<Object[], R>
  • Func1<T, Boolean> -> Predicate<T>
  • Action0 -> Consumer
  • Action1 -> BiConsumer
  • ActionN -> Consumer<Object[]>

Subscriber is Now Disposable:

因?yàn)楹蚏eactive-Streams的命名沖突, 所以Subscriber改名為Disposable. 它有一個(gè).dispose()方法, 類(lèi)似于Subscription.unsubscribe()方法.

onCompleted()也將變?yōu)?code>onComplete().

Composite Subscriptions Changes:

CompositeSubscription + subscribe()-> CompositeDisposable + subscribeWith()

Blocking Calls:
RxJava2加了一些新的操作符來(lái)變異步為同步.
.toBlocking.first() -> .blockingFirst()

Better Hooks for Plugins:
plugin系統(tǒng)被重寫(xiě)了. 現(xiàn)在你可以覆寫(xiě)內(nèi)置schedulers返回的值了. 這樣你就可以在做單元測(cè)試的時(shí)候覆寫(xiě)Schedulers.io()來(lái)返回同步的值, 甚至debug Schedulers.

Summary

目標(biāo)Release日期: October 29.

Retrofit已經(jīng)支持RxJava2了:
retrofit-rxjava2-adapter

這里還有一個(gè)Library用來(lái)把RxJava1轉(zhuǎn)換到RxJava2: RxJava2Interop

Sources:
RxJava 2.x javadoc,
Github Wiki: What's different in 2.0,
Stackoverflow

Eight Ways Your Android App Can STOP Leaking Memory

之前作者有個(gè)文章叫Eight Ways Your Android App Can Leak Memory, 講的是Android應(yīng)用中8種內(nèi)存泄露的原因, 主要是泄露了Activity.

這篇文章主要講解決方法:

Static Activities

錯(cuò)誤原因: 把Activity存在一個(gè)靜態(tài)引用里, Activity生命周期結(jié)束后仍然持有.

解決方法:
使用WeakReference.

Static Views

錯(cuò)誤原因: 靜態(tài)引用了View, 因?yàn)閍ttached View引用了Activity, 所以等于間接引用了Activity.

解決方法:

  1. 使用WeakReference;
  2. 在onDestroy()里面把引用置為null.

Inner Classes

內(nèi)部類(lèi)分兩種, 靜態(tài)內(nèi)部類(lèi)和非靜態(tài)內(nèi)部類(lèi): Nested Class

錯(cuò)誤原因: 在Activity里有一個(gè)內(nèi)部類(lèi)(非靜態(tài)), 創(chuàng)建內(nèi)部類(lèi)的對(duì)象, 然后靜態(tài)引用之. 因?yàn)閮?nèi)部類(lèi)持有外部類(lèi)的應(yīng)用, 所以會(huì)造成內(nèi)存泄露.

解決方法:
盡量不要存static引用.

匿名內(nèi)部類(lèi) AsyncTask, Handler, Thread, TimerTask

錯(cuò)誤原因:

如果你不在超出生命周期的地方引用它, 匿名內(nèi)部類(lèi)的對(duì)象是無(wú)害的.

但是上面的這些內(nèi)部類(lèi)對(duì)象全都是用來(lái)產(chǎn)生一些線(xiàn)程的, 這些線(xiàn)程是app全局的, 而且會(huì)引用創(chuàng)建它們的對(duì)象.

解決方法:

  1. 把上面的這些類(lèi)改成靜態(tài)內(nèi)部類(lèi), 靜態(tài)的內(nèi)部類(lèi)對(duì)象不會(huì)引用外部類(lèi)的對(duì)象.
  2. 如果你堅(jiān)持使用匿名內(nèi)部類(lèi), 可以在Activity的onDestroy()里面終止線(xiàn)程.

Sensor Manager

錯(cuò)誤原因:

把Activity作為listener注冊(cè)給了系統(tǒng)服務(wù), 但是在Activity生命周期結(jié)束之前沒(méi)有注銷(xiāo)listener.

解決方法: 在生命周期結(jié)束前注銷(xiāo)listener.

Auto rename Android versionName in Gradle

在應(yīng)用release的時(shí)候, 版本號(hào)是確定的, 這沒(méi)問(wèn)題. 在應(yīng)用開(kāi)發(fā)的時(shí)候, 如果每一個(gè)apk也有一個(gè)特定的版本號(hào), 將會(huì)非常有幫助.

自定義Gradle Plugin:
com.android.application就是一個(gè)gradle plugin.

有三種方式可以創(chuàng)建gradle plugin: doc.

本文作者選擇了buildSrc的方式, 因?yàn)檫@很容易, 而且可以被加到repo里, 但是這樣將依附于你的project, 不能復(fù)用.

具體代碼見(jiàn)原文.

這么做了之后, 每一次build的apk都自帶了分支信息, Jira卡號(hào), 或者任何你想帶的信息.

Is your custom view interactive aware?

什么是Interactive View?
當(dāng)View是可見(jiàn)的, 即可以和用戶(hù)交互, 即為interactive.

當(dāng)你的自定義View做一些很重的工作, 比如循環(huán)的動(dòng)畫(huà)或者loading, 或者依賴(lài)于傳感器, 當(dāng)這種View變?yōu)椴豢梢?jiàn)時(shí),你需要做一些工作來(lái)節(jié)約電量.

作者寫(xiě)了一個(gè)輔助類(lèi): InteractiveViewHelper 來(lái)做這個(gè).

具體利用了View的這幾個(gè)回調(diào):

void View::onVisibilityChanged(View, int)
void View::onWindowVisibilityChanged()
void View::onAttachedToWindow()
void View::onDetachedFromWindow()

還有兩個(gè)ACTION:

Intent.ACTION_SCREEN_ON
Intent.ACTION_SCREEN_OFF

Beta Testing Your Android App With Build Variants

講了如何用Build Variants, 添加不同的Flavors.

Make your build.gradle great again

1. 把你的build.gradle分成小份, 更加模塊化, 用apply應(yīng)用.

2. 在build file里指明application id.

applicationId是apk最終會(huì)用的包名.
packageName是用來(lái)找代碼中的R, 和activity/service組件的相對(duì)路徑.
如果不在build文件里指明applicationId可能會(huì)有一些問(wèn)題.

3. 給debug版使用一個(gè)不同的applicationId.

buildTypes {    
    debug {
        applicationIdSuffix ".debug"
    }
    // ...
}

好處是同一個(gè)機(jī)器上可以同時(shí)安裝debug和release版.

4. 統(tǒng)計(jì)build時(shí)間.

用--profile命令. 或Build Scans

還可以用build-time-tracker-plugin

5. 配置release.

Proguard在Java層面工作, 對(duì)于資源是不管的, 只把R中的id刪了.
如果想進(jìn)一步處理不用的資源, 需要加:
shrinkResources true.

更深一步的居然還可以拆分apk: config-apk-splits

6. 發(fā)現(xiàn)一些有用的tasks, 或者自己開(kāi)發(fā). Reddit page.

7. 把依賴(lài)的版本號(hào)抽出來(lái).

8. 使用jcenter, 響應(yīng)更快.

9. 在開(kāi)發(fā)時(shí)把最小sdk設(shè)為21或以上, 會(huì)build得更快.

LIBRARIES & CODE

Android Amazing Open Source Apps

這篇文章列舉了一些好的開(kāi)源app.
包括google/iosched, android-architecture, Telegram, Plaid, wire-android, ribot/ribot-app-android, PocketHub.

DoorSignView

一個(gè)自定義View, 顯示門(mén)牌. AnimatedDoorSignView可以根據(jù)傳感器進(jìn)行動(dòng)畫(huà).

Java Error Handler

一個(gè)統(tǒng)一的錯(cuò)誤處理器. 為每一種錯(cuò)誤建立全局默認(rèn)的處理方式.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,582評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,540評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,028評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,801評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,223評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,442評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,976評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,800評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,996評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評(píng)論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,233評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,926評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,702評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374

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