`
iaiai
  • 浏览: 2204694 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Android开发编码规范导致的内存泄露问题

 
阅读更多
在很久很久之前,看过一篇关于内存泄露的文章,里面列举了比较全的应该注意的问题,后来找不到原文地址,今天翻了微博,找到了该文章,为了方便日后自己查看,将注意的问题提取出来。在android开发中,我们的编码习惯可能会让我们编写出一些容易导致内存泄露的代码。所以我们应该要养成一个良好的编码习惯。

单例

平时,我们可能会这样写单例
public class Singleton{
    private static Singleton instance;
    private Context mContext;
    private Singleton(Context mContext){
        this.mContext = mContext;
    }
    public static Singleton getInstance(Context context){
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(context);
                }
            }
        }
        return instance;
    }
}

然后这样写会有隐患,原因是如果我们再Activity中或者其他地方使用Singleton.getInstance()时,我们会顺手写this或者mContext(这个变量也是指向this)作为参数传进去。我们的Singleton是单例,意味着被初始化后就应该一直存在内存中,以方便我们以后调用的时候不会再次创建Singleton对象,但是Singleton中的mContext变量一直都会持有Activity中的Context,导致Activity即使执行了onDestroy方法,也不能够将自己销毁,但是ApplicationContext就显然有所不同了,它一直伴随着我们的应用存在,所以就不用担心前面所说的导致Activity无法销毁的问题了。于是就诞生了正确的写法。
public class Singleton{
    private static Singleton instance;
    private Context mContext;
    private Singleton(Context mContext){
        this.mContext = mContext;
    }
    public static Singleton getInstance(Context context){
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(context.getApplicationContext());
                }
            }
        }
        return instance;
    }
}

匿名内部类

在Android开发中,很多地方会用到匿名内部类,比如事件的监听,Handler的消息处理等等,而一旦使用错误,也会导致内存泄露。
public class MainActivity extends Activity{   
    private Button btn;
    private Handler handler = new Handler(){
       @override
       public void handlerMessage(Message msg){   
       }
    };
    @override
    public void onCreate(Bundle bundle){
       super.onCreate(bundle);
       setContextView(R.layout.activity_main);
       btn=(Button)findViewById(R.id.btn);
       btn.setOnClickListner(View.OnClickListener(){
            @override
            public void onClick(View view){
                Message message = Message.obtain();
                handler.sendMessage(message);
            }
       });

    }
}

当我们执行了MainActivity的finish方法,被延迟的消息会在被处理之前存在于主线程消息队列中,而这个消息中又包含了Handler的引用,而Handler是一个匿名内部类的实例,其持有外面的MainActivity的引用,所以这导致了MainActivity无法回收,进而导致MainActivity持有的很多资源都无法回收,所以产生了内存泄露。然而一个静态的匿名内部类实例是不会持有外部类的引用的。所以正确的写法应该是这样的。
public class MainActivity extends Activity{
    private Button btn;
    private static class MyHandler extends Handler {
        private final WeakReference<MainActivity> mActivity;
        public MyHandler(MainActivity activity) {
            mActivity = new WeakReference<MainActivity>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = mActivity.get();
            if (activity != null) {
                 // ...
            }
        }
    }
    private final MyHandler handler = new MyHandler(this);
    @override
    public void onCreate(Bundle bundle){
       super.onCreate(bundle);
       setContextView(R.layout.activity_foo_layout);
       btn=(Button)findViewById(R.id.btn);
       btn.setOnClickListner(View.OnClickListener(){
            @override
            public void onClick(View view){
                Message message = Message.obtain();
                handler.sendMessage(message);
            }
       });
    }
}

很多时候我们甚至会使用handler发送一个匿名Runnable对象,同样的也会导致内存泄露的问题。因此Runnable对象应该也要使用静态的匿名内部类。
private static class MyRunnable implements Runnable{
    private WeakReference<TextView> textViewWeakReference;
    public MyRunnable(TextView textView){
        textViewWeakReference = new WeakReference<TextView>(textView);
    }
    @override
    public void run(){
         final TextView textView = textViewWeakReference.get();
         if(textView != null){
              textView.setText("OK");
         }
    };
}

使用的时候这样用就可以了
handler.postDelayed(new MyRunnable(textView),1000 * 60 * 10);

Handler

在使用了Handler之后,记得在onDestroy里面调用
handler.removeCallbacksAndMessages(object token);

移除相关消息。

我们可以使用
handler.removeCallbacksAndMessages(null);

当参数为null时,可以清除掉所有跟次handler相关的Runnable和Message,我们在onDestroy中调用次方法也就不会发生内存泄漏了。

对以上几点做一个总结
- 不要让生命周期长于Activity的对象持有到Activity的引用
- 尽量使用Application的Context而不是Activity的Context
- 尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用。如果使用静态内部类,将外部实例引用作为弱引用持有。
- 垃圾回收不能解决内存泄露,了解Android中垃圾回收机制

Context

注意Context与ApplicationContext的区别
- View.getContext,返回当前View对象的Context对象,通常是当前正在展示的Activity对象。
- Activity.getApplicationContext,获取当前Activity所在的(应用)进程的Context对象,通常我们使用Context对象时,要优先考虑这个全局的进程Context。
- ContextWrapper.getBaseContext():用来获取一个ContextWrapper进行装饰之前的Context,可以使用这个方法,这个方法在实际开发中使用并不多,也不建议使用。
- Activity.this 返回当前的Activity实例,如果是UI控件需要使用Activity作为Context对象,但是默认的Toast实际上使用ApplicationContext也可以。


大家注意看到有一些NO上添加了一些数字,其实这些从能力上来说是YES,但是为什么说是NO呢?
- 数字1:启动Activity在这些类中是可以的,但是需要创建一个新的task。一般情况不推荐。
- 数字2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。
- 数字3:在receiver为null时允许,在4.2或以上的版本中,用于获取黏性广播的当前值。(可以无视)
注:ContentProvider、BroadcastReceiver之所以在上述表格中,是因为在其内部方法中都有一个context用于使用。

好了,这里我们看下表格,重点看Activity和Application,可以看到,和UI相关的方法基本都不建议或者不可使用Application,并且,前三个操作基本不可能在Application中出现。实际上,只要把握住一点,凡是跟UI相关的,都应该使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例都可以,当然了,注意Context引用的持有,防止内存泄漏。
  • 大小: 54.1 KB
分享到:
评论

相关推荐

    Android开发编码规范

    ### Android开发编码规范 #### 1. 引言 ##### 1.1 目的 本文档旨在为Android开发人员提供一套系统化的编码规范指南,帮助团队统一代码风格,提高代码质量和可维护性。通过遵循这些规则,可以有效减少代码中的潜在...

    百度android编码规范下载

    在Android开发领域,遵循一套统一的编码规范是至关重要的,它能提高代码的可读性、可维护性和团队协作效率。百度作为中国领先的互联网科技公司,也推出了一套针对Android平台的Java编码规范,旨在帮助开发者养成良好...

    阿里巴巴Android开发手册

    开发者需要了解如何合理地使用这些机制来实现应用的功能,并处理好线程同步问题,避免内存泄漏等问题。 六、文件与数据库 应用中对文件和数据库的使用也是非常关键的,该部分会介绍文件存储的最佳实践和数据库(如...

    Android编码规范

    《Android编码规范与性能优化详解》 ...总结,Android开发中遵循编码规范和进行性能优化是提升应用体验的基础。开发者应注重代码的可读性、可维护性,同时兼顾性能和资源利用率,以打造出高质量的Android应用。

    Android开发常出现的警告

    Android系统会定期检测并报告可能导致内存泄漏的对象。内存泄漏会占用大量内存,降低应用性能,甚至导致应用无响应。开发者应使用工具如MAT (Memory Analyzer Tool) 或 Android Studio 自带的 Memory Profiler 来...

    Android开发规范参考文档.docx编程资料

    - 谨慎使用静态变量实现界面间的共享,以防止潜在的内存泄漏问题。 15. **日志管理** - 使用统一的日志标签(TAG)记录调试信息,并在发布版本中关闭日志输出。 16. **单元测试** - 实施单元测试策略,包括...

    阿里巴巴安卓开发规范

    这份规范源自阿里巴巴集团在长期的Android开发实践中积累的经验和最佳实践,对于任何想要在Android平台上进行专业开发的工程师来说,都具有极高的参考价值。 1. **命名规范**: - 命名应清晰、简洁且具有描述性,...

    13个Android开发常见误区-老程序员原来精在这!

    2. **过度使用AsyncTask**:AsyncTask是Android中处理异步任务的一种简单方式,但滥用会导致内存泄漏和线程安全问题。应该考虑使用Handler、IntentService或现代的LiveData、ViewModel等库来处理后台任务。 3. **...

    Android开发环境设置配置的常见错误

    理解项目结构,遵循编码规范,使用版本控制工具如Git,可以帮助管理源码并解决问题。 总的来说,理解并解决这些常见错误需要开发者具备一定的基础和耐心。通过不断学习,熟悉开发工具,了解Android系统的运作机制,...

    关于手机Android的开发

    不当的内存管理、缓冲区溢出等问题可能导致安全漏洞,因此在使用C函数时要遵循安全编码规范。 6. **C库集成**:Android支持许多开源C库,如OpenSSL、FFmpeg等,可以用于加密、媒体处理等。熟悉如何在Android项目中...

    android编程规范

    本压缩包包含的“android小组_编程规范_20120303”文档,很可能是Android开发团队内部使用的一份详细编码规范指南,用于指导开发者按照最佳实践编写代码。 1. **命名规范**:在Android开发中,命名规范是基础。变量...

    Android安全编码指南.docx

    以下是一些核心的编码规范和建议: 1. DRD00-J: 不要在外部存储(如SD卡)上存储未加密的敏感信息。这包括用户的个人数据、登录凭据等,因为外部存储相对更容易被未经授权的访问。 2. DRD01-J: 限制应用的敏感内容...

    android-lint-summary,更漂亮的android lint问题展示.zip

    6. **代码风格和一致性**:确保代码符合一定的格式和编码规范,提高代码可读性和团队协作效率。 "android-lint-summary" 这个开源项目的目标是改善原始 Lint 输出的用户体验,它可能提供了以下特性: 1. **美化...

    Android-基于注释的Androidlint检查生成

    在Android开发过程中,保持代码质量是非常重要的,而`AndroidLint`是Google提供的一款静态代码分析工具,用于检测潜在的代码问题、性能优化建议以及遵循编码规范。本项目"基于注释的Androidlint检查生成"专注于利用...

    Android C++高级编程使用NDK [ Pro Android C++ with the NDK ]书源码

    但是,不当的使用可能会导致内存泄漏、崩溃等问题。因此,了解内存管理、异常处理和性能调试技巧至关重要。 6. **源码分析**:书中提供的源码章节包括Chapter 10、Chapter 8、Chapter 13和Chapter 14等,分别涉及...

    Android studio 2021.3.1

    2. **内存管理优化**:新版本对内存管理进行了优化,降低了内存占用,减少了因内存泄漏导致的卡顿现象,提升了IDE的稳定性。 3. **更快的模拟器**:Android Emulator在新版本中也得到了升级,启动速度更快,运行更...

    android应用性能优化最佳实践

    - **遵循编码规范**:清晰、简洁的代码更容易被优化。 - **使用编译器插件和Lint**:检查代码中的潜在问题,自动优化部分代码。 10. **持续测试与迭代** - **多设备测试**:不同的设备配置可能会影响性能,因此...

    Android-GreenDao的代码生成器的优化版

    同时,它可能也考虑到了内存管理,避免了大量数据操作时可能导致的内存泄漏问题。 3. **更灵活的映射关系**: 原始的GreenDao可能在处理复杂的实体关系时有所限制,而优化版则可能增加了对多对多关系、自关联等...

Global site tag (gtag.js) - Google Analytics