Android APP啟動(dòng)優(yōu)化之工具篇

Android項(xiàng)目時(shí)間越長(zhǎng),一不留神尺棋,就發(fā)現(xiàn)啟動(dòng)越來(lái)越慢封锉,越慢越慢......

這極大地影響了用戶(hù)體驗(yàn),所以提升啟動(dòng)速度非常重要了陡鹃。

所謂工欲善其事烘浦,必先利其器,本章咱們先來(lái)講講啟動(dòng)優(yōu)化需要的一些工具萍鲸!

走起.jpeg

一闷叉、 一些簡(jiǎn)單的啟動(dòng)耗時(shí)統(tǒng)計(jì)

1. ADB 命令查看啟動(dòng)耗時(shí)。

在命令行中輸入以下命令脊阴,它會(huì)自己?jiǎn)?dòng)APP握侧,并且打印啟動(dòng)耗時(shí)蚯瞧。

adb shell am start -S -W com.xiaoxiao.demo/.MainActivity

打印結(jié)果:

Stopping: com.xiaoxiao.demo
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.xiaoxiao.demo/.MainActivity }
Status: ok
Activity: com.xiaoxiao.demo/.MainActivity
ThisTime: 6309
TotalTime: 6309
WaitTime: 6371
Complete

ThisTime :該activity啟動(dòng)耗時(shí),單位ms品擎。
TotalTime :應(yīng)用自身啟動(dòng)耗時(shí)=ThisTime+應(yīng)用application等資源啟動(dòng)時(shí)間埋合。WaitTime :系統(tǒng)啟動(dòng)應(yīng)用耗時(shí)=TotalTime+系統(tǒng)資源啟動(dòng)時(shí)間。

2. logcat查看頁(yè)面啟動(dòng)時(shí)間

在logcat中過(guò)濾包含 Displayed 的打印結(jié)果萄传。這個(gè)值代表從啟動(dòng)進(jìn)程到在屏幕上完成對(duì)應(yīng) Activity 的繪制所用的時(shí)間甚颂。

ActivityManager: Displayed com.babytree.apps.pregnancy/.MainActivity: +6s349ms
ActivityManager: Displayed com.babytree.apps.pregnancy/.MainActivity: +5s543ms

3. 增加啟動(dòng)耗時(shí)日志

想知道比較細(xì)節(jié)一些的,比如Application中哪些任務(wù)比較耗時(shí)秀菱,總耗時(shí)等振诬,就可以自己打印一些日志了。在方法調(diào)用前后分別加上日志衍菱,記錄時(shí)間即可赶么。

/**
 * 方便查看方法調(diào)用時(shí)長(zhǎng)。
 * 使用 方法前調(diào)用 begin("FACTION_TAG_NAME")  方法后調(diào)用 end("FACTION_TAG_NAME")
 */
object TimeLog {

    private var TAG = "TimeLog"
    private var isOpenLog = false
    private var times: ConcurrentHashMap<String, Long>? = null
    private var current = System.currentTimeMillis()
    private var current1 = current

    @JvmStatic
    fun openLog(open: Boolean) {
        isOpenLog = open
        if (isOpenLog) {
            times = ConcurrentHashMap(20)
        }
    }

    @JvmStatic
    fun setLogTag(tag: String) {
        TAG = tag
    }

    /**
     * 與 end 或者 endI 成對(duì)出現(xiàn)
     */
    @JvmStatic
    fun begin(name: String?) {
        if (times != null && !name.isNullOrEmpty()) {
            times!![name] = System.currentTimeMillis()
        }
    }

    /**
     * 與begin成對(duì)出現(xiàn)
     */
    @JvmStatic
    fun end(name: String?) {
        if (times != null && !name.isNullOrEmpty() && times?.get(name) != null) {
            val l = System.currentTimeMillis() - times?.get(name)!!
            Log.d(TAG, "$name:$l")
        }
    }

    /**
     * 與begin成對(duì)出現(xiàn)
     */
    @JvmStatic
    fun endI(name: String?) {
        if (!times.isNullOrEmpty() && !name.isNullOrEmpty() && times?.get(name) != null) {
            val l = System.currentTimeMillis() - times?.get(name)!!
            Log.i(TAG, "$name:$l")
        }
    }

