PhoneStateListener memery leaked ( LeakCanary ) 手机来电状态监听,泄漏无法解决

1、问题

用LeakCanary 分析手机app的泄漏情况, 发现有监听手机状态的界面全部泄漏,试了很久,都没有改成功

2、手机来电状态监听

2.1、权限
   <uses-permission android:name="android.permission.READ_PHONE_STATE" />

2.2、普通代码(会泄漏)

//注册
    public void register(PhoneStateListener listener) {
        TelephonyManager tm = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);
        tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
    }

//销毁
   public void unRegister(PhoneStateListener listener) {

        if (tm != null) {
            tm.listen(listener, PhoneStateListener.LISTEN_NONE);
            tm = null;
        }

    }

3、改为弱引用, 子线程注册 (依旧泄漏)

    TelephonyManager tm;
    WeakReference<Context> weakReference;

//注册
    public void register(PhoneStateListener listener) {


        new Thread(new Runnable() {
            @Override
            public void run() {

        tm = (TelephonyManager) weakReference.get().getSystemService(Service.TELEPHONY_SERVICE);
        tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);

            }
        }).start();


    }

//销毁
   public void unRegister(PhoneStateListener listener) {

        if (tm != null) {
            tm.listen(listener, PhoneStateListener.LISTEN_NONE);
            tm = null;
        }

    }

4、结论

查的几种方式 是都会泄漏, 不管listener ,tm 最终设置为null 还是弱引用,子线程操作什么的。

5、改进(无法释放, 规避 tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE) 概率)

在做手机来电状态监听时, 直接操作TelephonyManager 的方法是方便了很多,但是会使得context一直持有,泄漏。 改进方式是把使用方式写全,先监听电话广播,识别到是来电,再用TelephonyManager设置PhoneStateListener,这样子就只有来电时那个 Activity 是泄漏的, 并且使用弱引用。(没使用子线程注册)。

public class LogicTelephonyManager {
    final private String TAG = "LogicTelephonyManager";

    TelephonyManager tm;
    WeakReference<Context> weakReference;
    IntentFilter intentFilterPhone;
    PhoneReceiver receiver;

    public LogicTelephonyManager(Context context) {


        // this.context = context;
        weakReference = new WeakReference<Context>(context);

        intentFilterPhone = new IntentFilter();
        intentFilterPhone.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
        receiver = new PhoneReceiver();
        context.registerReceiver(receiver, intentFilterPhone);
    }


//注册 
    public void register(PhoneStateListener listener) {


//        new Thread(new Runnable() {
//            @Override
//            public void run() {

        tm = (TelephonyManager) weakReference.get().getSystemService(Service.TELEPHONY_SERVICE);
        tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);

//            }
//        }).start();


    }


//销毁
    public void unRegister(PhoneStateListener listener) {

        if (context != null) {
            context.unregisterReceiver(receiver);
        }

        if (tm != null) {
            tm.listen(listener, PhoneStateListener.LISTEN_NONE);
            tm = null;
        }

    }

    public class PhoneReceiver extends BroadcastReceiver {


        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
                // 如果是去电(拨出)
            } else {
                //不是去电当成来电

                //TODO:可以在这里做注册。 不过我的业务是把他发射出去在接收端进行注册绑定 listener
                EventBus.getDefault().post(new PhoneReceiverEvent());
            }
        }
    }

}

场景类

public class XXP {




        LogicTelephonyManager telephonyManagerUtil;


        public XXP(Activity activity) {

EventBus.getDefault().register(this);



            telephonyManagerUtil = new LogicTelephonyManager(activity);
}




        @Subscribe 

        public void onEvent(PhoneReceiverEvent event) {

if (event != null) {

            LogDebugUtil.i("LogicTelephonyManager", "PhoneReceiverEvent = " + event);

            if (telephonyManagerUtil != null) {
telephonyManagerUtil.register(listener);
}
}
}


        public void onDestroy() {
EventBus.getDefault().unregister(this);



            if (telephonyManagerUtil != null) {
telephonyManagerUtil.unRegister(listener);
}

            System.gc();
}






        PhoneStateListener listener = new PhoneStateListener() {

            @Override
            public void onCallStateChanged(int state, String incomingNumber) {
                //注意,方法必须写在super方法后面,否则incomingNumber无法获取到值。
                super.onCallStateChanged(state, incomingNumber);
                switch (state) {
                    case TelephonyManager.CALL_STATE_IDLE:
                        LogDebugUtil.d(TAG, "挂断");
                        break;
                    case TelephonyManager.CALL_STATE_OFFHOOK:
                        LogDebugUtil.d(TAG, "接听");


                        break;
                    case TelephonyManager.CALL_STATE_RINGING:
                        LogDebugUtil.d(TAG, "响铃:来电号码" + incomingNumber);
                        //输出来电号码


                        break;
                }
            }
        };
    }

6、补充

这个或许就是为什么要注册广播, 再注册监听方法吧。 因为监听后无法释放 也是尴尬。 有能路过能解决的 倒是可以留言指导下

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值