Android碎片化與兼容性問題的元兇

Android碎片化是其發展中的一個計劃內的必然結果,也從來就不是什么問題。真正的問題是不同Android系統的應用兼容性。

Android的碎片化

Android的碎片化,是指它在發展的過程中,隨著自身版本的更迭,以及硬件廠商的自行定制,而帶來的一個版本數量爆炸的問題。

碎片化的Android

本來Google的Android自身就有版本更迭的需求,這是當代大多數主流軟件的做法。流行的Android版本有2.2、2.3、3.2、4.0.3、4.2.2、4.4、5.0、5.1等,近期6.0也發布了。

但是,和大多數軟件不同的是,Android是手機的操作系統,不是說更新就更新的。很多改動不能保證系統層面的向前兼容,因此不能像普通軟件那樣推送了就更新。Android更新既有風險,也有限制

例如,Android 4.0.3一共就100M,而Android 5.1.1有700M,這還僅僅是原生ROM的大小,廠商定制的往往更大一些。 對部分高端的Android 4.0時代的手機來說,也許僅僅需要更改System分區的大小。但是磁盤分區,通用做法是先格式化——所以有數據丟失的風險。而對部分低端的手機來說,根本就沒那么大的容量來裝系統,誰知道它會暴漲7倍——所以還有根本更新不了的限制情況。

這一點,Android類似Windows。

但是,這也就是一個線性增長而已。真正的麻煩來自于每一個廠商都自己做Android與手機硬件的適配,其中大的那些還做定制,例如小米的MIUI、華為的EMUI、聯想的VIBEUI等。這就是N個廠商乘以M個版本,導致的版本數量爆炸。

開放手機聯盟與Android的哲學

開放手機聯盟的歷史

每一本寫Android相關的書,第一章提到發展歷史時,都會提到一個開放手機聯盟Open Handset Alliance )。算上初創和后來加入的,從2007年至2015年,已有84個成員。

The OHA was established on 5 November 2007, led by Google with 34 members, including mobile handset makers, application developers, some mobile carriers and chip makers.

成員中,由開發Android的Google領銜,既包含手機制造商如HTC、Motorola、LG等,也包含了移動通信主要專利持有者及芯片提供商如高通(Qualcomm)、聯發科(MediaTek)、愛立信(ST-Ericsson)等,也有其它手機芯片提供商德州儀器(Texas Instruments)、Marvell、博通(broadcom)、英偉達(NVIDIA)等,還有移動運營商如T-Mobile、中國移動等。更有三星(Samsung)、華為這種什么都沾邊的巨無霸。

這可以說是囊括了手機殼子里面所有相關軟硬件的一個全球性大聯盟。

這是Google為了推出Android與Apple部落的iOS抗衡而組建的聯盟,而現在回頭,也明顯看到了效果。Android超越了iOS,成為了市場份額第一。

Android必須碎片化

Android設備的屏幕分布。矩形代表屏幕大小,顏色深淺代表數量多少。

雖然沒有詳查相關協議,但是Android作為聯盟中連接各方的軟件實體,其地位非同一般。如果哪天Google不再開源Android,那么聯盟立刻瓦解。

Android從一開始,就肩負了拯救世界……呃呸,兼容不同硬件配置的手機設備的責任。Google與中游的移動通訊芯片提供商、下游的手機廠商,以及各種其它芯片、傳感器的解決方案提供商,共同努力,才能補全一個完整的Android。Google一家,造不出一臺Android手機(Nexus不僅代工,還代研)。

現在,一臺Android手機的系統軟件,開發流程通常是:

Google發布Android -> Qualcomm(or MediaTek)-> 手機廠商集成其它芯片的驅動 -> 手機廠商定制

為了讓廠商能夠閉源,Android不遵循GPL協議。這是一個“我開放,你基于我就必須開放”的傳染式協議,是一些廠商不能接受的。尤其是一些解決方案提供商,把驅動的源碼公開給競爭對手,基本上老底全泄光了。

Android不僅換成了允許商業閉源的Apache協議,還用各種方式把廠商需要修改的底層驅動,放到不會受Linux的GPL協議傳染的地方。

開源Android,然后給手機廠商進行閉源再開發,這本就是題中應有之義。Android碎片化是一個計劃內的必然結果。

