国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Android性能優化之內存優化

cheng10 / 2305人閱讀

摘要:導語智能手機發展到今天已經有十幾個年頭,手機的軟硬件都已經發生了翻天覆地的變化,特別是陣營,從一開始的一兩百到今天動輒,內存。恰好最近做了內存優化相關的工作,這里也對內存優化相關的知識做下總結。

導語

智能手機發展到今天已經有十幾個年頭,手機的軟硬件都已經發生了翻天覆地的變化,特別是Android陣營,從一開始的一兩百M到今天動輒4G,6G內存。然而大部分的開發者觀看下自己的異常上報系統,還是會發現各種內存問題仍然層出不窮,各種OOM為crash率貢獻不少。Android開發發展到今天也是已經比較成熟,各種新框架,新技術也是層出不窮,而內存優化一直都是Android開發過程一個不可避免的話題。 恰好最近做了內存優化相關的工作,這里也對Android內存優化相關的知識做下總結。

在開始文章之前推薦下公司同事翻譯整理版本《Android性能優化典范 - 第6季》,因為篇幅有限這里我對一些內容只做簡單總結,同時如果有不正確內容也麻煩幫忙指正。

本文將會對Android內存優化相關的知識進行總結以及最后案例分析(一二部分是理論知識總結,你也可以直接跳到第三部分看案例):

一、 Android內存分配回收機制
二 、Android常見內存問題和對應檢測,解決方式。
三、 JOOX內存優化案例
四 、總結

工欲善其事必先利其器,想要優化App的內存占用,那么還是需要先了解Android系統的內存分配和回收機制。

一 ,Android內存分配回收機制

參考Android 操作系統的內存回收機制[1],這里簡單做下總結:

從宏觀角度上來看Android系統可以分為三個層次
1. Application Framework,
2. Dalvik 虛擬機
3. Linux內核。

這三個層次都有各自內存相關工作:

1. Application Framework

Anroid基于進程中運行的組件及其狀態規定了默認的五個回收優先級:

Empty process(空進程)

Background process(后臺進程)

Service process(服務進程)

Visible process(可見進程)

Foreground process(前臺進程)

系統需要進行內存回收時最先回收空進程,然后是后臺進程,以此類推最后才會回收前臺進程(一般情況下前臺進程就是與用戶交互的進程了,如果連前臺進程都需要回收那么此時系統幾乎不可用了)。

由此也衍生了很多進程保活的方法(提高優先級,互相喚醒,native保活等等),出現了國內各種全家桶,甚至各種殺不死的進程。

Android中由ActivityManagerService 集中管理所有進程的內存資源分配。

2. Linux內核

參考QCon大會上阿里巴巴的Android內存優化分享[2],這里最簡單的理解就是ActivityManagerService會對所有進程進行評分(存放在變量adj中),然后再講這個評分更新到內核,由內核去完成真正的內存回收(lowmemorykiller, Oom_killer)。這里只是大概的流程,中間過程還是很復雜的,有興趣的同學可以一起研究,代碼在系統源碼ActivityManagerService.java中。

3. Dalvik虛擬機

Android進程的內存管理分析[3],對Android中進程內存的管理做了分析。

Android中有Native Heap和Dalvik Heap。Android的Native Heap言理論上可分配的空間取決了硬件RAM,而對于每個進程的Dalvik Heap都是有大小限制的,具體策略可以看看android dalvik heap 淺析[4]。

Android App為什么會OOM呢?其實就是申請的內存超過了Dalvik Heap的最大值。這里也誕生了一些比較”黑科技”的內存優化方案,比如將耗內存的操作放到Native層,或者使用分進程的方式突破每個進程的Dalvik Heap內存限制。

Android Dalvik Heap與原生Java一樣,將堆的內存空間分為三個區域,Young Generation,Old Generation, Permanent Generation。

最近分配的對象會存放在Young Generation區域,當這個對象在這個區域停留的時間達到一定程度,它會被移動到Old Generation,最后累積一定時間再移動到Permanent Generation區域。系統會根據內存中不同的內存數據類型分別執行不同的gc操作。

