android内存泄露

一.当你不再需要某个实例后,但是这个对象却仍然被引用防止被垃圾回收(Prevent from being bargage collected)。

这个情况就叫做内存泄露(Memory Leak)

二.常见内存泄露与解决方法

1.需要手动关闭的对象没有关闭

1.1. try/catch/finally中网络文件等流的手动关闭

  • HTTP
  • File
  • ContendProvider
  • Bitmap
  • Uri
  • Socket
1.2. onDestroy() 或者 onPause()中未及时关闭对象

泄露实例:

  • 线程泄漏:当你执行耗时任务,在onDestroy()的时候考虑调用Thread.close(),如果对线程的控制不够强的话,可以使用RxJava自动建立线程池进行控制,并在生命周期结束时取消订阅;
  • Handler泄露:当退出activity时,要注意所在Handler消息队列中的Message是否全部处理完成,可以考虑removeCallbacksAndMessages(null)手动关闭
  • 广播泄露:手动注册广播时,记住退出的时候要unregisterReceiver()
  • 第三方SDK/开源框架泄露:ShareSDK, JPush等第三方SDK需要按照文档控制生命周期,它们有时候要求你继承它们丑陋的activity,其实也为了帮你控制生命周期
  • 各种callBack/Listener的泄露,要及时设置为Null,特别是static的callback
  • EventBus等观察者模式的框架需要手动解除注册
  • 某些Service也要及时关闭,比如图片上传,当上传成功后,要stopself()
  • Webview需要手动调用WebView.onPause()以及WebView.destory()

比如常见的ButterKnife

  @Override public void onDestroyView() {
    super.onDestroyView();
    ButterKnife.reset(this);
  }

再比如ShareSDK(此垃圾再也不用)

protected void onDestroy() {
        ShareSDK.stopSDK(this);
        super.onDestroy();
    }

使用开源的框架(比如帮你写好的图片下载队列,REST解析等)可能会帮助你快速的解决这个问题,但是知其然并知其所以然,也要了解它们的生命周期

.2.2. 使用内部匿名类要注意什么?

匿名内部类实际上就是non-static inner class,比如某些初学者经常一个new Handler就写出来了,它对外部类有一个强引用。建议单独写出来这个类并继承,并加入static修饰。

3.2.4. 单例模式(Singleton)是不是内存泄漏?

在单例模式中,只有一个对象被产生,看起来一直占用了内存,但是这个不意味就是浪费了内存,内存本来就是用来装东西的,只要这个对象一直被高效的利用就不能叫做泄露。但是也不要偷懒,一个劲的全整成了单例,越多的单例会让内存占用过多,放在Application中初始化的内容也越多,意味着APP打开白屏的时间会更久,而且软件维护起来也变得复杂。

  • 好的例子:GlobalContext,SmsReceiver动态注册,EventBus

3.3. Bitmap的使用

  • 使用前注意配置Bitmap的Config,比如长宽,参数(565, 8888),格式;
  • 使用中注意缓存;
  • 使用后注意recycle以清理native层的内存。

2.3以后的bitmap不需要手动recycle了,内存已经在java层了。同时,Bitmap还有别人做好的轮子,比如PhotoView,Picasso,就可以方便的解决OOM问题。

3.4. 多线程

线程泄露可能是最严重的泄露问题了,第一它可能与Handler一样,转一转手机内存就没了,第二是当回调的时候,它极可能弹出NullPointException

个人在实际使用的一个失败实例

上传图片时退出Activity,等到图片完成后,Toast就会抛出空指针异常。

//retrofit 1.9 bad sample 
RestAdapter adapter = new RestAdapter.Builder().setEndpoint(HeadlineService.END_POINT)
            .setLogLevel(RestAdapter.LogLevel.FULL)
            .build();
adapter.create(ImageService.class)
    .updateImage(new TypedFile("image/*", file), new TypedString(nickname),
        new TypedString(Build.MODEL), new TypedString(avatar),
        new Callback<UploadResult>() {
          @Override public void success(UploadResult uploadResult, Response response) {
            if (uploadResult.getStatus() == 1) {
              Log.d(TAG, "upload successfully!");
              Toast.makeText(getActivity(), "上传成功!", Toast.LENGTH_SHORT)
                  .show();
            } else {
              Log.e(TAG, "upload failed!");
              Toast.makeText(getActivity(), "上传失败!", Toast.LENGTH_SHORT)
                  .show();
            }
            bmp.recycle();
          }

          @Override public void failure(RetrofitError error) {
            bmp.recycle();
          }
        });

我是使用Retrofit框架进行上传的,retrofit内部自己维护它的线程与生命周期,当我退出Activity时,Retrofit内部的网络线程并没有停止;当图片上传成功回调的时候,却发现window已经没了,这样就会抛出异常。

解决方法:在Activity中使用耗时任务本来就不合适,使用Service可以更好的控制回调问题。

3.5. Context与ApplicationContext

classContextApplicationContext
生命周期非常长,几乎就是单例
适用场景Activity中需要UI/素材资源的地方数据库,包管理,偏好设置,以及Picasso/Retrofit/ShareSDK/Webview等单例框架

Context的生命周期是一个Activiy,而ApplicationContext的生命周期是整个程序。我们最要注意的就是Context的内存泄露。

在Activiy的UI中要使用Context,而在其他的地方比如数据库、网络、系统服务的需要频繁调用Context的情况时,要使用ApplicationContext,以防止内存泄露。


Listview的item泄露

这个是入门问题了,加入ViewHolder可以减少findViewById的时间,或者使用RecyclerView,来解决“滑动很卡”的问题。这个实质也是一个单例。

使用弱引用

使用弱引用可以防止一定程度的无意引用造成的泄露,比如在Handler中使用弱引用作为参数,当销毁的时候就有可能不会发生泄露。

弱引用随时可能为null,使用前需要判断是否为空



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值