其實碎片化不是問題,真正的問題是兼容性——明明是一個Android應用,卻仍然無法正常運行;在那臺手機好好的,換這臺就不行。

Android的哲學

與iOS的封閉相對,Android的哲學核心就是開放

不僅代碼開源,而且實際運行的應用之間也有多種方式進行通信、協作,與iOS的隔離沙箱完全不同。

其次是替換

除了Android kernel本身,任何應用都是可以替換的。桌面系統Launcher的抽屜式設計如果你不喜歡,可以換成各種其它桌面;圖標看不慣,可以換各種其它主題。你可以用多款相機軟件來使用同一個相機硬件,也可以用不同的通訊錄軟件來管理通訊錄。理論上,除了【設置】以外(畢竟Google不可能知道下游的廠商在手機里安了什么硬件、需要怎么設置),其它軟件都是可替換的。

什么都是可以選擇的,這體現了一種自由的精神,一種軟件之間開放競爭的理念。

當然,一些需要系統權限的功能,必須在System分區預置。

手機廠商的定制,從來就不是問題。與普通開發者相比,廠商無非就是通過預置在System分區可以獲取系統權限。其它不需要系統權限的功能,Google、定制廠商、普通開發者,都在同一起跑線,可以自由競爭。

兼容性問題出在哪兒?

Google的問題

先說源頭的Google。

CTS

為了保證Android在開放并且碎片化的前提下,能夠有同樣的兼容性,Google設計了CTS這樣一個自動化測試。

CTS全稱Compatibility Test Suite,兼容性測試工具,其實是指Google發布的一組測試Android手機兼容性的工具,僅支持Linux平臺。

這套工具只是給系統平臺開發者用的,也就是手機廠商的軟件研發人員,所以相關資料不多。

這是一種自上而下,測試全套Android API及其系統資源是否有效的測試。如果某個API無法在調用后達成特定效果、返回正確的值,或者某張系統圖片被改動了一個像素,都會導致測試失敗。

每一臺Android手機,即使是廠商定制的,都要通過CTS測試。只有在Google公司里,通過了全套CTS測試的手機,才被Google官方認可為Android的手機,才能顯示“Powered by Android”并掛牌銷售。

可以說,這套測試系統是Google對Android兼容性的最大努力。也正是因此,無論手機廠商做出了怎樣的定制修改,都能支持正常的Android應用。

v4、v7、v13……

Android開發者對Android Support v4這個jar包都不會陌生,這是為了讓Android v1.6及以后的一些低版本,能夠使用一些高級版本才有的特性,而特別加入的。

4、7、13等,這些不是版本號,而是API Level,是與版本不同的另一套編號。基本上每一個重要的小版本改動,API Level都會加一。目前6.0的API Level是23。

這是一種向前兼容的努力。Google希望開發者在后來的新版本開發的應用,也能在一些比較老舊的版本里兼容。

此外,向后兼容,則是體現在對API的持續維護,盡量保證只增不減

但是,目前的Android應用前后版本不兼容,往往體現為舊版本的一些應用在新版本不能用。這類有些是Android API維護問題,有大量@deprecated的API。這些廢棄接口不能保證在幾個API Level后的版本仍然能正常使用。

新版本應用在舊版本不能用,往往體現在開發者沒有使用v4等兼容包,或者手機機能限制無法支持。是開發者或用戶的責任。當然,為了新版本而開發軟件沒有必要兼容舊版本,用戶也沒有義務更新手機硬件。責任是在那,但并沒有錯。只是不能把責任推給別人。

這些都是小問題。

Google Play Services

如果說CTS保證了每一個版本的橫向兼容(比如,每一個基于Android 4.4定制的其它ROM,都要保證和原生Android 4.4兼容),那么v4等包則保證了縱向兼容(Android 4.4的應用能在Android 2.2的版本運行)。 這之中如果出現一些問題,也都是小問題。

導致閃退最多的因素就是缺乏Google Play Services

Google Play是Google自家的Android應用商店,目前是全球最大的應用商店。很多在其中發布的應用,都依賴于Google在Android中預置的Google Play Services,以調用應用商店相關信息、Google+賬戶等內容。

而這個GoogleServicesFramework.apk,必須要在System分區才能起作用。

