PhoneStateListener 引起的内存泄露

由于开了StrictMode,今天看log发现类似这种错误:

E/StrictMode: android.os.StrictMode$InstanceCountViolation: class com.XXX.XXX.XXX.XXX.LiveVideoActivity; instances=2; limit=1
E/StrictMode: android.os.StrictMode$InstanceCountViolation: class com.XXX.XXX.XXX.XXX.LiveVideoActivity; instances=3; limit=1
E/StrictMode: android.os.StrictMode$InstanceCountViolation: class com.XXX.XXX.XXX.XXX.LiveVideoActivity; instances=4; limit=1
E/StrictMode: android.os.StrictMode$InstanceCountViolation: class com.XXX.XXX.XXX.XXX.LiveVideoActivity; instances=5; limit=1
……

activity的实例在稳步增长,本来猜测是有些地方没有释放Context导致,内存泄露,检查了下代码,把所有可能引起这个问题的地方都处理了,但是还没是有问题,没辙了,开始问度娘,问bing(就这个问题的搜索结果有感 如果访问不了google,建议还是用bing....)终于在 Stack Overflow 找到了同样的情况。

http://stackoverflow.com/questions/5956132/android-strictmode-instancecountviolation

大神给出了找出内存泄露的方法 ,如下:

    after backing out from MyActivity and seeing the log message
    make a heap dump (.hprof)
    open it in Eclipse Memory Analyzer
    run OQL: select * from instanceof full.package.name.of.MyActivity
    select all with Ctrl+Click or Shift+Click
    right click and Merge Shortest Path to GC Roots > with all references

最终抓到了 hprof文件 ,分析方法在这篇文章中找到:

http://blog.csdn.net/ccwwff/article/details/7817139

运行cmd打开命令行,cd到\ android-sdk-windows\tools所在目录,并输入命令hprof-conv xxxxx.hprof yyyyy.hprof,其中xxxxx.hprof为原始文件,yyyyy.hprof为转换过后的文件。转换过后的文件自动放在android-sdk-windows\tools 目录下。

这里有个问题,其实是在
<pre name="code" class="plain">hprof-conv是在   \ android-sdk-windows\platform-tools   里

 最终发现问题出在 PhoneStateListener 上。 

原因是,我做了个需求 播放器在后台继续保持运行,所以我需要监听通话状态,再来电话的时候,暂停播放 ,免得播放器抢占了音频(此处有待商榷,应该有别的解决方案)。

我在网上随便查了下PhoneStateListener的用法,然后抄了下。。。但是没人告诉我,这玩意会引起内存泄露。。。所以啊,随随便便查到的东西,千万不要直接用到代码里,得仔细研究下。。。

老规矩 继续查,在网上查到了这个东西需要解除监听,大神说要这样用:

You should try cleaning up on onPause and then onResume re-instate the stuff you need. This will help clean up some memory pressure and leaks. 
解除监听的方法是:

            telephonyManager.listen(telePhonePhoneStateListener,PhoneStateListener.LISTEN_NONE);
但是我的需求是在后台也得继续监听啊,所以,我把这个方法扔到了onDestroy()里。。。试了下,没什么卵用。。。没办法继续。。。

找到了这个:

http://www.07net01.com/2015/08/898417.html

在项目开发过程中通过ddms的堆看到内存一直持续在增长,很容易想到发生内存泄露,引用没有被释放,通过dump 最终发现是 PhoneStateListener 内部对自己有一个强引用的handler,如果是在主线程中引用的PhoneStateListener,那么他将释放不掉,引发内存泄露。 
解决方法就好的是在子线程中创建 PhoneStateListener ,其次是在PhoneStateListener中使用弱引用。比如需要activity的对象。那么就要在创建PhoneStateListener的时候将传入activity的弱引用 WeakRef ,这样就不用担心内存泄露的问题,不过这种弱引用的方式用起来简单一些,但是他还有个小问题,就是PhoneStateListener自己是释放不掉的,虽然他不再持有外部的一些引用,那么就要求不要在PhoneStateListener里面有过多的资源创建。

想试了下弱引用,发现没搞懂这里怎么使用弱引用。。。

恩,那就用子线程,代码如下:

 moniterTekePhone = new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                sonThreadlooper = Looper.myLooper();
                telephonyManager = (TelephonyManager)mActivity.getSystemService(Context.TELEPHONY_SERVICE);
                telePhonePhoneStateListener = new TelePhonePhoneStateListener(mDoTelePhonyWorkListener);
                telephonyManager.listen(telePhonePhoneStateListener,PhoneStateListener.LISTEN_CALL_STATE);
                Looper.loop();
             }
        });
<pre name="code" class="java">moniterTekePhone.start();
 销毁用,在onDestroy()中调用: 

    public void destoryMoniterTelePhone(){
        if(telephonyManager != null){
            telephonyManager.listen(telePhonePhoneStateListener,PhoneStateListener.LISTEN_NONE);
            telephonyManager = null;
        }
        if(moniterTekePhone != null && moniterTekePhone.isAlive()){
            if(sonThreadlooper != null){
                try{
                    sonThreadlooper.quit();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
            sonThreadlooper = null;
        }
    }

试了下,还是未能成功解决问题。。。依然维持了对当前activity的引用。

百思不得其解,估计是binder的问题。

继续百度,发现有另一种写法:监听广播


IntentFilter intentFilterPhone = new IntentFilter();
        intentFilterPhone.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
        phoneReceiver.setTelePhonyWorkListener(mDoTelePhonyWorkListener);
        mActivity.registerReceiver(phoneReceiver, intentFilterPhone);

重写广播:

public class PhoneReceiver extends BroadcastReceiver {

    DoTelePhonyWorkListener telePhonyWorkListener;

    public  void setTelePhonyWorkListener(DoTelePhonyWorkListener listener){
        telePhonyWorkListener = listener;
    }

    public interface DoTelePhonyWorkListener{
        void callStateIdle();
        void callStateRinging();
        void callStateOffHook();
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
            // 如果是去电(拨出)
        } else {
            TelephonyManager tm = (TelephonyManager) context
                    .getSystemService(Service.TELEPHONY_SERVICE);
            // 设置一个监听器
            tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
        }
    }

    PhoneStateListener listener = new PhoneStateListener() {

        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            // state 当前状态 incomingNumber,貌似没有去电的API
            super.onCallStateChanged(state, incomingNumber);
            switch (state) {
            case TelephonyManager.CALL_STATE_RINGING:
                if( telePhonyWorkListener != null){
                    telePhonyWorkListener.callStateRinging();
                }
                break;
            case TelephonyManager.CALL_STATE_IDLE:
                if(telePhonyWorkListener != null){
                    telePhonyWorkListener.callStateIdle();
                }
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK:
                if(telePhonyWorkListener != null){
                    telePhonyWorkListener.callStateOffHook();
                }
                break;
            }
        }

    };
}

最后 在onDestroy中解除注册广播。

这会儿是真的解决了。。。。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值