Android Studio Apk 打包 混淆


最近打包公司的項目中遇到一些問題序厉,在此記錄一下。

Android Studio下打包APK

  • 本文只講AS打包Apk的方式,eclispe的打包方式可以自行百度熟尉。

  • 打包Apk的兩種方式:

    • 第一種方式
      使用Android Studio自帶的工具生成Apk。具體步驟為 Build >Generate Signed APK,則會出現(xiàn)如圖所示的界面熙卡,如果是第一次生成APK杖刷,則需要創(chuàng)建新的簽名文件(圖在下二)。
生成APK界面.png
新建key.png
  • 下面來一一解釋其中方框需要填寫的東西

    • 1.選擇新建簽名文件保存的路徑驳癌,選擇好路徑并新建好簽名文件的名稱
    • 2.輸入簽名文件密碼(生成Apk 導(dǎo)入簽名文件需要這個密碼)
    • 3.確認密碼
    • 4.簽名文件別名
    • 5--6 別名密碼和確認密碼
    • 7.你的名字或者代號
    • 接下來的8---12 分別是公司滑燃、組織、城市喂柒、地區(qū)不瓶、國家(不是必須填寫,如果是公司項目可以填寫公司名稱等)
  • 填寫完成后就新建好了本項目的簽名文件灾杰,而如果以前已經(jīng)打包了本項目蚊丐,則直接導(dǎo)入以前生成的簽名文件并輸入密碼就可以了(簽名文件的密碼一定要記住,下面會解釋為什么)艳吠。如圖所示填入剛剛設(shè)置的密碼麦备,點擊下一步。


    選擇好簽名文件并輸入密碼.png
  • 最后可以選擇生成Apk的輸出路徑昭娩,Apk名稱凛篙,其中有兩個選項Signature Versions,他的含義為打包一個文件的簽名版本栏渺,選V1打包出來的app是jar的(一般這種就是當(dāng)做第三方導(dǎo)入項目來用的)呛梆,選v2打包出來的APP是apk版本的(也就是可以直接在手機上安裝的,可是上線的)磕诊,一般兩個都勾選上不會有什么問題填物。好了,到此第一種生成Apk的方式就弄完了霎终,是不是很簡單滞磺。


    配置完成打包Apk.png
  • 第二種方式,在控制臺使用命令的方式打包

首先配置gradle簽名文件信息莱褒,如圖所示:

配置簽名文件信息.png

然后在Build Types中選擇剛剛配置好的release(即簽名配置信息)击困,如圖所示

選擇剛剛配置好的release .png

這時候我們可以去gradle中查看我們剛剛配置好的簽名文件信息,并且已經(jīng)在 buildTypes有引用广凸,說明簽名文件已經(jīng)配置好了阅茶,如圖所示。

gradle中簽名文件信息.png

但是我們直接將簽名文件的密碼放在Gradle中會不安全谅海,可能會被他人反編譯看到目派,這時候我們可以在Gradle這樣設(shè)置,如圖

簽名信息設(shè)置讀取密碼.png
  • 這時候我們就可以在AS中的控制臺用命令gradlew assembleRelease來生成我們的Apk(如果使用讀取密碼的方式胁赢,輸入完命令之后會被要求輸入簽名文件的密碼)企蹭,如圖所示輸入完命令白筹,構(gòu)建完成生成的Apk在 app >build>outputs>apk 中(名稱有debug的Apk是未簽名的APK,名稱中有release則是我們簽名打包好的APK)谅摄。
控制臺輸入生成Apk命令.png

