由于我们的应用依赖 Activity 的 onStop 来停止播放器,近期发现退出直播间后,声音居然残留10s 左右,通过日志发现 Activity 的 onStop 在退出界面后需要10s左右才会被执行,该如何定位这个问题呢?
- Aug 10, 2018...more
Activity 销毁的延迟
May 24, 2017...more先看一张按下 Back 键之后,Activity 生命周期回调的日志截图:
从日志中可以看到当按下 Back 键时,当前的 Activity 会马上回调 onPause() 方法,而 onStop() 是在 MainActivity 的 onResume()之后才调用,onPause() 与 onStop()之间相隔了大约 300ms,也就是说 Activity 不是马上被销毁的。再看另一个快速重新打开
LifeActivity
的 Case:这里我采用代码模拟快速重新打开
LifeActivity
,finish() 后延迟 300ms 再启动LifeActivity
。因为我们的 Activity 里没做什么事,所以很难手动重现快速重新打开 Activity 的异常 Case,而实际项目因为逻辑复杂,往往在1~2s或者更长的时间里很容易复现这种情况。出现了诡异的事:LifeActivity[33732136] 是旧的 Activity,但它却在新的 LifeActivity[212157058] 显示之后才被销毁的,看到这个可能你已经心头一凉。这会导致什么问题呢?这会让我们依赖 Activity 生命周期回调来做资源回收的代码变得不可靠。
举个栗子:如果我们在 onStart() 中启动相机在 onStop() 中关闭相机,正常重新打开这个页面时相机的状态操作:打开 -> 关闭 -> 打开,快速重新打开这个 Activity 时就可能不是这个顺序了:打开->打开->关闭。然后用户就遭殃了,他将无法正常使用你的应用了,而用户只是因为手速过快。
ArrayList 导致的内存泄露
May 24, 2017...more前段时间在排查一处内存泄露时,发现是注册的一个监听器导致的。但检查了这个监听器在该取消注册的地方的确取消注册了,那内存中为什么还有它的引用呢?
「Wrapper」让你更愉快地调用 Listener
Mar 19, 2017...more在 GitHub 查看
背景
Android 开发中,会有很多情况下用到观察者模式,如果你自定义了一个 Listener,在需要调用他们来通知观察者时,或许会遇到以下几点头疼的问题:
这个 Listener 是 Nullable 的,那么每次调用前都需要作判空处理
1
2
3if (listener != null) {
listener.foo();
}事件是在其他线程中发出,而观察者需要在另一个线程中(通常是 UI 线程)处理,所以需要在每次调用的时候做
Handler#post
操作1
2
3
4
5
6
7
8
9
10if(Looper.myLooper() != Looper.getMainLooper()) {
handler.post(new Runnable() {
public void run() {
listener.foo();
}
});
} else {
listener.foo();
}
如果观察者可以注册多个,那每次在调用的时候的时候都需要遍历一下所有 Listener
1
2
3for(final FooListener listener : listener) {
listener.foo();
}
这三种情况可能会同时出现,比如在做与 Service 通信时是很常见的,如果回调方法更多时,代码写起来那是相当痛苦😂
我的2016
Jan 23, 2017...more2016 年,在我人生中是变化很大的一年
调用完 Handler#removeCallbacks() 就安全了吗?
Nov 25, 2016...moreHandler
容易引起内存泄露,这是大家都知道的,所以你应该会在适当的时候调用removeCallbacks()
方法来移除消息。但当以下使用场景时,依然可能会出现内存泄露。Android Orientation 的坑
Nov 9, 2016...more最近在做 Bigo Live 直播间的横屏适配,横屏和竖屏下会有一些状态的差异。但我们的应用在横屏下,切换后台再回来后,发现一些状态显示不对。
此文献给我的强迫症
Aug 31, 2016...more设置界面中 SwitchCompat 无动画效果
Feb 26, 2016...more问题分析
Support library 的
SwitchCompat.java
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void setChecked(boolean checked) {
super.setChecked(checked);
// Calling the super method may result in setChecked() getting called
// recursively with a different value, so load the REAL value...
checked = isChecked();
if (getWindowToken() != null && ViewCompat.isLaidOut(this) && isShown()) {
animateThumbToCheckedState(checked);
} else {
// Immediately move the thumb to the new position.
cancelPositionAnimator();
setThumbPosition(checked ? 1 : 0);
}
}xip.io + gradle 在调试时动态设置服务端地址
Jul 24, 2015...more日常开发中,如果服务端在本地,通常可通过改hosts、写死IP、动态域名等方式来设置服务端地址,但总觉很麻烦,不灵活;比如更换网络导致IP变化,就得重新设置。
今天突然想到,利用xip.io 和 gradle来自动设置服务端地址