在内存泄漏的解决中,内部类(案例为:匿名内部类)改成静态类绝对是一大杀器。比这曾经在一个项目中用这种方法,解决了百分之70的泄漏。解决方案简单粗暴
1)就是将匿名内部类改写为静态类,并添加弱引用
问题代码:(我使用阿里热修复的时候的代码):
SophixManager.getInstance().setPatchLoadStatusStub(new PatchLoadStatusListener() {
@Override
public void onLoad(final int mode, final int code, final String info, final int handlePatchVersion) {
//todo something
}
})
使用LeakCanranry,直接报泄漏,泄漏日志显示“是PatchLoadStatusListener 持有acitivity 引用”
这个案例是在首页做的,只要一按下返回键,必现泄漏。
处理方法就是改写为:
private static class MyPatchLoadStatusListener implements PatchLoadStatusListener {
private WeakReferenceweakReference;
public MyPatchLoadStatusListener(HomeActivity homeActivity) {
weakReference =new WeakReference<>(homeActivity);
}
@Override
public void onLoad(int i, int code, String s, int i2) {
}
}
调用的地方修改为:
SophixManager.getInstance().setPatchLoadStatusStub(new MyPatchLoadStatusListener(this));
修正后,leakcananry 不再报错。开发中养成良好的习惯,坚决不写有泄漏的代码
2)内部类改成外部类,并添加弱引用
开发一个倒计时splash页面,之后进入应用。在进入的时候,发现leakCananry报泄漏。日志如下:
重点看哪里呢,重点看紫色实线和虚线的交叉处,如果只有虚线,直接从虚线底端看起
这里表明了,是匿名内部类TimeTask持有引用了ActivityAdvertising ,导致我们出现了泄漏:
找到代码:
public class ActivityAdvertising extends MyActivity {
.....
ScheduledExecutorService newScheduledThreadPool = Executors
.newScheduledThreadPool(1);
private int time;
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
runOnUiThread(() -> {
time--;
if (countdownProgressView != null) {
touch_ok.setClickable(false);
countdownProgressView.setText(time + "s");
}
if (time <= 0) {
if (countdownProgressView != null) {
countdownProgressView.setText("進入");
touch_ok.setClickable(true);
}
}
});
}
};
}
....
一看代码,高危呀,匿名内部类。 处理成外部类问题解决:
public class Ttimetask extends TimerTask {
public WeakReference weakReference;
public Ttimetask(ActivityAdvertising activityAdvertising){
weakReference =new WeakReference(activityAdvertising);
}
@Override
public void run() {
weakReference.get().runTimeTask();
}
}
调用的地方:
class ActivityAdvertising : MyActivity() {
internal var newScheduledThreadPool = Executors
.newScheduledThreadPool(1)
private var time: Int = 0
var timerTask=Ttimetask(getActivity())
fun runTimeTask(){
runOnUiThread {
time--
if (countdownProgressView != null) {
touch_ok!!.isClickable = false
countdownProgressView!!.text = time.toString() + "s"
}
if (time <= 0) {
if (countdownProgressView != null) {
countdownProgressView!!.text = "進入"
touch_ok!!.isClickable = true
}
}
}
}
override fun initView() {
timerTask.run()
}
fun cancelTimer() {
if (timerTask != null) {
timerTask.cancel()
}
}
override fun initData() {
time = 5
newScheduledThreadPool.scheduleAtFixedRate(timerTask, 0, 1000, TimeUnit.MILLISECONDS)
}
override fun onDestroy() {
super.onDestroy()
cancelTimer()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
一个timeTask泄漏就解决掉了:网上很多人说是需要添加 TimeTask的取消方法,kotlin版本下我的代码实测没有通过,我是通过外部类的方法解决掉的:
再次点击,leakCanary下,测试了一大波,还是只有Audiodmanager的泄漏,timeTask泄漏顺利解决。
Android内存泄漏解决(总)