    /**
     * 打印第一次初始化此類(lèi)時(shí)間和現(xiàn)在時(shí)間的時(shí)間差脊串。
     * 打印與上一個(gè)調(diào)用times()方法的時(shí)間差辫呻。
     * 打印當(dāng)前時(shí)間的時(shí)間戳。
     */
    @JvmStatic
    fun times(tag: String) {
        val millis = System.currentTimeMillis()
        val l = millis - current
        val l1 = millis - current1
        Log.d(TAG, "$tag:totalTime:$l,thisTime:$l1,current:$millis")
        current1 = millis
    }
}

比如琼锋,想要知道直播初始化需要的時(shí)間:

TimeLog.begin("LiveCenter")
LiveCenter.init(this, 1, sdkDebug)
TimeLog.end("LiveCenter")

這種方式比較簡(jiǎn)單放闺,方便我們自己查看各任務(wù)的啟動(dòng)時(shí)間,快速定位哪些任務(wù)耗時(shí)斩例,后續(xù)再針對(duì)任務(wù)進(jìn)行分析優(yōu)化雄人。還有从橘,這種直接打印也只是自己看看念赶,知道個(gè)大概,數(shù)據(jù)不準(zhǔn)確恰力。如果想知道準(zhǔn)確的數(shù)據(jù)叉谜,則需要大批量的數(shù)據(jù)信息了,這個(gè)現(xiàn)在有些公司提供的有服務(wù)踩萎,比如火山停局,也是通過(guò)這種類(lèi)似的方式添加日志,不過(guò)他們已經(jīng)做好了數(shù)據(jù)統(tǒng)計(jì)香府,所以比較方便查看大數(shù)據(jù)量的統(tǒng)計(jì)結(jié)果董栽,這樣就能看到各個(gè)任務(wù)在各種機(jī)型和所有手機(jī)上的平均啟動(dòng)時(shí)長(zhǎng)等信息了。

來(lái)企孩,筒子們锭碳,看看美圖,養(yǎng)養(yǎng)眼勿璃,咱們休息1秒鐘~

薇爾莉特1.png

二擒抛、啟動(dòng)時(shí)長(zhǎng)分析監(jiān)測(cè)工具

1. Systrace工具

Systrace工具使用文檔

瀏覽Systrace報(bào)告

最簡(jiǎn)單的生成 HTML 報(bào)告的命令:

python2 systrace.py

在systrace.py文件目錄下(在platform-tools文件夾里面)推汽,輸入左邊命令。比如我的systrace.py在以下目錄中

file:///Users/xiaoxiao/Library/Android/sdk/platform-tools/systrace

手機(jī)連上電腦歧沪,按下enter命令后就開(kāi)始跟蹤歹撒,啟動(dòng)想要追蹤的APP,啟動(dòng)結(jié)束后诊胞,在命令行中再次按 Enter 鍵結(jié)束跟蹤暖夭。systrace 會(huì)將報(bào)告保存到 systrace.py 所在的目錄中,并將其命名為 trace.html撵孤。

至于systrace的其他命令鳞尔,大家可以上網(wǎng)查啊,一大堆啊早直,我就不詳細(xì)描述了寥假。

python2 systrace.py -a com.xiaoxiao.demo -t 9 -o trace_app_9s.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res memory

trace.html可以直接用瀏覽器打開(kāi) ( 基本操作:w鍵放大,s鍵縮小霞扬,a左移糕韧,d右移 ),這時(shí)候就可以比較直觀的查看啟動(dòng)耗時(shí)了喻圃。

trace.html

對(duì)于比較耗時(shí)的任務(wù)萤彩,可以查看它里面具體的耗時(shí)的方法,從而分析耗時(shí)的原因斧拍,再看能不能減少耗時(shí)雀扶。來(lái),咱們舉個(gè)栗子八列凇愚墓!

FileProvider

仔細(xì)看FileProvider啟動(dòng)耗時(shí),我們發(fā)現(xiàn)它的啟動(dòng)耗時(shí)主要就是getPathStrategy() 方法耗時(shí)昂勉。如果要解決FileProvider耗時(shí)問(wèn)題浪册,就得從這個(gè)方法出發(fā),往下找它的耗時(shí)原因岗照,再找解決方案村象。

