Android性能優(yōu)化整理總結(jié)

性能優(yōu)化的目的

在不斷地迭代開發(fā)過程中,我們的應(yīng)用功能會越來越復(fù)雜,代碼量也會不斷增加设拟。再加上偶爾的重構(gòu)、人員的變更等等原因久脯,我們曾經(jīng)那個(gè)如絲順滑的項(xiàng)目也會漸漸變得卡頓蒜绽。
那么這個(gè)時(shí)候,就不得不提性能優(yōu)化這個(gè)話題了桶现。正好這段時(shí)間有空躲雅,就整理了一下常見的性能優(yōu)化的幾個(gè)方面以及各個(gè)方面的注意事項(xiàng)。一來是給自己腦中的知識做個(gè)梳理骡和,加深下記憶相赁,二來也能給一些萌新提供點(diǎn)思路。

內(nèi)存優(yōu)化

內(nèi)存優(yōu)化慰于,可以說是性能優(yōu)化中最重要的一部分內(nèi)容了钮科。如果應(yīng)用占用內(nèi)存過大,輕則應(yīng)用卡頓降低用戶體驗(yàn)婆赠,重則內(nèi)存溢出(OOM)绵脯,程序崩潰。所以內(nèi)存優(yōu)化很重要,接下來我們從一下幾個(gè)方面來進(jìn)行講解

android的內(nèi)存管理

  • 垃圾回收

    android的垃圾回收和java 的垃圾回收一直蛆挫,一旦確定程序不再使用內(nèi)存赃承,便將其釋放,而無需人為干預(yù)悴侵。

    垃圾回收有兩個(gè)步驟:在程序中產(chǎn)兆將來無法訪問的數(shù)據(jù)對象瞧剖;將那些對象的資源釋放并回收。

  • 內(nèi)存管理

    android作為一個(gè)多任務(wù)的操作系統(tǒng)可免,為了維持系統(tǒng)功能正常抓于,會對每個(gè)應(yīng)用程序的內(nèi)存大小做出硬性限制。具體的數(shù)值和機(jī)型以及內(nèi)存有關(guān)浇借。如果應(yīng)用內(nèi)存打到限制后還要申請內(nèi)存捉撮,就會引發(fā)OutOfMemoryError

    如果在開發(fā)中有獲取剩余內(nèi)存的需求妇垢,可以調(diào)用getMemoryClass()方法呕缭。用來在內(nèi)存快滿時(shí)及時(shí)回收一些不必要的東西。

android內(nèi)存監(jiān)測

  • LeakCanary 詳情

    一個(gè)可以檢測程序在運(yùn)行過程中發(fā)生的內(nèi)存泄漏問題修己,通過簡單的代碼配置恢总,可以方便的找出我們應(yīng)用中的內(nèi)存問題

  • Memory Monitor

    android studio自帶的實(shí)時(shí)內(nèi)存分析工具,我們可以通過實(shí)時(shí)的內(nèi)存睬愤、CPU等的波動(dòng)來分析問題片仿,如果某個(gè)頁面反復(fù)進(jìn)入后內(nèi)存持續(xù)增長,我們就要注意了尤辱。

    [圖片上傳失敗...(image-211493-1570784432908)]

  • Heap Viewer 詳情

    也是android studio中可以直接使用的內(nèi)存分析工具砂豌,需要android系統(tǒng)在5.0以上并保持開發(fā)者選項(xiàng)可用。具體使用情況請點(diǎn)擊詳情光督。

  • Allocation Tracker 詳情

    可以追蹤內(nèi)存分配信息阳距,按順序排列,這樣我們就可以清晰看出某一個(gè)操作的內(nèi)存是如何一步步分配出來的结借,從而進(jìn)一步找出發(fā)生問題的代碼筐摘。

  • 更多性能測試工具看這里

