我們一起來(lái)聊聊內(nèi)存優(yōu)化

感嘆

其實(shí)吧蟹肘,要開(kāi)發(fā)一個(gè)app是很簡(jiǎn)單的事,很多人都認(rèn)為俯树,不就寫(xiě)幾個(gè)界面帘腹,什么LinearLayout、RelativeLayout许饿、FrameLayout阳欲、TextView、ImageView等等組合在一起陋率,然后在Activity中從服務(wù)器獲取數(shù)據(jù)顯示出來(lái)嘛球化,就那么簡(jiǎn)單,我只想說(shuō)瓦糟,那只是最基本的app開(kāi)發(fā)工作筒愚,而且app開(kāi)發(fā)根本就不你想象的那么簡(jiǎn)單的,特別是Android開(kāi)發(fā)菩浙,各種適配問(wèn)題巢掺,各種崩潰問(wèn)題,還有各種內(nèi)存爆掉的問(wèn)題劲蜻,今天我們就來(lái)聊聊如何讓你的app在手機(jī)運(yùn)行起來(lái)如絲般絲滑陆淀,扯那么遠(yuǎn),今天聊的就是如何寫(xiě)更好的代碼斋竞,處理app的內(nèi)存泄漏倔约,進(jìn)行內(nèi)存優(yōu)化。

單例

相信作為一個(gè)程序猿(不要意思坝初,又自嘲了0.0)浸剩,在你編碼的過(guò)程中,肯定有使用到過(guò)單例吧鳄袍?不知道你們的代碼中的單例是怎么編寫(xiě)的呢绢要?

public static Test mTest;
private Context mContext;
private Test(Context context) {
    mContext = context;
}
public static synchronized Test getInstance(Context context) {   
    if (mTest == null) {        
        mTest = new Test(context);
    }    
    return mTest;
}

相信絕大部分人寫(xiě)的單例都是這樣的吧?這就是傳說(shuō)中的懶漢模式拗小,我們先不去討論此種方式是否好重罪,我們要討論的時(shí)候創(chuàng)建單例的時(shí)候如何避免內(nèi)存的泄漏,仔細(xì)的同學(xué)應(yīng)該發(fā)現(xiàn),創(chuàng)建單例的時(shí)候傳過(guò)來(lái)了一個(gè)Context剿配,那么我們應(yīng)該是用Activity的Context呢搅幅?還是Application的呢?我們來(lái)分析一下呼胚。

假設(shè)傳入的是Activity的Context茄唐,當(dāng)Activity銷毀再重建后,因?yàn)槭菃卫詍Test肯定部位空沪编,所以不會(huì)走 if (mTest == null) ,不會(huì)再進(jìn)行創(chuàng)建年扩,而是直接返回mTest蚁廓。 此時(shí)的Context 還是上一個(gè)activity實(shí)例的Context,所以,上一個(gè)activity實(shí)例并未被釋放掉,所以就會(huì)造成內(nèi)存泄漏了厨幻。所以我們應(yīng)該把Application的Context傳進(jìn)來(lái)相嵌,不應(yīng)是Activity的。

非靜態(tài)匿名內(nèi)部類

有人看到這名詞會(huì)不會(huì)感覺(jué)到陌生克胳?那我們就用實(shí)例來(lái)解析下吧平绩。

public class Test extends Activity { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.test); 
        test(); 
    } 
    /** 
    * 測(cè)試內(nèi)存泄漏的代碼 
    */ 
    private void test() { 
        new Thread(new Runnable() { 
            @Override 
            public void run() { 
                while (true) { 
                    try { 
                        Thread.sleep(1000); 
                    } catch (InterruptedException e) {
                        e.printStackTrace(); 
                    } 
                } 
            } 
        }).start(); 
    }

雖然上面的例子非常的簡(jiǎn)單,而且在寫(xiě)代碼的時(shí)候我們沒(méi)那么蠢會(huì)寫(xiě)這樣的代碼漠另,但是new Thread()捏雌,這個(gè)我們還是會(huì)有用到的吧?這樣看起來(lái)好像沒(méi)什么問(wèn)題啊笆搓,執(zhí)行test方法性湿,然后就一直睡眠1秒鐘嘛,應(yīng)該沒(méi)事啊满败,但當(dāng)我們銷毀了Test這個(gè)activity的時(shí)候肤频,會(huì)出現(xiàn)什么問(wèn)題呢?創(chuàng)建一個(gè)非靜態(tài)的內(nèi)部類實(shí)例如new Thread()算墨,就會(huì)引用它的外圍實(shí)例宵荒,也就是Test。如果這個(gè)非靜態(tài)內(nèi)部類實(shí)例做了一些耗時(shí)的操作净嘀,就會(huì)造成外圍對(duì)象不會(huì)被回收报咳,從而導(dǎo)致內(nèi)存泄漏。

這類問(wèn)題的解決方案為:

1.將內(nèi)部類變成靜態(tài)內(nèi)部類挖藏。
2.如果有強(qiáng)引用Activity中的屬性暑刃,則將該屬性的引用方式改為弱引用。
3.當(dāng)Activity執(zhí)行onDestory時(shí)膜眠,把這些耗時(shí)任務(wù)給干掉岩臣。

WebView

WebView在我之前寫(xiě)的文章中有講到過(guò)溜嗜,這邊也就不重復(fù)了。

WebView想說(shuō)愛(ài)你不容易啊

有興趣的可以進(jìn)去看看架谎。

Handler

這個(gè)的話炸宵,我敢保證,你絕對(duì)有用過(guò)狐树!

private Handler recordHandler = new Handler() {    
    @Override    
    public void handleMessage(Message msg) {     
    }
};