GC發生的時候,所有的線程都是會被暫停的。執行GC所占用的時間和它發生在哪一個Generation也有關系,Young Generation中的每次GC操作時間是最短的,Old Generation其次,Permanent Generation最長。

GC時會導致線程暫停,導致卡頓,Google在新版本的Android中優化了這個問題, 在ART中對GC過程做了優化揭秘 ART 細節 —— Garbage collection[5],據說內存分配的效率提高了10倍,GC的效率提高了2-3倍(可見原來效率有多低),不過主要還是優化中斷和阻塞的時間,頻繁的GC還是會導致卡頓。

上面就是Android系統內存分配和回收相關知識,回過頭來看,現在各種手機廠商鼓吹人工智能手機,號稱18個月不卡頓,越用越快,其實很大一部分Android系統的內存優化有關,無非就是利用一些比較成熟的基于統計,機器學習的算法定時清理數據,清理內存,甚至提前加載數據到內存。

二 ,Android常見內存問題和對應檢測,解決方式 1. 內存泄露

不止Android程序員,內存泄露應該是大部分程序員都遇到過的問題,可以說大部分的內存問題都是內存泄露導致的,Android里也有一些很常見的內存泄露問題[6],這里簡單羅列下:

單例(主要原因還是因為一般情況下單例都是全局的,有時候會引用一些實際生命周期比較短的變量,導致其無法釋放)

靜態變量(同樣也是因為生命周期比較長)

Handler內存泄露[7]

匿名內部類(匿名內部類會引用外部類,導致無法釋放,比如各種回調)

資源使用完未關閉(BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap)

對Android內存泄露業界已經有很多優秀的組件其中LeakCanary最為知名(Square出品,Square可謂Android開源界中的業界良心,開源的項目包括okhttp, retrofit,otto, picasso, Android開發大神Jake Wharton就在Square),其原理是監控每個activity,在activity ondestory后,在后臺線程檢測引用,然后過一段時間進行gc,gc后如果引用還在,那么dump出內存堆棧,并解析進行可視化顯示。使用LeakCanary可以快速地檢測出Android中的內存泄露。

正常情況下,解決大部分內存泄露問題后,App穩定性應該會有很大提升,但是有時候App本身就是有一些比較耗內存的功能,比如直播,視頻播放,音樂播放,那么我們還有什么能做的可以降低內存使用,減少OOM呢?

2. 圖片分辨率相關

分辨率適配問題。很多情況下圖片所占的內存在整個App內存占用中會占大部分。我們知道可以通過將圖片放到hdpi/xhdpi/xxhdpi等不同文件夾進行適配,通過xml android:background設置背景圖片,或者通過BitmapFactory.decodeResource()方法,圖片實際上默認情況下是會進行縮放的。在Java層實際調用的函數都是或者通過BitmapFactory里的decodeResourceStream函數

public static Bitmap decodeResourceStream(Resources res, TypedValue value,
        InputStream is, Rect pad, Options opts) {

    if (opts == null) {
        opts = new Options();
    }

    if (opts.inDensity == 0 && value != null) {
        final int density = value.density;
        if (density == TypedValue.DENSITY_DEFAULT) {
            opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
        } else if (density != TypedValue.DENSITY_NONE) {
            opts.inDensity = density;
        }
    }

    if (opts.inTargetDensity == 0 && res != null) {
        opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
    }

    return decodeStream(is, pad, opts);
}

decodeResource在解析時會對Bitmap根據當前設備屏幕像素密度densityDpi的值進行縮放適配操作,使得解析出來的Bitmap與當前設備的分辨率匹配,達到一個最佳的顯示效果,并且Bitmap的大小將比原始的大,可以參考下騰訊Bugly的詳細分析Android 開發繞不過的坑:你的 Bitmap 究竟占多大內存?。

關于Density、分辨率、-hdpi等res目錄之間的關系:

舉個例子,對于一張1280×720的圖片,如果放在xhdpi,那么xhdpi的設備拿到的大小還是1280×720而xxhpi的設備拿到的可能是1920×1080,這兩種情況在內存里的大小分別為:3.68M和8.29M,相差4.61M,在移動設備來說這幾M的差距還是很大的。