內(nèi)存優(yōu)化方案

  • 界面不可見時(shí)及時(shí)回收部分內(nèi)存

    當(dāng)用戶打開了另外一個(gè)程序,我們的程序界面已經(jīng)不再可見的時(shí)候船老,我們應(yīng)當(dāng)將所有和界面相關(guān)的資源進(jìn)行釋放咖熟。在這種場景下釋放資源可以讓系統(tǒng)緩存后臺進(jìn)程的能力顯著增加,因此也會讓用戶體驗(yàn)變得更好柳畔。

    檢測界面是否可見我們可以重寫如下方法:

    @Override  
    public void onTrimMemory(int level) {  
        super.onTrimMemory(level);  
        switch (level) {  
        case TRIM_MEMORY_UI_HIDDEN:  
            // 進(jìn)行資源釋放操作  
            break;  
        }  
    }
    

    onTrimMemory方法只有當(dāng)一個(gè)Activity完全不可見時(shí)候才會調(diào)用馍管,這和onStop()方法還是有很大區(qū)別的,因?yàn)?code>onStop()方法只是當(dāng)一個(gè)Activity完全不可見的時(shí)候就會調(diào)用薪韩,比如說用戶打開了我們程序中的另一個(gè)Activity确沸。

    因此捌锭,我們可以在onStop()方法中去釋放一些Activity相關(guān)的資源,比如說取消網(wǎng)絡(luò)連接或者注銷廣播接收器等罗捎,但是像UI相關(guān)的資源應(yīng)該一直要等到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)這個(gè)回調(diào)之后才去釋放观谦,這樣可以保證如果用戶只是從我們程序的一個(gè)Activity回到了另外一個(gè)Activity,界面相關(guān)的資源都不需要重新加載宛逗,從而提升響應(yīng)速度坎匿。

  • 使用Handler時(shí)盡量弱引用

    我們經(jīng)常會在handler中進(jìn)行一些延時(shí)任務(wù)盾剩,這些延時(shí)任務(wù)會導(dǎo)致Activity被引用雷激,從而發(fā)生內(nèi)存泄漏,為了避免這類事情發(fā)生告私,我們可以對handler使用弱引用屎暇。

    public class MainActivity extends AppCompatActivity {
    
        public static final String TAG = "carson:";
        private Handler showhandler;
    
        // 主線程創(chuàng)建時(shí)便自動(dòng)創(chuàng)建Looper & 對應(yīng)的MessageQueue
        // 之后執(zhí)行Loop()進(jìn)入消息循環(huán)
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            //1. 實(shí)例化自定義的Handler類對象->>分析1
            //注:
                // a. 此處并無指定Looper,故自動(dòng)綁定當(dāng)前線程(主線程)的Looper驻粟、MessageQueue根悼;
                // b. 定義時(shí)需傳入持有的Activity實(shí)例(弱引用)
            showhandler = new FHandler(this);
    
            // 2. 啟動(dòng)子線程1
            new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // a. 定義要發(fā)送的消息
                    Message msg = Message.obtain();
                    msg.what = 1;// 消息標(biāo)識
                    msg.obj = "AA";// 消息存放
                    // b. 傳入主線程的Handler & 向其MessageQueue發(fā)送消息
                    showhandler.sendMessage(msg);
                }
            }.start();
    
            // 3. 啟動(dòng)子線程2
            new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // a. 定義要發(fā)送的消息
                    Message msg = Message.obtain();
                    msg.what = 2;// 消息標(biāo)識
                    msg.obj = "BB";// 消息存放
                    // b. 傳入主線程的Handler & 向其MessageQueue發(fā)送消息
                    showhandler.sendMessage(msg);
                }
            }.start();
    
        }
    
        // 分析1:自定義Handler子類
        // 設(shè)置為:靜態(tài)內(nèi)部類
        private static class FHandler extends Handler{
    
            // 定義 弱引用實(shí)例
            private WeakReference<Activity> reference;
    
            // 在構(gòu)造方法中傳入需持有的Activity實(shí)例
            public FHandler(Activity activity) {
                // 使用WeakReference弱引用持有Activity實(shí)例
                reference = new WeakReference<Activity>(activity); }
    
            // 通過復(fù)寫handlerMessage() 從而確定更新UI的操作
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 1:
                        Log.d(TAG, "收到線程1的消息");
                        break;
                    case 2:
                        Log.d(TAG, " 收到線程2的消息");
                        break;
    
    
                }
            }
        }
    }
    

    這樣一來,handler中發(fā)送延時(shí)消息便不會發(fā)生內(nèi)存泄漏了蜀撑。

    當(dāng)然避免handler內(nèi)存泄漏還可以采取當(dāng)Activity結(jié)束使用時(shí)候挤巡,清空消息隊(duì)列的操作,如下:

    @Override
    protected void onDestroy() {
      super.onDestroy();
      mHandler.removeCallbacksAndMessages(null);
      // 外部類Activity生命周期結(jié)束時(shí)酷麦,同時(shí)清空消息隊(duì)列 & 結(jié)束Handler生命周期
    }
    
  • 加載圖片的注意事項(xiàng) 詳情

    • 不在小控件上顯示大圖
    • 列表類圖片矿卑,僅加載當(dāng)前頁面可見的圖片
    • 有顯示原圖需求時(shí),要判斷可用內(nèi)存沃饶,內(nèi)存不夠時(shí)要壓縮圖片
  • 避免內(nèi)存抖動(dòng) 詳情

    • 盡量避免在循環(huán)體內(nèi)創(chuàng)建對象母廷,應(yīng)該把對象創(chuàng)建移到循環(huán)體外。

    • 注意自定義View的onDraw()方法會被頻繁調(diào)用糊肤,所以在這里面不應(yīng)該頻繁的創(chuàng)建對象琴昆。

    • 當(dāng)需要大量使用Bitmap的時(shí)候,試著把它們緩存在數(shù)組中實(shí)現(xiàn)復(fù)用馆揉。

    • 對于能夠復(fù)用的對象业舍,同理可以使用對象池將它們緩存起來。