是不是絕大部分人都是這樣來(lái)操作的焙压?給我說(shuō)中了吧鸿脓,其實(shí)就跟上面說(shuō)到的非靜態(tài)內(nèi)部類一樣抑钟,將 Handler 聲明為靜態(tài)的, 則其存活期跟 Activity 的生命周期就無(wú)關(guān)了野哭。同時(shí)通過(guò)弱引用的方式引入 Activity在塔, 避免直接將Activity 作為 context 傳進(jìn)去。

正確的使用方式是

static class TestHandler extends Handler {    
    private final WeakReference<Test> mActivityRef; 
    TestHandler(Test activity) {        
        mActivityRef = new WeakReference<>(activity);
    }    
    @Override    
    public void handleMessage(Message msg) {
        final Test activity = mActivityRef.get();
        if (activity == null || activity.isFinishing()) {
            removeCallbacksAndMessages(null);            
            return;        
        }        
        switch (msg.what) {  
        }    
    }
}
資源

對(duì)于Bitmap拨黔,BraodcastReceiver蛔溃,ContentObserver,F(xiàn)ile篱蝇,Cursor贺待,Stream等資源的使用,應(yīng)該在Activity銷毀時(shí)及時(shí)關(guān)閉或者注銷零截,否則這些資源將不會(huì)被回收麸塞,造成內(nèi)存泄漏的。這個(gè)是也一個(gè)良好的習(xí)慣吧涧衙,隨用隨開(kāi)哪工,開(kāi)完就要關(guān),就像你打開(kāi)冰箱門(mén)弧哎,拿了可樂(lè)出來(lái)雁比,使用完了之后就會(huì)把冰箱門(mén)關(guān)上,防止冷氣浪費(fèi)了撤嫩,就如同防止內(nèi)存泄漏了一樣偎捎。

靜態(tài)

Context對(duì)象為靜態(tài)的。

publicclassTestextendsActivity{
publicstaticContextmContext;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
mContext=this;
}
}

像上面的代碼序攘,那么Activity就無(wú)法正常銷毀茴她,會(huì)常駐內(nèi)存。這樣就會(huì)造成內(nèi)存泄漏了两踏,最好就不要把Context變成靜態(tài)的變量败京,可以使用Application的Context。

總結(jié)

1梦染、對(duì)于生命周期比Activity長(zhǎng)的對(duì)象如果需要應(yīng)該使用ApplicationContext


3赡麦、對(duì)于需要在靜態(tài)內(nèi)部類中使用非靜態(tài)外部成員變量(如:Context朴皆、View ),可以在靜態(tài)內(nèi)部類中使用弱引用來(lái)引用外部類的變量來(lái)避免內(nèi)存泄漏
4泛粹、對(duì)于不再需要使用的對(duì)象或者資源遂铡,顯示的將其賦值為null,比如使用完Bitmap后先調(diào)用recycle()晶姊,再賦為null
5扒接、注意activity的生命周期,要在銷毀的時(shí)候把所有耗時(shí)的任務(wù)或者資源都要釋放
6们衙、合理使用單例钾怔,并且要注意其生命周期
7、推薦使用內(nèi)存泄漏檢測(cè)工具蒙挑,
(1)代碼檢測(cè)工具:LeakCanary
(2)Android Studio自帶工具M(jìn)onitors宗侦,可以時(shí)刻監(jiān)控app的Memory.

其實(shí)內(nèi)存泄漏都是人為的(這不是廢話么,難道代碼不是人寫(xiě)的么忆蚀?)矾利,我這里說(shuō)的人為的意思是,不良的編碼習(xí)慣馋袜,還有基礎(chǔ)功不扎實(shí)造成的男旗,所以不管怎樣,正所謂代碼是人寫(xiě)的欣鳖,內(nèi)存泄漏也是人為的察皇,那么就肯定會(huì)有對(duì)應(yīng)的方法去解決的,只有在不斷開(kāi)發(fā)中遇到困難观堂,在困難中不斷學(xué)習(xí)让网,在學(xué)習(xí)中不斷成長(zhǎng),這才是我們想要的师痕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末溃睹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子胰坟,更是在濱河造成了極大的恐慌因篇,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笔横,死亡現(xiàn)場(chǎng)離奇詭異竞滓,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)吹缔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門(mén)商佑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人厢塘,你說(shuō)我怎么就攤上這事茶没〖∮模” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵抓半,是天一觀的道長(zhǎng)喂急。 經(jīng)常有香客問(wèn)我,道長(zhǎng)笛求,這世上最難降的妖魔是什么廊移? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮探入,結(jié)果婚禮上狡孔,老公的妹妹穿的比我還像新娘。我一直安慰自己新症,他們只是感情好步氏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著徒爹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪芋类。 梳的紋絲不亂的頭發(fā)上隆嗅,一...
    開(kāi)封第一講書(shū)人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音侯繁,去河邊找鬼胖喳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛贮竟,可吹牛的內(nèi)容都是我干的丽焊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼咕别,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼技健!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起惰拱,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤雌贱,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后偿短,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體欣孤,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年昔逗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了降传。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡勾怒,死狀恐怖婆排,靈堂內(nèi)的尸體忽然破棺而出款票,到底是詐尸還是另有隱情,我是刑警寧澤泽论,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布艾少,位于F島的核電站,受9級(jí)特大地震影響翼悴,放射性物質(zhì)發(fā)生泄漏缚够。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一鹦赎、第九天 我趴在偏房一處隱蔽的房頂上張望谍椅。 院中可真熱鬧,春花似錦古话、人聲如沸雏吭。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)杖们。三九已至,卻和暖如春肩狂,著一層夾襖步出監(jiān)牢的瞬間摘完,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工傻谁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留孝治,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓审磁,卻偏偏與公主長(zhǎng)得像谈飒,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子态蒂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

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