盡管現在已經有比較先進的圖片加載組件類似Glide,Facebook Freso, 或者老牌Universal-Image-Loader,但是有時就是需要手動拿到一個bitmap或者drawable,特別是在一些可能會頻繁調用的場景(比如ListView的getView),怎樣盡可能對bitmap進行復用呢?這里首先需要明確的是對同樣的圖片,要 盡可能復用,我們可以簡單自己用WeakReference做一個bitmap緩存池,也可以用類似圖片加載庫寫一個通用的bitmap緩存池,可以參考GlideBitmapPool[8]的實現。

我們也來看看系統是怎么做的,對于類似在xml里面直接通過android:background或者android:src設置的背景圖片,以ImageView為例,最終會調用Resource.java里的loadDrawable:

Drawable loadDrawable(TypedValue value, int id, Theme theme) throws NotFoundException {

    // Next, check preloaded drawables. These may contain unresolved theme
    // attributes.
    final ConstantState cs;
    if (isColorDrawable) {
        cs = sPreloadedColorDrawables.get(key);
    } else {
        cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
    }

    Drawable dr;
    if (cs != null) {
        dr = cs.newDrawable(this);
    } else if (isColorDrawable) {
        dr = new ColorDrawable(value.data);
    } else {
        dr = loadDrawableForCookie(value, id, null);
    }

    ...

    return dr;
}  

可以看到實際上系統也是有一份全局的緩存,sPreloadedDrawables, 對于不同的drawable,如果圖片時一樣的,那么最終只會有一份bitmap(享元模式),存放于BitmapState中,獲取drawable時,系統會從緩存中取出這個bitmap然后構造drawable。而通過BitmapFactory.decodeResource()則每次都會重新解碼返回bitmap。所以其實我們可以通過context.getResources().getDrawable再從drawable里獲取bitmap,從而復用bitmap,然而這里也有一些坑,比如我們獲取到的這份bitmap,假如我們執行了recycle之類的操作,但是假如在其他地方再使用它是那么就會有”Canvas: trying to use a recycled bitmap android.graphics.Bitmap”異常。

3. 圖片壓縮

BitmapFactory 在解碼圖片時,可以帶一個Options,有一些比較有用的功能,比如:

inTargetDensity 表示要被畫出來時的目標像素密度

inSampleSize 這個值是一個int,當它小于1的時候,將會被當做1處理,如果大于1,那么就會按照比例(1 / inSampleSize)縮小bitmap的寬和高、降低分辨率,大于1時這個值將會被處置為2的倍數。例如,width=100,height=100,inSampleSize=2,那么就會將bitmap處理為,width=50,height=50,寬高降為1 / 2,像素數降為1 / 4

inJustDecodeBounds 字面意思就可以理解就是只解析圖片的邊界,有時如果只是為了獲取圖片的大小就可以用這個,而不必直接加載整張圖片。

inPreferredConfig 默認會使用ARGB_8888,在這個模式下一個像素點將會占用4個byte,而對一些沒有透明度要求或者圖片質量要求不高的圖片,可以使用RGB_565,一個像素只會占用2個byte,一下可以省下50%內存。

inPurgeableinInputShareable 這兩個需要一起使用,BitmapFactory.java的源碼里面有注釋,大致意思是表示在系統內存不足時是否可以回收這個bitmap,有點類似軟引用,但是實際在5.0以后這兩個屬性已經被忽略,因為系統認為回收后再解碼實際會反而可能導致性能問題

inBitmap 官方推薦使用的參數,表示重復利用圖片內存,減少內存分配,在4.4以前只有相同大小的圖片內存區域可以復用,4.4以后只要原有的圖片比將要解碼的圖片大既可以復用了。

4. 緩存池大小

現在很多圖片加載組件都不僅僅是使用軟引用或者弱引用了,實際上類似Glide 默認使用的事LruCache,因為軟引用 弱引用都比較難以控制,使用LruCache可以實現比較精細的控制,而默認緩存池設置太大了會導致浪費內存,設置小了又會導致圖片經常被回收,所以需要根據每個App的情況,以及設備的分辨率,內存計算出一個比較合理的初始值,可以參考Glide的做法。

5. 內存抖動

什么是內存抖動呢?Android里內存抖動是指內存頻繁地分配和回收,而頻繁的gc會導致卡頓,嚴重時還會導致OOM。