這里我直接說(shuō)結(jié)果啊,分析完源碼的結(jié)果就是:getPathStrategy 是在啟動(dòng)階段完全沒(méi)有必要的攒至,可以在 FileProvider的query厚者、getType、openFile 等接口被調(diào)用到的時(shí)候再去執(zhí)行 getPathStrategy 邏輯迫吐。

解決方案:FileProvider 是 androidx 中的代碼库菲,無(wú)法直接修改,但是它會(huì)參與代碼編譯渠抹,所以可以在編譯階段通過(guò)修改字節(jié)碼的方式去修改它的實(shí)現(xiàn)蝙昙。

通過(guò)字節(jié)碼修改FileProvider.attachInfo 內(nèi) getPathStrategy耗時(shí)方法調(diào)用時(shí)機(jī)闪萄,延遲給mStrategy變量賦值的減少Application.attachBaseContext結(jié)束到onCreate開(kāi)始之間的耗時(shí),然后在 FileProvider的query奇颠、getType败去、openFile 等接口被調(diào)用到的時(shí)候再去執(zhí)行 getPathStrategy 并賦值的邏輯。

FileProvider優(yōu)化后

上圖是優(yōu)化后的圖烈拒,可以看到在啟動(dòng)的時(shí)候getPathStrategy沒(méi)有調(diào)用圆裕,不過(guò)圖中沒(méi)有體現(xiàn)出來(lái)啟動(dòng)時(shí)長(zhǎng)差異,據(jù)不負(fù)責(zé)統(tǒng)計(jì)荆几,實(shí)際上啟動(dòng)時(shí)長(zhǎng)短了約30ms(這個(gè)我們沒(méi)有走大數(shù)據(jù)量的統(tǒng)計(jì)吓妆,只是根據(jù)個(gè)別手機(jī)得出來(lái)的一個(gè)數(shù)值)。

篇幅原因吨铸,這里涉及到的修改字節(jié)碼和具體修改這里不細(xì)說(shuō)了行拢,回頭會(huì)單獨(dú)寫(xiě)一篇文章說(shuō)說(shuō)FileProvider的啟動(dòng)耗時(shí)優(yōu)化。感興趣的朋友也可以在網(wǎng)上搜一下這一塊的文章诞吱。

2. 字節(jié)開(kāi)源性能分析工具-Btrace

github-btrace

btrace(又名 RheaTrace) 是一個(gè)基于 Systrace 實(shí)現(xiàn)的高性能 Android trace 工具舟奠,它支持在 App 編譯期間自動(dòng)注入自定義事件,并使用 bhook額外提供 IO 等 native 事件房维。

特征:

  • 支持自動(dòng)注入自定義事件沼瘫,在編譯 Apk 期間為 App 方法自動(dòng)注入Trace#beginSection(String) 和 Trace#endSection()。

  • 提供額外 IO 等 native 事件咙俩,方便定位耗時(shí)原因耿戚。

  • 支持僅采集主線程 trace 事件。

  • 使用便捷阿趁,穩(wěn)定性高膜蛔,性能優(yōu)于 Systrace。

接入和使用:

  1. 根目錄下 build.gradle 文件中增加 rhea-gradle-plugin 作為依賴(lài)歌焦。
dependencies {
        classpath 'com.bytedance.btrace:rhea-gradle-plugin:1.0.1'
}
  1. app/build.gradle 文件中應(yīng)用如下所示插件和依賴(lài)飞几。
dependencies {
    //rheatrace core lib
    implementation "com.bytedance.btrace:rhea-core:1.0.1"
}
...
rheaTrace {

   compilation {
      //為減少 APK 體積, 你可以為 App 中需要跟蹤的方法設(shè)置 id 以此來(lái)跟蹤此自定義事件, 默認(rèn)值 false。
      traceWithMethodID = false
      //該文件配置決定哪些方法您不希望跟蹤, 默認(rèn)值 null独撇。
      traceFilterFilePath = "${project.rootDir}/rhea-trace/traceFilter.txt"
      //用特指定方法 id 來(lái)設(shè)置自定義事件名稱(chēng), 默認(rèn)值 null。
      applyMethodMappingFilePath = "${project.rootDir}/rhea-trace/keep-method-id.txt"
  }

   runtime {
      //僅在主線程抓取跟蹤事件, 默認(rèn)值 false躁锁。
      mainThreadOnly true
      //在 App 啟動(dòng)之初開(kāi)始抓取跟蹤事件, 默認(rèn)值 true纷铣。
      startWhenAppLaunch true
      //指定內(nèi)存存儲(chǔ) atrace 數(shù)據(jù) ring buffer 的大小。
      atraceBufferSize "500000"
   }
}
...
apply plugin: 'com.bytedance.rhea-trace' 
  1. 檢測(cè)電腦 python 版本战转,由于 Systrace 的關(guān)系 RheaTrace 僅支持 python2.7 版本搜立,請(qǐng)將 systrace配置在環(huán)境變量中。

