由于开了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
……
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
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 目录下。
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();
}
});
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;
}
}
};
}
这会儿是真的解决了。。。。