一個很經典的案例是string拼接創建大量小的對象(比如在一些頻繁調用的地方打字符串拼接的log的時候), 見Android優化之String篇[9]。

而內存抖動為什么會引起OOM呢?

主要原因還是有因為大量小的對象頻繁創建,導致內存碎片,從而當需要分配內存時,雖然總體上還是有剩余內存可分配,而由于這些內存不連續,導致無法分配,系統直接就返回OOM了。

比如我們坐地鐵的時候,假設你沒帶公交卡去坐地鐵,地鐵的售票機就只支持5元,10元,而哪怕你這個時候身上有1萬張1塊的都沒用(是不是覺得很反人類..)。當然你可以去兌換5元,10元,而在Android系統里就沒那么幸運了,系統會直接拒絕為你分配內存,并扔一個OOM給你(有人說Android系統并不會對Heap中空閑內存區域做碎片整理,待驗證)。

其他

常用數據結構優化,ArrayMap及SparseArray是android的系統API,是專門為移動設備而定制的。用于在一定情況下取代HashMap而達到節省內存的目的,具體性能見HashMap,ArrayMap,SparseArray源碼分析及性能對比[10],對于key為int的HashMap盡量使用SparceArray替代,大概可以省30%的內存,而對于其他類型,ArrayMap對內存的節省實際并不明顯,10%左右,但是數據量在1000以上時,查找速度可能會變慢。

枚舉,Android平臺上枚舉是比較爭議的,在較早的Android版本,使用枚舉會導致包過大,在個例子里面,使用枚舉甚至比直接使用int包的size大了10多倍 在stackoverflow上也有很多的討論, 大致意思是隨著虛擬機的優化,目前枚舉變量在Android平臺性能問題已經不大,而目前Android官方建議,使用枚舉變量還是需要謹慎,因為枚舉變量可能比直接用int多使用2倍的內存。

ListView復用,這個大家都知道,getView里盡量復用conertView,同時因為getView會頻繁調用,要避免頻繁地生成對象

謹慎使用多進程,現在很多App都不是單進程,為了保活,或者提高穩定性都會進行一些進程拆分,而實際上即使是空進程也會占用內存(1M左右),對于使用完的進程,服務都要及時進行回收。

盡量使用系統資源,系統組件,圖片甚至控件的id

減少view的層級,對于可以 延遲初始化的頁面,使用viewstub

數據相關:序列化數據使用protobuf可以比xml省30%內存,慎用shareprefercnce,因為對于同一個sp,會將整個xml文件載入內存,有時候為了讀一個配置,就會將幾百k的數據讀進內存,數據庫字段盡量精簡,只讀取所需字段。

dex優化,代碼優化,謹慎使用外部庫, 有人覺得代碼多少于內存沒有關系,實際會有那么點關系,現在稍微大一點的項目動輒就是百萬行代碼以上,多dex也是常態,不僅占用rom空間,實際上運行的時候需要加載dex也是會占用內存的(幾M),有時候為了使用一些庫里的某個功能函數就引入了整個龐大的庫,此時可以考慮抽取必要部分,開啟proguard優化代碼,使用Facebook redex使用優化dex(好像有不少坑)。

三 案例

JOOX是IBG一個核心產品,2014年發布以來已經成為5個國家和地區排名第一的音樂App。東南亞是JOOX的主要發行地區,實際上這些地區還是有很多的低端機型,對App的進行內存優化勢在必行。

上面介紹了Android系統內存分配和回收機制,同時也列舉了常見的內存問題,但是當我們接到一個內存優化的任務時,我們應該從何開始?下面是一次內存優化的分享。

1. 首先是解決大部分內存泄露。

不管目前App內存占用怎樣,理論上不需要的東西最好回收,避免浪費用戶內存,減少OOM。實際上自JOOX接入LeakCanary后,每個版本都會做內存泄露檢測,經過幾個版本的迭代,JOOX已經修復了幾十處內存泄露。

2. 通過MAT查看內存占用,優化占用內存較大的地方。