最后徒河,有可能遇到的問題

  • 我們寫好代碼測試的時候?qū)?yīng)用安裝到到手機上,然后我們打包好的應(yīng)用再次安裝到手機上就會出現(xiàn)“已存在簽名沖突的同名數(shù)據(jù)包”送漠,導(dǎo)致應(yīng)用安裝失敗顽照,這就是我們簽名文件出現(xiàn)了沖突,測試安裝的應(yīng)用使用的簽名文件是IDE工具的測試簽名文件(默認為debug闽寡,使用的簽名文在$HOME/.android/debug.keystore)代兵,而我們打包的APK使用的簽名文件是我們自己創(chuàng)建的簽名文件,所以有沖突是肯定的爷狈,所以我們上面說一定要保存好我們自己生成的簽名文件和密碼植影,否則當(dāng)你做版本更新的時候是無法進行覆蓋安裝的。
  • 選擇已經(jīng)存在的簽名文件涎永,如果密碼打錯思币,也是可以繼續(xù)進行打包的,只是最后會顯示打包失敗羡微,錯誤日志顯示無法讀取這個日志文件谷饿,如果使用控制臺輸入命令生成Apk則沒有這個問題。
  • app 包下build.gradle妈倔,如果開啟混淆(minifyEnabled true)博投,但是混淆文件規(guī)則沒有配置,也會打包失敗盯蝴,如果不開啟混淆贬堵,則minifyEnabled false,開啟混淆的同時也是配合去除無用資源(shrinkResources true)一起使用的,如果结洼,關(guān)閉混淆,也要相應(yīng)設(shè)置shrinkResources false叉跛,如果為true也會打包失斔扇獭;如果單獨設(shè)置minifyEnabled true筷厘,并且混淆文件中的規(guī)則設(shè)置正確鸣峭,也是可以成功打包的。
 buildTypes {
        release {
          ......
          minifyEnabled true
          shrinkResources true
          ......
   }
}
  • 混淆配置
    混淆配置一般用于發(fā)布上線的Apk中酥艳,混淆Apk有兩個好處:
    • 可以防止你的項目被他人反編譯獲取源碼(如何反編譯Apk這里不做討論摊溶,反編譯用到的工具一般有apktool(反編譯Apk 得到資源文件,其中smile 文件是 .java 文件編譯后的文件充石,如果想破解Apk,需要熟悉smile語法莫换,有興趣大家可以去研究) ;dex2jar 將解壓的Apk 中的dex文件轉(zhuǎn)換成.jar文件;jd-gui 這個用具查看前面生成的.jar文件拉岁,到此就可以看到反編譯Apk的代碼坷剧,如果混淆,則會看到包名和類名用a b c等字符代替喊暖,讓你無法閱讀源碼惫企,如下圖)
    • 可以減小Apk的體積,混淆代碼陵叽,打包出來的Apk體積肯定會減小狞尔,這也是Apk瘦身的一種方案。
jd-gui查看反編譯Apk源碼(源碼被混淆).png
  • 如何混淆

    • 首先在app目錄下的build.gradle中做混淆配置巩掺,配置debug和release都使用proguard-rules.pro這個混淆規(guī)則文件偏序,我們可以先debug測試混淆規(guī)則通過后才release打包正式版,如下圖:
混淆配置 gradle.png
  • 其次锌半,配置混淆規(guī)則文件禽车,要配置混淆文件規(guī)則,首先要了解規(guī)則是什么刊殉,了解規(guī)則殉摔,具體的細節(jié)這里就不細說了,可以看這篇文章记焊,傳送門(http://www.reibang.com/p/b471db6a01af
 參數(shù):

-include {filename}    從給定的文件中讀取配置參數(shù)   
-basedirectory {directoryname}    指定基礎(chǔ)目錄為以后相對的檔案名稱   
-injars {class_path}    指定要處理的應(yīng)用程序jar,war,ear和目錄   
-outjars {class_path}    指定處理完后要輸出的jar,war,ear和目錄的名稱   
-libraryjars {classpath}    指定要處理的應(yīng)用程序jar,war,ear和目錄所需要的程序庫文件   
-dontskipnonpubliclibraryclasses    指定不去忽略非公共的庫類逸月。   
-dontskipnonpubliclibraryclassmembers    指定不去忽略包可見的庫類的成員。

保留選項

-keep {Modifier} {class_specification}    保護指定的類文件和類的成員   
-keepclassmembers {modifier} {class_specification}    保護指定類的成員遍膜,如果此類受到保護他們會保護的更好   
-keepclasseswithmembers {class_specification}    保護指定的類和類的成員碗硬,但條件是所有指定的類和類成員是要存在。   
-keepnames {class_specification}    保護指定的類和類的成員的名稱(如果他們不會壓縮步驟中刪除)   
-keepclassmembernames {class_specification}    保護指定的類的成員的名稱(如果他們不會壓縮步驟中刪除)   
-keepclasseswithmembernames {class_specification}    保護指定的類和類的成員的名稱瓢颅,如果所有指定的類成員出席(在壓縮步驟之后)   
-printseeds {filename}    列出類和類的成員-keep選項的清單恩尾,標準輸出到給定的文件  

壓縮

-dontshrink    不壓縮輸入的類文件   
-printusage {filename}   
-whyareyoukeeping {class_specification}  

優(yōu)化

-dontoptimize    不優(yōu)化輸入的類文件   
-assumenosideeffects {class_specification}    優(yōu)化時假設(shè)指定的方法,沒有任何副作用   
-allowaccessmodification    優(yōu)化時允許訪問并修改有修飾符的類和類的成員  

混淆

-dontobfuscate    不混淆輸入的類文件   
-printmapping {filename}   
-applymapping {filename}    重用映射增加混淆   
-obfuscationdictionary {filename}    使用給定文件中的關(guān)鍵字作為要混淆方法的名稱   
-overloadaggressively    混淆時應(yīng)用侵入式重載   
-useuniqueclassmembernames    確定統(tǒng)一的混淆類的成員名稱來增加混淆   
-flattenpackagehierarchy {package_name}    重新包裝所有重命名的包并放在給定的單一包中   
-repackageclass {package_name}    重新包裝所有重命名的類文件中放在給定的單一包中   
-dontusemixedcaseclassnames    混淆時不會產(chǎn)生形形色色的類名   
-keepattributes {attribute_name,...}    保護給定的可選屬性挽懦,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.  
-renamesourcefileattribute {string}    設(shè)置源文件中給定的字符串常量  
  • 最后翰意,貼出我打包項目的混淆配置規(guī)則文件,僅供參考信柿,如果直接復(fù)制進你項目冀偶,可能會打包出錯,具體需要根據(jù)自己的項目來酌情配置渔嚷, 如果項目中使用了第三方的包进鸠,一般可以去官網(wǎng)找到相應(yīng)的混淆配置。到此形病,混淆配置完成客年,這樣打包出來的Apk的代碼就已經(jīng)實現(xiàn)混淆霞幅。
#指定代碼的壓縮級別
-optimizationpasses 5

#包明不混合大小寫
-dontusemixedcaseclassnames

#不去忽略非公共的庫類
-dontskipnonpubliclibraryclasses

 #優(yōu)化  不優(yōu)化輸入的類文件
-dontoptimize

 #預(yù)校驗
-dontpreverify

 #混淆時是否記錄日志
-verbose

 # 混淆時所采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

#保護注解
-keepattributes *Annotation*

# 保持哪些類不被混淆
-keep public class * extends android.app.Fragment
-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 com.android.vending.licensing.ILicensingService
#如果有引用v4包可以添加下面這行
-keep public class * extends android.support.v4.app.Fragment


#忽略警告
-ignorewarning

##記錄生成的日志數(shù)據(jù),gradle build時在本項目根目錄輸出##
#apk 包內(nèi)所有 class 的內(nèi)部結(jié)構(gòu)
-dump proguard/class_files.txt
#未混淆的類和成員
-printseeds proguard/seeds.txt
#列出從 apk 中刪除的代碼
-printusage proguard/unused.txt
#混淆前后的映射
-printmapping proguard/mapping.txt
########記錄生成的日志數(shù)據(jù),gradle build時 在本項目根目錄輸出-end######

#如果引用了v4或者v7包
-dontwarn android.support.**

####混淆保護自己項目的部分代碼以及引用的第三方j(luò)ar包library-end####



#保持 native 方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}

#保持自定義控件類不被混淆
-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}