對其技術細節,我也不是很清楚,畢竟活在墻內。我不知道為什么這個apk需要這樣做,但是在Android的系統架構上,它一定是可以不通過這種方式實現的。應用寶、360手機助手這種應用商店,就完全沒要求廠商預置在系統分區,而是用戶隨時下載隨時使用。

這個必須放在System分區的要求,就使得要么預置,要么偽裝為預置(獲取Root權限然后復制到/System/app/下)。

這就是最大的問題,也是絕大多數Google Play上的Android應用閃退的原因!

一般來說,如果一個應用依賴于第三方類庫、框架,例如各種.so、.jar文件,往往會自行在編譯時設為Dependency,自帶在APK里;如果依賴一個第三方APK,也可以打包在自家APK里,或者提示用戶下載安裝這個APK。

Google利用職務之便(發布Android系統給芯片提供商),改成了這種模式,直接造成了Android最大的兼容性問題。

Google Play 的野心

谷歌不是一家搜索廣告公司,是一家研發公司。

這是Google高層對自己的界定。不得不說,非常了不起。

但是,商業公司都是逐利的,上市公司尤其如此。這一點,并不能被CEO的意志所改變。你可以不吃肉,也可以不吃素,但不能不吃飯。
無論Google在人們心中是什么形象,無論Google曾經為了技術或以外的什么因素而放棄了多少賺錢機會,都不能否認這一點,它是要逐利的。這也并沒有什么不對。

從盈利的角度來看,Google大約90%的收入來源于廣告,可以說是一家賣廣告的公司。

Google組建開放手機聯盟是為了推廣Android,這個過程是無利的。推廣Android是為了制造一個可以與封閉的iOS抗衡的大平臺,這個過程是推動世界智能手機技術發展的。但是制造大平臺還不夠,因為Google不收Android的授權費(也正因為如此,Google不對下游提供技術支持),它是開源免費的。

這樣,利益從何處來?
做Android的研發人員難道靠做搜索的來養?

答案就是Google Play應用商店,一個Android平臺中的平臺,完全由Google獨家控制的大平臺。在這里,Google可以繼續賣廣告

但是,Google Play只是一個應用,不屬于Android kernel。按照Android的設計哲學,它也是、或者說也應該是,可以替換的。

對開放手機聯盟的背叛

用戶、普通開發者,對開發手機聯盟基本沒有了解。甚至聯盟內部的系統開發者,都對這事知之甚少,平時也閉口不提。

看小米科技大紅大紫,其實它根本沒加入這個聯盟。而作為領跑者的HTC,如今在生死線上掙扎。我不禁要問,加入這個聯盟有什么好處?

為什么在當代,中國大陸的智能手機飛速崛起,而海外(包括臺灣省)的除了韓系三星、LG等,基本都風雨飄搖,既無大利潤,也無大出貨量?

這個問題很大,解答也很多,這里僅提出一個觀點:那堵墻,逼迫中國廠商去除Google相關應用,換上自家做的。本來不情不愿地額外付出了研發成本,卻意外地奪回了其中蘊含的巨大利益,并借此生存了下來。

小米直言“硬件不賺錢”,但由于牢牢把控了應用商店、視頻、音樂、閱讀、瀏覽器等平臺,卻獲得了足以飛速發展的高額利潤,同時也提高了中國用戶的體驗。而國外的廠商,強如HTC、Motorola,也只能賺一點辛苦的硬件差價,為Google做嫁衣。

應用商店,這一類基于Android的平臺究竟該是誰的蛋糕?

既是Google的,也是廠商的,也是其它第三方平臺的。可以說,就算Apple跑來做一個“Apk store”,也是符合Android規矩的。
而Google把Google Play預置在System分區,否則不可用,這種技術手段,直接掠奪了墻外相關組織的利益。通過預置和身在System分區的優勢,強行提高競爭力,把國外所有第三方APK分發平臺打得潰不成軍。
這與Microsoft預置IE、QQ綁定各種自家應用、PC的360安全中心自動把360手機助手安裝到插入電腦的Android手機上的這類做法,別無二致。

從某種意義上來說,Android是屬于開放手機聯盟的。可以說,沒有這個聯盟的推廣,就沒有Android的今天。如果各大公司各行其是,昔日的王者Symbian將持續演進,Windows Phone將不會慘淡,如三星BADA這樣的大廠商自制系統也不會少見。

