Android代碼混淆及ProGuard手冊

Android代碼的縮減、混淆和優(yōu)化

為什么把這三個內(nèi)容放到一起說?因為在Android Gradle中配置方法基本是在一起的。

官方說明如下:

為了盡可能減小應(yīng)用的大小,您應(yīng)在發(fā)布 build 中啟用縮減功能來移除不使用的代碼和資源喊崖。啟用縮減功能后,您還會受益于兩項功能雇逞,一項是混淆處理功能荤懂,該功能會縮短應(yīng)用的類和成員的名稱;另一項是優(yōu)化功能塘砸,該功能會采用更積極的策略來進(jìn)一步減小應(yīng)用的大小节仿。本頁介紹 R8 如何為項目執(zhí)行這些編譯時任務(wù),以及您如何對這些任務(wù)進(jìn)行自定義掉蔬。

當(dāng)您使用 Android Gradle 插件 3.4.0 或更高版本構(gòu)建項目時粟耻,該插件不再使用 ProGuard 執(zhí)行編譯時代碼優(yōu)化查近,而是與 R8 編譯器協(xié)同工作,處理以下編譯時任務(wù):

  • 代碼縮減(即搖樹優(yōu)化):從應(yīng)用及其庫依賴項中檢測并安全地移除不使用的類挤忙、字段霜威、方法和屬性(這使其成為了一個對于規(guī)避 64k 引用限制非常有用的工具)。例如册烈,如果您僅使用某個庫依賴項的少數(shù)幾個 API戈泼,那么縮減功能可以識別應(yīng)用不使用的庫代碼并僅從應(yīng)用中移除這部分代碼。如需了解詳情赏僧,請轉(zhuǎn)到介紹如何縮減代碼的部分大猛。
  • 資源縮減:從封裝應(yīng)用中移除不使用的資源,包括應(yīng)用庫依賴項中不使用的資源淀零。此功能可與代碼縮減功能結(jié)合使用挽绩,這樣一來,移除不使用的代碼后驾中,也可以安全地移除不再引用的所有資源唉堪。如需了解詳情,請轉(zhuǎn)到介紹如何縮減資源的部分肩民。
  • 混淆:縮短類和成員的名稱唠亚,從而減小 DEX 文件的大小。如需了解詳情持痰,請轉(zhuǎn)到介紹如何對代碼進(jìn)行混淆處理的部分灶搜。
  • 優(yōu)化:檢查并重寫代碼,以進(jìn)一步減小應(yīng)用的 DEX 文件的大小工窍。例如割卖,如果 R8 檢測到從未采用過給定 if/else 語句的 else {} 分支,則會移除 else {} 分支的代碼患雏。如需了解詳情鹏溯,請轉(zhuǎn)到介紹代碼優(yōu)化的部分。

默認(rèn)情況下纵苛,在構(gòu)建應(yīng)用的發(fā)布版本時剿涮,R8 會自動執(zhí)行上述編譯時任務(wù)言津。不過攻人,您也可以停用某些任務(wù)或通過 ProGuard 規(guī)則文件自定義 R8 的行為。事實上悬槽,R8 支持所有現(xiàn)有 ProGuard 規(guī)則文件怀吻,因此您在更新 Android Gradle 插件以使用 R8 時,無需更改現(xiàn)有規(guī)則初婆。

開啟混淆功能

上面沒有提到的一個代碼混淆的重要作用:我們知道apk文件是相對容易被反編譯的蓬坡,未加混淆的apk猿棉,反編譯后基本裸奔。而混淆的apk即使被反編譯屑咳,類名與變量名都會處理成無意義的字符萨赁,很大程度上降低了源碼的可讀性。

以下是在Android Studio中開啟混淆的方法和常用的配置兆龙。Android Studio的Gradle混淆功能默認(rèn)是關(guān)閉的杖爽,我們需要手動在module的build.gradle中打開。

  1. 在Module的build.gradle中添加如下配置:
android{
    ...
    buildTypes {

        release {
            // 開啟混淆
            minifyEnabled true
            // 開啟資源壓縮紫皇,編譯時會自動刪除未使用到的res資源
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }

        debug {
            //開啟混淆會導(dǎo)致編譯速度變慢慰安,debug通常不開啟
            minifyEnabled false
            shrinkResources false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
} 

之后我們需要在proguard-rules.pro文件中編輯混淆規(guī)則。

proguard手冊

輸入/輸出配置項

  • @filename

    等同于:-include filename聪铺,filename 文件名化焕。

  • -include [filename]

    從給定文件中讀取配置項,filename 文件名铃剔。

  • -basedirectory [directoryname]

    為后續(xù)的相對文件指定基礎(chǔ)目錄撒桨,directoryname 目錄名。

  • injars [classPath] 指定要處理的輸入jar(等壓縮包)番宁,包中的class文件會被處理后輸出到outjars的指定路徑下元莫,非class文件直接輸出到outjars指定路徑,classpath jar包輸入路徑蝶押。

  • outjars [classPath] 指定輸出路徑踱蠢,接收injars的文件輸出,classPath 輸出路徑棋电。

  • libraryjars [classPath] 指定不被混淆的jar茎截,classPath 文件路徑。

壓縮配置項

  • -dontshrink

    不進(jìn)行代碼壓縮赶盔,默認(rèn)會進(jìn)行代碼壓縮企锌,打開此配置項則不進(jìn)行壓縮。

  • -printusage [fileName]

    將被壓縮的代碼輸出到指定路徑于未。

優(yōu)化配置項

  • -dontoptimize

    指定不進(jìn)行代碼優(yōu)化撕攒,默認(rèn)情況下ProGuard會優(yōu)化所有代碼。

  • -optimizations [optimization_filter]

    在更細(xì)粒度的級別指定要啟用和禁用的優(yōu)化烘浦,篇幅有限不展開講了抖坪,具體可以參考:Configuration - Optimizations

    常用的配置方式:-optimizations !code/simplification/cast,!field/*,!class/merging/*

  • -optimizationpasses n

    指定代碼迭代優(yōu)化的次數(shù),默認(rèn)執(zhí)行一遍闷叉。

混淆配置

  • -dontobfuscate

    不進(jìn)行混淆擦俐,默認(rèn)會進(jìn)行代碼混淆。

  • -printmapping [filename]

    將混淆前后映射表輸出到指定文件握侧。

  • -dontusemixedcaseclassnames

    混淆時不使用大小寫混合蚯瞧,混淆后類名為小寫嘿期。

  • -keepattributes [attribute_filter]

    指定要保留的屬性,如:-keepattributes Exceptions,InnerClasses,具體可參考:Optional attributes

通用配置項

  • -verbose

    指定在處理期間輸出更多信息埋合。如果程序因異常終止备徐,此選項將打印出整個堆棧跟蹤,而不僅僅是異常消息甚颂。

  • -dontnote [class_filter]

    指定不打印匹配類的相關(guān)異常信息坦喘,class_filter為正則表達(dá)式,符合表達(dá)式的類不打印信息西设。

  • -dontwarn [class_filter]

    指定相關(guān)類不發(fā)出警告瓣铣,class_filter為正則表達(dá)式。如:-dontwarn androidx.**

  • -dump [filename]

    將所有class的內(nèi)部結(jié)構(gòu)輸出到指定文件贷揽。如:-dump proguard/class_files.txt

keep配置項

  • -keep []

    指定要保留的類和類成員棠笑。

    //View子類,類名不混淆
    - keep public class * extends android.view.View{
            //set開頭的方法不混淆
        void set*(***);
        //構(gòu)造函數(shù)不混淆
        public <init>(android.content.Context);
        public <init>(android.content.Context, android.util.AttributeSet);
        public <init>(android.content.Context, android.util.AttributeSet, int);
    } 
    

    指定的類名和類成員不被混淆禽绪,需要注意的是以下配置只能保證類名不被混淆蓖救。

    -keep public class * extends android.app.Activity 
    

    想要類名和類成員全部不混淆應(yīng)該使用:

    -keep public class * extends android.app.Activity{*;} 
    
  • -keepclassmembers

    保留指定的類成員。

    //指定不混淆的類成員印屁,類名會混淆
    -keepclassmembers class * {
        //事件監(jiān)聽類(參數(shù)符合**On*Event)的方法不混淆
        void *(**On*Event);
    } 
    
  • -keepclasseswithmembers

    在所有類成員都存在的情況下循捺,指定要保留的類和類成員。

    //需要匹配類和所有指定成員都存在的類雄人,類和類成員不混淆
    -keepclasseswithmembers class * {
        public <init>(android.content.Context, android.util.AttributeSet);
        public <init>(android.content.Context, android.util.AttributeSet, int);
    } 
    

    功能與-keep很像从橘,舉個例子:

    public class MyClass extends android.view.View{
        public MyClass(Context context) {
            super(context);
        }
    
        public MyClass(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        public MyClass(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        /**
         * 自定義方法
         * @param onClickListener
         */
        public void addListener(OnClickListener onClickListener){
            //TODO: todo
        }
    } 
    

    當(dāng)使用-keep時:

    - keep public class * extends android.view.View{
        //構(gòu)造函數(shù)不混淆
        public <init>(android.content.Context);
        public <init>(android.content.Context, android.util.AttributeSet);
        public <init>(android.content.Context, android.util.AttributeSet, int);
      
        public void addListener(***)
        public void getListener(***)
    } 
    

    MyClass類名和指定的類成員不會被混淆(未指定的類成員也會被混淆),因為MyClass符合 class * extends android.view.View條件础钠。

    而使用-keepclasseswithmembers時:

    - keep public class * extends android.view.View{
        //構(gòu)造函數(shù)不混淆
        public <init>(android.content.Context);
        public <init>(android.content.Context, android.util.AttributeSet);
        public <init>(android.content.Context, android.util.AttributeSet, int);
      
        public void addListener(***)
        public void getListener(***)
    } 
    

    MyClass會被混淆恰力,因為MyClass不符合public void getListener(***)條件。當(dāng)keepclasseswithmembers指定的類和類成員全部存在的情況下旗吁,才會匹配該類(MyClass不存在getListener()方法踩萎,所以不能被匹配)。

  • -keepnames/-keepclassmembernames和-keepclasseswithmembernames

    選項 等同于 作用
    -keepnames ** -keep,allowshrinking ** 指定要保留的類和類成員,允許代碼壓縮優(yōu)化很钓,成員可能在壓縮階段被刪除
    -keepclassmembernames ** - keepclassmembers,allowshrinking ** 指定要保留的類成員香府,允許代碼壓縮優(yōu)化,成員可能在壓縮階段被刪除
    -keepclasseswithmembernames ** -keepclasseswithmember,allowshrinking ** 在所有類成員都存在的情況下码倦,指定要保留的類和類成員,允許代碼壓縮優(yōu)化企孩,成員可能在壓縮階段被刪除

    簡單來說使用-keep/-keepclassmember和-keepclasseswithmember可能會阻止成員被壓縮優(yōu)化,而使用-keepnames/-keepclassmembernames和-keepclasseswithmembernames而不會叹洲。

Keep配置項修飾符

可用用來修飾-keep/-keepclassmember和-keepclasseswithmember柠硕,使用方式:

-keep[,modifiter,...] class_specification
-keep,allowshrinking public class * extends android.view.View{*;} 
  • allowshrinking

    上面提到過了工禾,允許對象進(jìn)行代碼壓縮运提,對象可能在壓縮階段被刪除蝗柔。

  • allowoptimization

    指定的對象可能會被改變(優(yōu)化步驟),但可能不會被混淆或者刪除民泵。

  • allowobfuscation

    指定對象可能會被重命名癣丧,但是不會被刪除和優(yōu)化。

  • includedescriptorclasses

  • includecode

    這兩個自行了解吧栈妆,太不常用了胁编。(其實我也沒搞懂。鳞尔。嬉橙。)

通配符

符號 意義 人話
? matches any single character in a class name, but not the package separator. For example, "com.example.Test?" matches "com.example.Test1" and "com.example.Test2", but not "com.example.Test12". 匹配名稱中的任意單個字符。
* matches any part of a class name not containing the package separator. For example, "com.example.*Test*" matches "com.example.Test" and "com.example.YourTestApplication", but not "com.example.mysubpackage.MyTest". Or, more generally, "com.example.*" matches all classes in "com.example", but not in its subpackages. 匹配名稱中任意多個字符寥假,不包含包分隔符和目錄分隔符市框,用在類體中可以匹配任意字段和方法。
** matches any part of a class name, possibly containing any number of package separators. For example, "**.Test" matches all Test classes in all packages except the root package. Or, "com.example.**" matches all classes in "com.example" and in its subpackages. 匹配名稱中任何部分糕韧,可以包含分隔符枫振。
<n> matches the n'th matched wildcard in the same option. For example, "com.example.*Foo<1>" matches "com.example.BarFooBar". 在相同選項中匹配第n個匹配的通配符。

篇幅有限只列舉了部分常用的配置項萤彩。
proguard常用配置規(guī)則


常用文件配置如下:

1.常用配置

-optimizationpasses 5
# 混淆時不使用大小寫混合粪滤,混淆后的類名為小寫
-dontusemixedcaseclassnames
# 指定不去忽略非公共庫的類
-dontskipnonpubliclibraryclasses
# 指定不去忽略非公共庫的成員
-dontskipnonpubliclibraryclassmembers
# 混淆時不做預(yù)校驗
-dontpreverify
# 混淆時不記錄日志
-verbose
# 代碼優(yōu)化
-dontshrink
# 不優(yōu)化輸入的類文件
-dontoptimize
# 保留注解不混淆
-keepattributes *Annotation*,InnerClasses
# 避免混淆泛型
-keepattributes Signature
# 保留代碼行號,方便異常信息的追蹤
-keepattributes SourceFile,LineNumberTable
# 混淆采用的算法
-optimizations !code/simplification/cast,!field/*,!class/merging/*

# dump.txt文件列出apk包內(nèi)所有class的內(nèi)部結(jié)構(gòu)
-dump proguard/class_files.txt
# seeds.txt文件列出未混淆的類和成員
-printseeds proguard/seeds.txt
# usage.txt文件列出從apk中刪除的代碼
-printusage proguard/unused.txt
# mapping.txt文件列出混淆前后的映射
-printmapping proguard/mapping.txt 

2. Android 系統(tǒng)類

android類

# Android類
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View 

support包

# support
-keep class android.support.** {*;}
-keep public class * extends android.support.**
-dontwarn android.support.**
-keep interface android.support.** { *; } 

androidx包

# androidx
-keep class androidx.** {*;}
-keep interface androidx.** {*;}
-keep public class * extends androidx.**
-dontwarn androidx.** 

自定義控件get/set和構(gòu)造

# 自定義控件
-keep public class * extends android.view.View{
    *** get*();
    void set*(***);
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
} 

R文件

# R文件
-keep class **.R$* {
 *;
} 

webview

# webview
-keepclassmembers class android.webkit.WebView {
   public *;
}
-keepclassmembers class * extends android.webkit.WebViewClient {
    public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
    public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
    public void *(android.webkit.WebView, *);
} 

android事件方法

# 按鍵等事件
-keepclassmembers class * {
    void *(**On*Event);
}
# onClick
-keepclassmembers class * extends android.app.Activity{
    public void *(android.view.View);
} 

枚舉類型

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
} 

其他類

# native方法
-keepclasseswithmembernames class * {
    native <methods>;
}
# View構(gòu)造方法
-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
}
# Parcelable
-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}
# Serializable
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
} 

3. 第三庫

ButterKnife

-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
    @butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
    @butterknife.* <methods>;
} 

OkHttp

-dontwarn com.squareup.okhttp3.**
-keep class com.squareup.okhttp3.** { *;}
-dontwarn okio.** 

glide 3

-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
} 

glide 4

-keep public class * implements com.bumptech.glide.module.AppGlideModule
-keep public class * implements com.bumptech.glide.module.LibraryGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
} 

gson

-keep class com.google.gson.** {*;}
-keep class com.google.**{*;}
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
-keep class com.google.gson.examples.android.model.** { *; } 

greendao

-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties
# If you do not use SQLCipher:
-dontwarn org.greenrobot.greendao.database.**
# If you do not use Rx:
-dontwarn rx.** 

Retrofit2

-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions 

RxJava雀扶、RxAndroid

-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
   long producerIndex;
   long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode consumerNode;
} 

Picasso

-keep class com.parse.*{ *; }
-dontwarn com.parse.**
-dontwarn com.squareup.picasso.**
-keepclasseswithmembernames class * {
    native <methods>;
} 

fastJson

-dontwarn com.alibaba.fastjson.**
-keep class com.alibaba.fastjson.**{*; } 

EventBus

# EventBus2
-keepclassmembers class ** {
    public void onEvent*(***);
}

# Only required if you use AsyncExecutor
-keepclassmembers class * extends de.greenrobot.event.util.ThrowableFailureEvent {
    <init>(java.lang.Throwable);
}
#EventBus3
-keepattributes *Annotation*
-keepclassmembers class ** {
    @org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }

# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
    <init>(java.lang.Throwable);
} 

阿里云推送

-keepclasseswithmembernames class ** {
    native <methods>;
}
-keepattributes Signature
-keep class sun.misc.Unsafe { *; }
-keep class com.taobao.** {*;}
-keep class com.alibaba.** {*;}
-keep class com.alipay.** {*;}
-keep class com.ut.** {*;}
-keep class com.ta.** {*;}
-keep class anet.**{*;}
-keep class anetwork.**{*;}
-keep class org.android.spdy.**{*;}
-keep class org.android.agoo.**{*;}
-keep class android.os.**{*;}
-keep class org.json.**{*;}
-dontwarn com.taobao.**
-dontwarn com.alibaba.**
-dontwarn com.alipay.**
-dontwarn anet.**
-dontwarn org.android.spdy.**
-dontwarn org.android.agoo.**
-dontwarn anetwork.**
-dontwarn com.ut.*
-dontwarn com.ta.** 

以上只是列舉了一些Android系統(tǒng)和第三方庫常用的混淆配置方式杖小,項目中需要按照實際需求進(jìn)行配置。

最后

在技術(shù)領(lǐng)域內(nèi)愚墓,沒有任何一門課程可以讓你學(xué)完后一勞永逸窍侧,再好的課程也只能是“師傅領(lǐng)進(jìn)門,修行靠個人”转绷∥凹“學(xué)無止境”這句話,在任何技術(shù)領(lǐng)域议经,都不只是良好的習(xí)慣斧账,更是程序員和工程師們不被時代淘汰、獲得更好機會和發(fā)展的必要前提煞肾。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咧织,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子籍救,更是在濱河造成了極大的恐慌习绢,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異闪萄,居然都是意外死亡梧却,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門败去,熙熙樓的掌柜王于貴愁眉苦臉地迎上來放航,“玉大人,你說我怎么就攤上這事圆裕」泖ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵吓妆,是天一觀的道長赊时。 經(jīng)常有香客問我,道長行拢,這世上最難降的妖魔是什么蛋叼? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮剂陡,結(jié)果婚禮上狈涮,老公的妹妹穿的比我還像新娘。我一直安慰自己鸭栖,他們只是感情好歌馍,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著晕鹊,像睡著了一般松却。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上溅话,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天晓锻,我揣著相機與錄音,去河邊找鬼飞几。 笑死砚哆,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的屑墨。 我是一名探鬼主播躁锁,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼卵史!你這毒婦竟也來了战转?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤以躯,失蹤者是張志新(化名)和其女友劉穎槐秧,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡刁标,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年颠通,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片命雀。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖斩箫,靈堂內(nèi)的尸體忽然破棺而出吏砂,到底是詐尸還是另有隱情,我是刑警寧澤乘客,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布狐血,位于F島的核電站,受9級特大地震影響易核,放射性物質(zhì)發(fā)生泄漏匈织。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一牡直、第九天 我趴在偏房一處隱蔽的房頂上張望缀匕。 院中可真熱鬧,春花似錦碰逸、人聲如沸乡小。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽满钟。三九已至,卻和暖如春胳喷,著一層夾襖步出監(jiān)牢的瞬間湃番,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工吭露, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吠撮,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓讲竿,卻偏偏與公主長得像纬向,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子戴卜,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355

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