#保持自定義控件類不被混淆
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

-keep public class * extends android.view.View {
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(...);
}

#保持 Parcelable 不被混淆
-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}

#保持 Serializable 不被混淆
-keepnames class * implements java.io.Serializable

#保持 Serializable 不被混淆并且enum 類也不被混淆
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient <fields>;
    !private <fields>;
    !private <methods>;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

#保持枚舉 enum 類不被混淆
-keepclassmembers enum * {
  public static **[] values();
  public static ** valueOf(java.lang.String);
}

-keepclassmembers class * {
    public void *ButtonClicked(android.view.View);
}

#不混淆資源類
-keepclassmembers class **.R$* {
    public static <fields>;
}

#避免混淆泛型 如果混淆報錯建議關(guān)掉
-keepattributes Signature

#移除Log類打印各個等級日志的代碼搀罢,打正式包的時候可以做為禁log使用蝗岖,這里可以作為禁止log打印的功能使用,另外的一種實現(xiàn)方案是通過BuildConfig.DEBUG的變量來控制
-assumenosideeffects class android.util.Log {
    public static *** v(...);
    public static *** i(...);
    public static *** d(...);
    public static *** w(...);
    public static *** e(...);
}

#gson
#如果用用到Gson解析包的榔至,直接添加下面這幾行就能成功混淆抵赢,不然會報錯。
-keepattributes Signature
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.** { *; }
-keep class com.google.gson.stream.** { *; }

#mob
-keep class android.net.http.SslError
-keep class android.webkit.**{*;}
-keep class cn.sharesdk.**{*;}
-keep class com.sina.**{*;}
-keep class m.framework.**{*;}
-keep class **.R$* {*;}
-keep class **.R{*;}
-dontwarn cn.sharesdk.**
-dontwarn **.R$*

#butterknife
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }

-keepclasseswithmembernames class * {
    @butterknife.* <fields>;
}

-keepclasseswithmembernames class * {
    @butterknife.* <methods>;
}

# 如果使用了Gson之類的工具要使被它解析的JavaBean類即實體類不被混淆唧取。
-keep class com.lecloud.base.** { *; }
-keep class com.lecloud.imlib.** { *; }
-keep class com.lecloud.leblockmodelmanager.** { *; }


# --------------- 友盟統(tǒng)計避免混淆 -------------------------
-dontwarn Android.support.v4.**
-dontwarn org.apache.commons.net.**
-dontwarn com.tencent.**
-keepclasseswithmembernames class * {
    native <methods>;
}
-keepclasseswithmembernames class * {
    public <init>(Android.content.Context, Android.util.AttributeSet);
}
-keepclasseswithmembernames class * {
    public <init>(Android.content.Context, Android.util.AttributeSet, int);
}
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
-keep class * implements Android.os.Parcelable {
  public static final Android.os.Parcelable$Creator *;
}
-keepclasseswithmembers class * {
    public <init>(Android.content.Context);
}
-dontshrink
-dontoptimize
-dontwarn com.google.Android.maps.**
-dontwarn Android.webkit.WebView
-dontwarn com.umeng.**
-dontwarn com.tencent.weibo.sdk.**
-dontwarn com.facebook.**
-keep enum com.facebook.**
-keepattributes Exceptions,InnerClasses,Signature
-keepattributes *Annotation*
-keepattributes SourceFile,LineNumberTable
-keep public interface com.facebook.**
-keep public interface com.tencent.**
-keep public interface com.umeng.socialize.**
-keep public interface com.umeng.socialize.sensor.**
-keep public interface com.umeng.scrshot.**
-keep public class com.umeng.socialize.* {*;}
-keep public class javax.**
-keep public class Android.webkit.**
-keep class com.facebook.**
-keep class com.umeng.scrshot.**
-keep public class com.tencent.** {*;}
-keep class com.umeng.socialize.sensor.**
-keep class com.tencent.mm.sdk.openapi.WXMediaMessage {*;}
-keep class com.tencent.mm.sdk.openapi.** implements com.tencent.mm.sdk.openapi.WXMediaMessage$IMediaObject {*;}
-keep class im.yixin.sdk.api.YXMessage {*;}
-keep class im.yixin.sdk.api.** implements im.yixin.sdk.api.YXMessage$YXMessageData{*;}
-keep public class [your_pkg].R$*{
    public static final int *;
}