而如今,Android一統天下,聯盟被Google用完就甩了。

本來大家以為Android是手機界的Linux,但其實它是一個可以讓所有人看代碼、可以讓部分人修改分支代碼、只有Google自己才能修改主線代碼的Windows。

高通與聯發科的問題

前面說過,高通(Qualcomm,也簡稱QC)和聯發科(MediaTek,也簡稱MTK),是移動通訊相關技術的主要芯片提供商。此外,它們也是開發一個Android手機過程中的一個獨立環節。手機廠商都是從這一環節發布的Android開始開發,而不是直接修改Google的版本。

Telephony完全不同

Android kernel,面向普通開發者的部分,主要是指frameworks/base下編譯出來的那個jar。而Telephony是指frameworks/opt/telephony編譯出來的jar,也包括部分apk。算是Android API下面實現層的一個組成部分。

Telephony下面是RIL( Radio Interface Layer ),負責和C/C++那一層對接。再下面就是Modem芯片了。Modem是智能手機作為“手機”的那部分功能的硬件,包括電話、短信、數據連接等,負責和移動基站交互。

以往Google是不太管Telephony的,往往都交給高通、聯發科這樣的芯片提供商來定制。這兩家在Telephony可以說各逞手段,除了大概的架構,細節幾乎完全不同!

比如,在Android 4.4及以前,Android手機是不能支持多張手機卡的。市面上的所有雙卡雙待,都是這兩家自行定制,手機廠商再持續改良的結果。實現方案完全不同。

這直接導致了電話、短信等相關應用,基本無法被普通開發者開發,因為有大量的非API依賴。而各大廠商、甚至同一廠商不同型號的手機,這類應用都無法通用。

Google插手Telephony

在Android 5.0以后,Google開始介入Telephony的開發。但是由于版本碎片化,這并沒有解決兼容性問題。最多就是可以期待以后的向后兼容。

不過,無情的事實打碎了這種美好的愿景。

整個Telephony在Android 5.0的大幅修改,換來的是與之前版本的完全不兼容。尤其是聯發科一系,幾乎全部特有實現都砍掉了。從實現方式看,很可能這是高通技術支持的結果,因此聯發科成了這次版本變動最大的輸家。

從Android 5.0到5.1,對所有用戶、絕大多數開發者來說,基本沒什么變化。但是對Telephony來說,又是一次翻天覆地的大變。

這兩次改動,變化的類、接口、數據庫字段等,實在太多。這里僅說一個:subId

Subscription Identification,是每一張電話卡在手機中的唯一標志符。第一插入的卡是1,此后在數據庫中依次遞增。在Android 4.4及以前,這是一個int類型;在Android 5.0,這變成了一個long類型;在Android 5.1以后,這又變成了一個int類型!

雖然這個東西,并不是對外(普通開發者)開放的,但也是處于整個Telephony模塊核心地位,而且上層的部分系統應用都有涉及,改一動萬,上下全亂。即使修改,也不能這樣反復。這體現出了相關模塊負責人的技術水平。

對Nexus以外的智能手機用戶來說,這兩個版本(5.0、5.1)在“智能”方面的功能可能問題不大,在“手機”方面的功能(駐網、識卡、電話、短信、數據連接等)都處于一個不穩定的狀態。比如前面說的雙卡雙待功能,Google介入后,下游必須大幅改動、重新實現,造成額外開發成本,并降低了系統穩定性。如果在這兩個版本的手機上發現相關bug或兼容性問題,十有八九是這個值的類型改動造成的蝴蝶效應所致。

聯發科的手很長

和高通比,聯發科在移動通訊的技術上是有很大差距的。不知道是不是因為在芯片技術上比不過,聯發科對手機廠商提供的技術支持非常豐富而廣泛。

聯發科的手長,不僅體現在硬件上(除了CPU、Modem外還提供很多其它),也體現在軟件上。高通在Telephony及相關的模塊以外,修改較少,而聯發科則到處都是!從系統應用,到framework,再到下面的各種C/C++層,到處都是它的爪印。用

// M: For some reason {
...
// }

這種東西包著的代碼塊就是了。

這就導致很多應用,在高通的Android手機上可以正常運行,在聯發科的不可以。