JOOX修復了一系列內存泄露后,內存占用還是居高不下,只能通過MAT查看到底是哪里占用了內存。關于MAT的使用,網上教程無數,簡單推薦兩篇MAT使用教程[11],MAT - Memory Analyzer Tool 使用進階[12]。

點擊Android Studio這里可以dump當前的內存快照,因為直接通過Android Sutdio dump出來的hprof文件與標準hprof文件有些差異,我們需要手動進行轉換,利用sdk目錄/platform-tools/hprof-conv.exe可以直接進行轉換,用法:hprof-conv 原文件.hprof 新文件.hprof。只需要輸入原文件名還有目標文件名就可以進行轉換,轉換完就可以直接用MAT打開。

下面就是JOOX打開App,手動進行多次gc的hprof文件。

這里我們看的是Dominator Tree(即內存里占用內存最多的對象列表)。

Shallo Heap:對象本身占用內存的大小,不包含其引用的對象內存。

Retained Heap: Retained heap值的計算方式是將retained set中的所有對象大小疊加。或者說,由于X被釋放,導致其它所有被釋放對象(包括被遞歸釋放的)所占的heap大小。

第一眼看去 居然有3個8M的對象,加起來就是24M啊 這到底是什么鬼?

我們通過List objects->with incoming references查看(這里with incoming references表示查看誰引用了這個對象,with outgoing references表示這個對象引用了誰)

通過這個方式我們看到這三張圖分別是閃屏,App主背景,App抽屜背景。

這里其實有兩個問題:

這幾張圖原圖實際都是1280x720,而在1080p手機上實測這幾張圖都縮放到了1920x1080

閃屏頁面,其實這張圖在閃屏顯示過后應該可以回收,但是因為歷史原因(和JOOX的退出機制有關),這張圖被常駐在后臺,導致無謂的內存占用。

優化方式:我們通過將這三張圖從xhdpi挪動到xxhdpi(當然這里需要看下圖片顯示效果有沒很大的影響),以及在閃屏顯示過后回收閃屏圖片。
優化結果:

從原來的8.29x3=24.87M 到 3.68x2=7.36M 優化了17M(有沒一種萬馬奔騰的感覺。。可能有時費大力氣優化很多代碼也優化不了幾百K,所以很多情況下內存優化時優化圖片還是比較立竿見影的)。

同樣方式我們發現對于一些默認圖,實際要求的顯示要求并不高(圖片相對簡單,同時大部分情況下圖片加載會成功),比如下面這張banner的背景圖:

優化前1.6M左右,優化后700K左右。

同時我們也發現了默認圖片一個其他問題,因為歷史原因,我們使用的圖片加載庫,設置默認圖片的接口是需要一個bitmap,導致我們原來幾乎每個adapter都用BitmapFactory decode了一個bitmap,對同一張默認圖片,不但沒有復用,還保存了多份,不僅會造成內存浪費,而且導致滑動偶爾會卡頓。這里我們也對默認圖片使用全局的bitmap緩存池,App全局只要使用同一張bitmap,都復用了同一份。

另外對于從MAT里看到的圖片,有時候因為看不到在項目里面對應的ID,會比較難確認到底是哪一張圖,這里stackoverflow上有一種方法,直接用原始數據通過GIM還原這張圖片。

這里其實也看到JOOX比較吃虧一個地方,JOOX不少地方都是使用比較復雜的圖片,同時有些地方還需要模糊,動畫這些都是比較耗內存的操作,Material Design出來后,很多App都遵循MD設計進行改版,通常默認背景,默認圖片一般都是純色,不僅App看起來比較明亮輕快,實際上也省了很多的內存,對此,JOOX后面對低端機型做了對應的優化。

3. 我們也對Bugly上的OOM進行了分析,發現其實有些OOM是可以避免的。

下面這個crash就是上面提到的在LsitView的adapter里不停創建bitmap,這個地方是我們的首頁banner位,理論上App一打開就會緩存這張默認背景圖片了,而實際在使用過一段時間后,才因為為了解碼這張背景圖而OOM, 改為用全局緩存解決。

下面這個就是傳說中的內存抖動

實際代碼如下,因為打Log而進行了字符串拼接,一旦這個函數被比較頻繁地調用,那么就很有可能會發生內存抖動。這里我們新版本已經改為使用stringbuilder進行優化。