#極光推送
-dontoptimize
-dontpreverify

-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }

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

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

-keepclasseswithmembernames class * {
    @butterknife.* <methods>;
}
#fastjson
-dontwarn com.alibaba.fastjson.**
-keep class com.alibaba.fastjson.** { *; }

#webView處理铅鲤,項目中沒有使用到webView忽略即可
-keepclassmembers class fqcn.of.javascript.interface.for.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, jav.lang.String);
}

#融云
-keepattributes Exceptions,InnerClasses

-keepattributes Signature

# RongCloud SDK
-keep class io.rong.** {*;}
-keep class * implements io.rong.imlib.model.MessageContent {*;}
-dontwarn io.rong.push.**
-dontnote com.xiaomi.**
-dontnote com.google.android.gms.gcm.**
-dontnote io.rong.**

# VoIP
-keep class io.agora.rtc.** {*;}

# Location
-keep class com.amap.api.**{*;}
-keep class com.amap.api.services.**{*;}

# 紅包
-keep class com.google.gson.** { *; }
-keep class com.uuhelper.Application.** {*;}
-keep class net.sourceforge.zbar.** { *; }
-keep class com.google.android.gms.** { *; }
-keep class com.alipay.** {*;}
-keep class com.jrmf360.rylib.** {*;}

-ignorewarnings

#播放器框架
-keep class tv.danmaku.ijk.media.player.** {*;}

好了,本次記錄就到這里了枫弟,如果大家有發(fā)現(xiàn)不對的地方邢享,歡迎給我指出,大家一起學(xué)習(xí)進步淡诗。如果覺得文章對你有幫助骇塘,也請給我一個喜歡。

參考文章:http://blog.csdn.net/u012124438/article/details/54958757

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末韩容,一起剝皮案震驚了整個濱河市款违,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌群凶,老刑警劉巖插爹,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異请梢,居然都是意外死亡赠尾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門毅弧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來气嫁,“玉大人,你說我怎么就攤上這事够坐〈缦” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵咆霜,是天一觀的道長。 經(jīng)常有香客問我嘶朱,道長蛾坯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任疏遏,我火速辦了婚禮脉课,結(jié)果婚禮上救军,老公的妹妹穿的比我還像新娘。我一直安慰自己倘零,他們只是感情好唱遭,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著呈驶,像睡著了一般拷泽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上袖瞻,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天司致,我揣著相機與錄音,去河邊找鬼聋迎。 笑死脂矫,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的霉晕。 我是一名探鬼主播庭再,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼牺堰!你這毒婦竟也來了拄轻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤萌焰,失蹤者是張志新(化名)和其女友劉穎哺眯,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扒俯,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡奶卓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了撼玄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夺姑。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖掌猛,靈堂內(nèi)的尸體忽然破棺而出盏浙,到底是詐尸還是另有隱情,我是刑警寧澤荔茬,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布废膘,位于F島的核電站,受9級特大地震影響慕蔚,放射性物質(zhì)發(fā)生泄漏丐黄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一孔飒、第九天 我趴在偏房一處隱蔽的房頂上張望灌闺。 院中可真熱鬧艰争,春花似錦、人聲如沸桂对。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蕉斜。三九已至逾柿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蛛勉,已是汗流浹背鹿寻。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留诽凌,地道東北人毡熏。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像侣诵,于是被迫代替她去往敵國和親痢法。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

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