比如視頻播放軟件,要播放MKV等格式,在很多聯發科的手機上不支持硬件解碼(即芯片本身不能解碼播放),只能使用軟件解碼播放。而大多數視頻軟件根本不支持軟件解碼,這就導致軟件閃退、視頻無法播放等問題。

當然,也有少數反過來的情況。例如VPN,聯發科的支持就比高通好些。

無論如何,高通和聯發科在通訊技術以外的模塊,多少都有介入,聯發科尤其多。二者各顯神通的結果,就是部分應用在這兩類Android手機上不能同時兼容。

國內廠商的問題

去除Google Play Services

國內廠商定制Android ROM,通常第一步都是先去除Google Play Services。

這是被指責最多的一點,但是如前所述,一方面這是國情所限(部分仇恨轉移到政府),另一方面這是Android生態“題中應有之義”(剩下仇恨轉移回Google)。

這類閃退,主要原因還是Google把很多Android應用,誘拐為Google Play應用所致。

其實,從代碼層面來講是非常清楚的。

所有Android API在Java里import時,都是import android.*這種形式的,而屬于Java語言本身的那部分,則是import java.*。除了這二者之外,Android系統在原則上不提供任何其它方式的調用,要import其它的class,請自備jar。

import com.android.*這種的,是系統內部自用的,不提供給普通開發者。
import com.google.*這種東西,和Android沒有什么關系。

另外,從CTS的角度來講,任何一臺通過CTS的手機,都是Android手機;能夠在某個版本以上的、通過CTS的所有手機上正常運行的應用,才是Android應用。因此依賴于Google Play Services的應用,本身就不具備Android需要的兼容性,這也是我為什么說“誘拐為Google Play應用”。

如果Google真的認為Google Play Services是Android不可分割的一部分,那么將其存在性檢查加入CTS就好了。Why not?

不能在任意一臺通過了CTS的手機上正常運行的應用,就不是一個兼容性良好的Android應用,而只是一個專有應用罷了。比如一個只能在華為手機上正常使用的應用,就只能算華為應用;一個只能在預置了Google Play的手機上運行的應用,就只是Google Play應用罷了。

所以,不是國內手機不兼容Google Play應用,而是Google Play的部分應用不兼容Android!

無腦定制與bug

廠商在定制的時候,很多在framework層的修改,是欠缺系統規劃的。往往是這需要個功能,添加一個代碼塊;那有一個bug,添加一個代碼塊。

這些多余的東西,在普通應用調用相關API時,會產生額外的后果,有時會產生應用的功能不正常或直接崩潰。

最典型的就是,普通應用在某些情況下調一個API,接口內部的廠商代碼塊中發生概率性bug,丟異常,導致應用崩潰。

這類是廠商的軟件工程師水平低下所致。

傳感器問題

手機或受限于研發成本、或受限于技術限制、或受限于目標用戶群體,在一些功能細節上是不一致的。

例如有些手機有溫度傳感器、氣壓傳感器,最近的手機又開始一起安裝指紋解鎖、NFC等。這些都不是手機的必備硬件,是完全可以由廠商自主定制的。

而硬件上的不同,帶來的應用功能失效,這也是兼容性問題的一種。比如一個測溫度的應用,放到一個沒有溫度傳感器的手機上,當然無法正常工作。

這就完全沒辦法了。

結語

一不小心寫多了。

Google是Android應用兼容性問題最大的元兇!——其實我就是想把這個會被拍磚的觀點,藏在標題以外的地方,并且讓它顯得合情合理一點,字數就上去了。

Google明明為了在Android必須碎片化(開放手機聯盟)的前提下,保證應用兼容性而做出了多重努力(CTS、v4等兼容包),但還是自相矛盾地搞出了GoogleServicesFramework.apk必須放到System分區這種富含商業深意的技術設定。這體現了一家了不起的商業大公司,在擁有那樣的領導人、那樣的工程師文化的情況下,仍然也不得不時常屈服于逐利的本能需求。

篇幅頭重腳輕,非是想對Google多加指責,對國內手機廠商隨便說說。實是寫到后面,精力不濟、吐槽不能。

最后說下身份。我是某國內手機廠商的Android系統平臺開發人員(仿佛看見一盆盆狗血往頭上噴來),視角與常人不同,一些觀點也難免受到自身立場影響。

技術和閱歷有限,歡迎指正。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容