我们一起来聊聊内存优化

感叹

其实吧,要开发一个app是很简单的事,很多人都认为,不就写几个界面,什么LinearLayout、RelativeLayout、FrameLayout、TextView、ImageView等等组合在一起,然后在Activity中从服务器获取数据显示出来嘛,就那么简单,我只想说,那只是最基本的app开发工作,而且app开发根本就不你想象的那么简单的,特别是Android开发,各种适配问题,各种崩溃问题,还有各种内存爆掉的问题,今天我们就来聊聊如何让你的app在手机运行起来如丝般丝滑,扯那么远,今天聊的就是如何写更好的代码,处理app的内存泄漏,进行内存优化。

单例

相信作为一个程序猿(不要意思,又自嘲了0.0),在你编码的过程中,肯定有使用到过单例吧?不知道你们的代码中的单例是怎么编写的呢?

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;
}

相信绝大部分人写的单例都是这样的吧?这就是传说中的懒汉模式,我们先不去讨论此种方式是否好,我们要讨论的时候创建单例的时候如何避免内存的泄漏,仔细的同学应该发现,创建单例的时候传过来了一个Context,那么我们应该是用Activity的Context呢?还是Application的呢?我们来分析一下。

假设传入的是Activity的Context,当Activity销毁再重建后,因为是单例,所以mTest肯定部位空,所以不会走 if (mTest == null) ,不会再进行创建,而是直接返回mTest。 此时的Context 还是上一个activity实例的Context,所以,上一个activity实例并未被释放掉,所以就会造成内存泄漏了。所以我们应该把Application的Context传进来,不应是Activity的。

非静态匿名内部类

有人看到这名词会不会感觉到陌生?那我们就用实例来解析下吧。

public class Test extends Activity { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.test); 
        test(); 
    } 
    /** 
    * 测试内存泄漏的代码 
    */ 
    private void test() { 
        new Thread(new Runnable() { 
            @Override 
            public void run() { 
                while (true) { 
                    try { 
                        Thread.sleep(1000); 
                    } catch (InterruptedException e) {
                        e.printStackTrace(); 
                    } 
                } 
            } 
        }).start(); 
    }

虽然上面的例子非常的简单,而且在写代码的时候我们没那么蠢会写这样的代码,但是new Thread(),这个我们还是会有用到的吧?这样看起来好像没什么问题啊,执行test方法,然后就一直睡眠1秒钟嘛,应该没事啊,但当我们销毁了Test这个activity的时候,会出现什么问题呢?创建一个非静态的内部类实例如new Thread(),就会引用它的外围实例,也就是Test。如果这个非静态内部类实例做了一些耗时的操作,就会造成外围对象不会被回收,从而导致内存泄漏。

这类问题的解决方案为:

1.将内部类变成静态内部类。
2.如果有强引用Activity中的属性,则将该属性的引用方式改为弱引用。
3.当Activity执行onDestory时,把这些耗时任务给干掉。

WebView

WebView在我之前写的文章中有讲到过,这边也就不重复了。

WebView想说爱你不容易啊

有兴趣的可以进去看看。

Handler

这个的话,我敢保证,你绝对有用过!

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

是不是绝大部分人都是这样来操作的?给我说中了吧,其实就跟上面说到的非静态内部类一样,将 Handler 声明为静态的, 则其存活期跟 Activity 的生命周期就无关了。同时通过弱引用的方式引入 Activity, 避免直接将Activity 作为 context 传进去。

正确的使用方式是

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) {  
        }    
    }
}
资源

对于Bitmap,BraodcastReceiver,ContentObserver,File,Cursor,Stream等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏的。这个是也一个良好的习惯吧,随用随开,开完就要关,就像你打开冰箱门,拿了可乐出来,使用完了之后就会把冰箱门关上,防止冷气浪费了,就如同防止内存泄漏了一样。

静态

Context对象为静态的。

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

像上面的代码,那么Activity就无法正常销毁,会常驻内存。这样就会造成内存泄漏了,最好就不要把Context变成静态的变量,可以使用Application的Context。

总结

1、对于生命周期比Activity长的对象如果需要应该使用ApplicationContext


3、对于需要在静态内部类中使用非静态外部成员变量(如:Context、View ),可以在静态内部类中使用弱引用来引用外部类的变量来避免内存泄漏
4、对于不再需要使用的对象或者资源,显示的将其赋值为null,比如使用完Bitmap后先调用recycle(),再赋为null
5、注意activity的生命周期,要在销毁的时候把所有耗时的任务或者资源都要释放
6、合理使用单例,并且要注意其生命周期
7、推荐使用内存泄漏检测工具,
(1)代码检测工具:LeakCanary
(2)Android Studio自带工具Monitors,可以时刻监控app的Memory.

其实内存泄漏都是人为的(这不是废话么,难道代码不是人写的么?),我这里说的人为的意思是,不良的编码习惯,还有基础功不扎实造成的,所以不管怎样,正所谓代码是人写的,内存泄漏也是人为的,那么就肯定会有对应的方法去解决的,只有在不断开发中遇到困难,在困难中不断学习,在学习中不断成长,这才是我们想要的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352

推荐阅读更多精彩内容