布局優(yōu)化 詳情

避免過度繪制

  • 什么是過度繪制

    過度繪制就是在同一個(gè)位置升酣,有多次的顏色繪制過程勤讽。常見的情況就是在同一個(gè)位置堆疊了許多控件,這會造成一些性能問題拗踢,嚴(yán)重的情況會造成卡頓脚牍。

  • 如何檢測過度繪制

    開發(fā)者選項(xiàng)->調(diào)試GPU過度繪制->顯示過度繪制區(qū)域

  • 過度繪制優(yōu)化

    • 移除控件中不需要的背景
    • 將layout層級扁平化
    • 減少透明度的使用
    • 自定義View中減少重復(fù)繪制區(qū)域

布局優(yōu)化技巧

  • 簡單布局優(yōu)先使用LinearLayoutFragmentLayout
  • 復(fù)雜布局優(yōu)先使用constrainLayout
  • 使用include標(biāo)簽提高復(fù)用性
  • 使用ViewStub標(biāo)簽延遲加載
  • onDraw()中不要?jiǎng)?chuàng)建新的局部變量以及不要做耗時(shí)操作

網(wǎng)絡(luò)優(yōu)化 詳情

為什么要網(wǎng)絡(luò)優(yōu)化

  • 過多的無用網(wǎng)絡(luò)請求,會消耗用戶的網(wǎng)絡(luò)流量巢墅。流量消耗過大會流失用戶
  • 頻繁的網(wǎng)絡(luò)操作會導(dǎo)致設(shè)備用電量提升
  • 網(wǎng)絡(luò)彈框的頻繁出現(xiàn)會降低用戶體驗(yàn)
  • 應(yīng)用更新诸狭、大文件下載等場景券膀,更優(yōu)的網(wǎng)絡(luò)傳輸速度可提升用戶體驗(yàn)

網(wǎng)絡(luò)優(yōu)化的方式

  • 使用GZip壓縮,數(shù)據(jù)壓縮后可以減少流量的消耗驯遇,減少傳輸?shù)臅r(shí)間
  • 使用IP直連芹彬,DNS域名解析是一個(gè)較為耗時(shí)操作,可以直連IP減少解析時(shí)間
  • 圖片加載
    • 使用WebP格式可以大幅節(jié)省流量
    • 圖片按需加載叉庐,列表中圖片只加載縮略圖
    • 大圖上傳時(shí)舒帮,采用分片傳輸,失敗只傳對應(yīng)片段
    • 用戶體驗(yàn)影響不大時(shí)陡叠,手機(jī)原圖壓縮后再傳輸
  • 減少接口數(shù)量玩郊,同一個(gè)頁面盡量只使用一個(gè)接口办斑,數(shù)據(jù)可以放到后臺去拼湊
  • 利用緩存赃绊,對數(shù)據(jù)設(shè)定有效期,有效期內(nèi)數(shù)據(jù)不重復(fù)請求
  • 檢測網(wǎng)絡(luò)狀態(tài)铃将,不同網(wǎng)絡(luò)轉(zhuǎn)態(tài)執(zhí)行不同策略兴溜,例如移動(dòng)網(wǎng)絡(luò)不加載圖片侦厚,2G網(wǎng)絡(luò)只加載標(biāo)題等。
  • 文件上傳拙徽、下載采用斷點(diǎn)續(xù)傳刨沦,不浪費(fèi)已傳輸完成部分流量
  • 利用抓包工具模擬多種情況,在實(shí)踐中調(diào)整不斷優(yōu)化用戶體驗(yàn)