例如槐秧,我是Mac啄踊,環(huán)境變量配置在 ~/.bash_profile 文件中忧设。

export PATH=${PATH}:/Users/${user_name}/Library/Android/sdk/platform-tools/systrace

以上配置完成后,打開(kāi)終端颠通,先進(jìn)入到rheatrace.py 所在位置址晕,你下載的btrace代碼存放位置。

如:/Users/xiaoxiao/code/btrace/scripts/python/rheatrace/

btrace的命令和systrace是一樣的昂~~

python2 rheatrace.py -a com.xiaoxiao.demo -t 9 -o rheatrace_9s.html

已知問(wèn)題(官方說(shuō)的啊~):

  • 僅支持 python2.7顿锰,請(qǐng)注意檢查 python 環(huán)境谨垃。

  • 暫不支持 Windows。

  • 僅支持采集主進(jìn)程的 trace 事件硼控。

  • 需要外置存儲(chǔ)的讀寫(xiě)權(quán)限刘陶,因此您需要手動(dòng)賦予該權(quán)限。

  • 如果您無(wú)法直接打開(kāi)輸出產(chǎn)物 systrace.html 牢撼,請(qǐng)用 perfetto 加載匙隔。ps:我就是這樣的,直接用瀏覽器打不開(kāi)熏版,只能用perfetto打開(kāi)牡直。perfetto也很好用,直接將文件拖進(jìn)去就行了纳决,而且它支持手勢(shì)放大縮小碰逸,不用像在瀏覽器那樣用按鍵控制。

perfetto地址

perfetto打開(kāi)的大概長(zhǎng)這樣阔加。

perfetto-btrace.jpg

筒子們饵史,咱們?cè)傩菹⒁幻腌妬?lái)~


薇爾莉特

app啟動(dòng)優(yōu)化之工具篇

三、 profiler使用和分析

Traceview是android平臺(tái)配備一個(gè)很好的性能分析的工具胜榔。它可以通過(guò)圖形化的方式讓我們了解我們要跟蹤的程序的性能胳喷,并且能具體到每個(gè)方法的執(zhí)行時(shí)間。但是目前Traceview 已棄用夭织。

如果使用 Android Studio3.2 或更高版本吭露,則應(yīng)改為使用 CPU Profiler.谷歌官網(wǎng)profiler工具使用文檔:

google-profile

采樣方式有:

    1. 直接采樣,運(yùn)行APP的時(shí)候尊惰,打開(kāi)profile讲竿,點(diǎn)擊開(kāi)始采樣。
    1. 代碼采樣弄屡,通過(guò)代碼進(jìn)行精準(zhǔn)采樣题禀。
    1. 捕獲設(shè)備上的系統(tǒng)跟蹤記錄

1. 直接采樣

APP --》 Edit configurations --》 Profiling

--》 勾選 Start this recording on startup

--》 選擇Java/Kotlin Mothed Trace(全采樣) 或者 Java/Kotlin Mothed Sample(部分采樣,建議選擇這個(gè)膀捷,另一個(gè)太卡了)

profiler.png

采樣示例圖如下:

application火焰圖.png
activity火焰圖.png

分析數(shù)據(jù)主要看主線程的迈嘹,就是上面的main模塊。

2.代碼采樣

在需要采樣的類(lèi)里面,通過(guò)以下代碼進(jìn)行采樣

val fileName = BAFFileUtil.getSDCardPath() + "test_trace_app_all" 
Debug.startMethodTracing(fileName,800000000) 

//startMethodTracing(String tracePath, int bufferSize) {
//tracePath:trace文件存放位置秀仲;
//bufferSize: trace文件最大支持的大小如果文件不設(shè)置大小融痛,最大默認(rèn)為8M,一般都不夠神僵。
  