還有一些比較奇怪的情況,這里是我們掃描歌曲文件頭的時候發生的,有些文件頭居然有幾百M大,導致一次申請了過大的內存,直接OOM,這里暫時也無法修復,直接catch住out of memory error。

4. 同時我們對一些邏輯代碼進行調整,比如我們的App主頁的第三個tab(Live tab)進行了數據延遲加載,和定時回收。

這里因為這個頁面除了有大圖還有輪播banner,實際強引用的圖片會有多張,如果這個時候切到其他頁面進行聽歌等行為,這個頁面一直在后臺緩存,實際是很浪費耗內存的,同時為優化體驗,我們又不能直接通過設置主頁的viewpager的緩存頁數,因為這樣經常都會回收,導致影響體驗,所以我們在頁面不可見后過一段時間,清理掉adapter數據(只是清空adapter里的數據,實際從網絡加載回來的數據還在,這里只是為了去掉界面對圖片的引用),當頁面再次顯示時再用已經加載的數據顯示,即減少了很多情況下圖片的引用,也不影響體驗。

5. 最后我們也遇到一個比較奇葩的問題,在我們的Bugly上報上有這樣一條上報

我們在stackoverflow上看到了相關的討論,大致意思是有些情況下比如息屏,或者一些省電模式下,頻繁地調System.gc()可能會因為內核狀態切換超時的異常。這個問題貌似沒有比較好的解決方法,只能是優化內存,盡量減少手動調用System.gc()

優化結果

我們通過啟動App后,切換到我的音樂界面,停留1分鐘,多次gc后,獲取App內存占用

優化前:

優化后:

多次試驗結果都差不多,這里只截取了其中一次,有28M的優化效果。
當然不同的場景內存占用不同,同時上面試驗結果是通過多次手動觸發gc穩定后的結果。對于使用其他第三方工具不手動gc的情況下,試驗結果可能會差異比較大。

對于上面提到的JOOX里各種圖片背景等問題,我們做了動態的優化,對不同的機型進行優化,對特別低端的機型設置為純色背景等方式,最終優化效果如下:

平均內存降低41M。

本次總結主要還是從圖片方面下手,還有一點邏輯優化,已經基本達到優化目標。

四 總結

上面寫了很多,我們可以簡單總結,目前Andorid內存優化還是比較重要一個話題,我們可以通過各種內存泄露檢測組件,MAT查看內存占用,Memory Monitor跟蹤整個App的內存變化情況, Heap Viewer查看當前內存快照, Allocation Tracker追蹤內存對象的來源,以及利用崩潰上報平臺從多個方面對App內存進行監控和優化。上面只是列舉了一些常見的情況,當然每個App功能,邏輯,架構也都不一樣,造成內存問題也是不盡相同,掌握好工具的使用,發現問題所在,才能對癥下藥。

參考鏈接

1.Android 操作系統的內存回收機制
https://www.ibm.com/developerworks/cn/opensource/os-cn-android-mmry-rcycl/

2.阿里巴巴的Android內存優化分享
http://www.infoq.com/cn/presentations/android-memory-optimization

3.Android進程的內存管理分析
http://blog.csdn.net/gemmem/article/details/8920039

4.android dalvik heap 淺析
http://blog.csdn.net/cqupt_chen/article/details/11068129

5.揭秘 ART 細節 —— Garbage collection
http://www.cnblogs.com/jinkeep/p/3818180.html

6.Android性能優化之常見的內存泄漏
http://blog.csdn.net/u010687392/article/details/49909477

7.Android App 內存泄露之Handler
http://blog.csdn.net/zhuanglonghai/article/details/38233069

8.GlideBitmapPool
https://github.com/amitshekhariitbhu/GlideBitmapPool

9.Android 性能優化之String篇
http://blog.csdn.net/vfush/article/details/53038437

10.HashMap,ArrayMap,SparseArray源碼分析及性能對比
http://www.jianshu.com/p/7b9a1b386265

11.MAT使用教程
http://blog.csdn.net/itomge/article/details/48719527

12.MAT - Memory Analyzer Tool 使用進階
http://www.lightskystreet.com/2015/09/01/mat_usage/

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70566.html

相關文章

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<