啟動(dòng)優(yōu)化 詳情

閃屏頁優(yōu)化

主流APP是在應(yīng)用啟動(dòng)時(shí)候會加載一個(gè)默認(rèn)的主題膘怕,用來去掉應(yīng)用啟動(dòng)時(shí)候的黑/白屏的情況

<style name="AppThemeWelcome" parent="Theme.AppCompat.NoActionBar">
    ...
    <item name="android:windowBackground">@drawable/logo</item>  <!-- 默認(rèn)背景-->
</style>

應(yīng)用主題到ApplicationActivity

<activity android:name=".ui.activity.DemoSplashActivity"
  android:configChanges="orientation|screenSize|keyboardHidden"
  android:theme="@style/AppThemeWelcome"
  android:screenOrientation="portrait">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>

其實(shí)就是個(gè)障眼法而已想诅,提前讓你看到了假的界面。也算是一種不錯(cuò)的方法淳蔼,但是治標(biāo)不治本侧蘸。

第三方庫懶加載

在開發(fā)中會用到很多的三方庫,如友盟鹉梨、百度讳癌、bugly、圖片庫存皂、網(wǎng)絡(luò)庫等晌坤。

這些都是必須的,不能去掉旦袋,那么辦法就是異步加載了骤菠,可以有以下幾種思路

  • 像友盟,bugly這樣的業(yè)務(wù)非必要的可以的異步加載疤孕。

  • 比如地圖商乎,推送等,非第一時(shí)間需要的可以在主線程做延時(shí)啟動(dòng)祭阀。當(dāng)程序已經(jīng)啟動(dòng)起來之后鹉戚,在進(jìn)行初始化鲜戒。

  • 對于圖片,網(wǎng)絡(luò)請求框架必須在主線程里初始化了抹凳。

  • 我們一般會有閃屏頁面遏餐,也可以把延時(shí)啟動(dòng)的地圖、推送的啟動(dòng)放到這個(gè)頁面

按照以上方式處理后赢底,還可以進(jìn)一步降低應(yīng)用啟動(dòng)時(shí)間失都。

WebView啟動(dòng)優(yōu)化

  • WebView第一次創(chuàng)建比較耗時(shí),可以預(yù)先創(chuàng)建WebView幸冻,提前將其內(nèi)核初始化粹庞。
  • 使用WebView緩存池,用到WebView的地方都從緩存池取嘁扼,緩存池中沒有緩存再創(chuàng)建信粮,注意內(nèi)存泄漏問題黔攒。
  • 本地預(yù)置html和css趁啸,WebView創(chuàng)建的時(shí)候先預(yù)加載本地html,之后通過js腳本填充內(nèi)容部分督惰。

數(shù)據(jù)項(xiàng)預(yù)加載

主頁數(shù)據(jù)變化不大時(shí)候不傅,可以再第一次啟動(dòng)后,緩存主頁數(shù)據(jù)到本地赏胚,下次啟動(dòng)先讀取本地?cái)?shù)據(jù)访娶,頁面完全顯示后再去請求新數(shù)據(jù)進(jìn)行增量更新。

安裝包體積優(yōu)化

體積優(yōu)化的必要性

安裝包體積是用戶搜索應(yīng)用后能第一眼看到的數(shù)據(jù)觉阅,雖然現(xiàn)在的應(yīng)用體積越來越大崖疤,但小體積的App依舊是很多存儲空間緊張用戶的痛點(diǎn)。所以減少安裝包體積是性能優(yōu)化方面必不可少的一步典勇。