//間隔采樣
//intervalUs 間隔時(shí)間雁刷,微秒
//startMethodTracingSampling(String tracePath, int bufferSize,int intervalUs)
Debug.startMethodTracingSampling(fileName,800000000,10)
  
//結(jié)束采樣
Debug.stopMethodTracing()

注意:

startMethodTracing 和 stopMethodTracing 需要成對(duì)出現(xiàn)
startMethodTracingSampling 和 stopMethodTracing 需要成對(duì)出現(xiàn)

3. 設(shè)備直接采樣

捕獲設(shè)備上的系統(tǒng)跟蹤記錄

優(yōu)點(diǎn):方便 缺點(diǎn):不夠詳細(xì),很多東西沒(méi)采樣到


手機(jī)采樣1.jpg
手機(jī)采樣2.png
手機(jī)采樣3.png

首次使用設(shè)置:

如果您是首次在測(cè)試設(shè)備上使用 System Tracing挑豌,或在設(shè)備的快捷設(shè)置面板中看不到 System Tracing 圖塊(手機(jī)采樣3.png)安券,請(qǐng)完成以下設(shè)置步驟:

  • 啟用開(kāi)發(fā)者選項(xiàng)(如果尚未啟用此選項(xiàng))。

  • 打開(kāi)開(kāi)發(fā)者選項(xiàng)設(shè)置屏幕氓英。

  • 在調(diào)試部分中侯勉,選擇 System Tracing。此時(shí)會(huì)打開(kāi) System Tracing 應(yīng)用铝阐,其中顯示了應(yīng)用菜單址貌。

  • 在應(yīng)用菜單中,啟用顯示“快捷設(shè)置”圖塊徘键,如圖 2 所示练对。系統(tǒng)會(huì)將 System Tracing 圖塊添加到快捷設(shè)置面板中。這樣就可以采樣了~

四吹害、啟動(dòng)優(yōu)化器

這個(gè)不算是工具啊螟凭,是一種啟動(dòng)框架,一般是通過(guò)有向無(wú)環(huán)圖構(gòu)建的異步啟動(dòng)框架它呀。感興趣的同學(xué)可以自己了解一下~
Alpha啟動(dòng)框架
android-startup
AppStartFaster

以上螺男,就說(shuō)到這里!

如果能幫到你是我的榮幸纵穿,如果有錯(cuò)誤歡迎指正下隧,歡迎留言~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谓媒,隨后出現(xiàn)的幾起案子淆院,更是在濱河造成了極大的恐慌,老刑警劉巖句惯,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件土辩,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡宗弯,警方通過(guò)查閱死者的電腦和手機(jī)脯燃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蒙保,“玉大人,你說(shuō)我怎么就攤上這事欲主〉瞬蓿” “怎么了逝嚎?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)详恼。 經(jīng)常有香客問(wèn)我补君,道長(zhǎng),這世上最難降的妖魔是什么昧互? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任挽铁,我火速辦了婚禮,結(jié)果婚禮上敞掘,老公的妹妹穿的比我還像新娘叽掘。我一直安慰自己,他們只是感情好玖雁,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布更扁。 她就那樣靜靜地躺著,像睡著了一般赫冬。 火紅的嫁衣襯著肌膚如雪浓镜。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天劲厌,我揣著相機(jī)與錄音膛薛,去河邊找鬼。 笑死补鼻,一個(gè)胖子當(dāng)著我的面吹牛哄啄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辽幌,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼增淹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了乌企?” 一聲冷哼從身側(cè)響起虑润,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎加酵,沒(méi)想到半個(gè)月后磷斧,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體儿子,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了虏杰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡右莱,死狀恐怖救拉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤捌归,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布肛响,位于F島的核電站,受9級(jí)特大地震影響惜索,放射性物質(zhì)發(fā)生泄漏特笋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一巾兆、第九天 我趴在偏房一處隱蔽的房頂上張望猎物。 院中可真熱鬧,春花似錦角塑、人聲如沸蔫磨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)质帅。三九已至,卻和暖如春留攒,著一層夾襖步出監(jiān)牢的瞬間煤惩,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工炼邀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留魄揉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓拭宁,卻偏偏與公主長(zhǎng)得像洛退,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子杰标,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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