減少應(yīng)用體積的N種辦法

  • 使用lint工具刪除無用的資源

  • 簡單的切圖盡量替換為shape類型的xml文件

  • 形狀一致的圖片只使用一個(gè)切圖劫哼,比如方向不同的箭頭、圖像相同著色不同的切圖等

  • 對圖片進(jìn)行壓縮割笙,優(yōu)先使用WebP格式圖像

  • 使用矢量圖(.9)圖來實(shí)現(xiàn)大小可變的背景圖

  • 代碼混淆权烧,使用proGuard代碼混淆器工具,它包括壓縮伤溉、優(yōu)化般码、混淆等功能。

  • 插件化乱顾,不需要的部分可以存在服務(wù)器板祝,當(dāng)用到時(shí)候動(dòng)態(tài)下載。

電量優(yōu)化

電量優(yōu)化我放到最后說走净,是因?yàn)檫@個(gè)優(yōu)先級比較低券时,因?yàn)橐话?code>APP在使用過程中囊嘉,很難造成電量的明顯下降,除非是游戲革为、相機(jī)或者視頻類APP

電量優(yōu)化相對來說比較簡單扭粱,在開發(fā)中注意一下幾點(diǎn)就可以了:

  • 數(shù)據(jù)備份、日志報(bào)告等后臺活動(dòng)震檩,可以放到電量充足或者正在充電時(shí)候執(zhí)行
  • 除視屏播放外琢蛤,一直避免一直亮屏。
  • 錄音抛虏、GPS博其、相機(jī)等耗電操作,在執(zhí)行完成后及時(shí)釋放對應(yīng)資源
  • 后臺不必要的service記得及時(shí)關(guān)閉

總結(jié)

Android的性能優(yōu)化是一個(gè)長期且漫長的過程迂猴。一般企業(yè)在開發(fā)中都是先實(shí)現(xiàn)功能再去管性能慕淡,這樣做會導(dǎo)致后期優(yōu)化起來麻煩且耗時(shí)。建議有可能的話盡量保持一個(gè)好的開發(fā)習(xí)慣沸毁,在項(xiàng)目初期就注意性能方面的事情峰髓,不要引入無用的內(nèi)容、保持代碼整潔息尺、及時(shí)刪除已廢棄模塊等携兵,這樣開發(fā)的項(xiàng)目才回高效且易維護(hù)。


這篇文章也是根據(jù)我開發(fā)的經(jīng)驗(yàn)以及網(wǎng)絡(luò)中的好多精品文章整理而來搂誉,其中一些精彩的深入分析文章大家可以點(diǎn)擊詳情去查看徐紧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市炭懊,隨后出現(xiàn)的幾起案子并级,更是在濱河造成了極大的恐慌,老刑警劉巖侮腹,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘲碧,死亡現(xiàn)場離奇詭異,居然都是意外死亡凯旋,警方通過查閱死者的電腦和手機(jī)呀潭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來至非,“玉大人钠署,你說我怎么就攤上這事』耐郑” “怎么了谐鼎?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長趣惠。 經(jīng)常有香客問我狸棍,道長身害,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任草戈,我火速辦了婚禮塌鸯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘唐片。我一直安慰自己丙猬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布费韭。 她就那樣靜靜地躺著茧球,像睡著了一般。 火紅的嫁衣襯著肌膚如雪星持。 梳的紋絲不亂的頭發(fā)上抢埋,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音督暂,去河邊找鬼揪垄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛损痰,可吹牛的內(nèi)容都是我干的福侈。 我是一名探鬼主播酒来,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼卢未,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了堰汉?” 一聲冷哼從身側(cè)響起辽社,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎翘鸭,沒想到半個(gè)月后滴铅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡就乓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年汉匙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片生蚁。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡噩翠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出邦投,到底是詐尸還是另有隱情伤锚,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布志衣,位于F島的核電站屯援,受9級特大地震影響猛们,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狞洋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一弯淘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吉懊,春花似錦耳胎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至淹魄,卻和暖如春郁惜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背甲锡。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工兆蕉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缤沦。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓虎韵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缸废。 傳聞我的和親對象是個(gè)殘疾皇子包蓝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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