thread.setAccessible(true);
thread.set(watchdog, null);
} catch (final Throwable t) {
Log.e(TAG, “stopWatchDog, set null occur error:” + t);
t.printStackTrace();
try {
// 直接调用stop方法,在Android 6.0之前会有线程安全问题
final Method method = clazz.getSuperclass().getDeclaredMethod(“stop”);
method.setAccessible(true);
method.invoke(watchdog);
} catch (final Throwable e) {
Log.e(TAG, “stopWatchDog, stop occur error:” + t);
t.printStackTrace();
}
}
} catch (final Throwable t) {
Log.e(TAG, “stopWatchDog, get object occur error:” + t);
t.printStackTrace();
}
其实我是用的是stackoverflow这篇帖子中提供的方法:
public static void fix() {
try {
Class clazz = Class.forName(“java.lang.Daemons$FinalizerWatchdogDaemon”);
Method method = clazz.getSuperclass().getDeclaredMethod(“stop”);
method.setAccessible(true);
Field field = clazz.getDeclaredField(“INSTANCE”);
field.setAccessible(true);
method.invoke(field.get(null));
}
catch (Throwable e) {
e.printStackTrace();
}
}
两种方法都是通过反射最终将FinalizerWatchdogDaemon
中的thread
置空,这样也就不会执行此线程,所以不会再有超时异常发生。推荐老师的方法,更加全面完善。因为在Android 6.0之前会有线程安全问题,如果直接调用stop方法,还是会有几率触发此异常。5.0源代码如下:
private static abstract class Daemon implements Runnable {
private Thread thread;// 一种是直接置空thread
public synchronized void start() {
if (thread != null) {
throw new IllegalStateException(“already running”);
}
thread = new Thread(ThreadGroup.systemThreadGroup, this, getClass().getSimpleName());
thread.setDaemon(true);
thread.start();
}
public abstract void run();
protected synchronized boolean isRunning() {
return thread != null;
}
public synchronized void interrupt() {
if (thread == null) {
throw new IllegalStateException(“not running”);
}
thread.interrupt();
}
public void stop() {
Thread threadToStop;
synchronized (this) {
threadToStop = thread;
thread = null; // 一种是通过调用stop置空thread
}
if (threadToStop == null) {
throw new IllegalStateException(“not running”);
}
threadToStop.interrupt();
while (true) {
try {
threadToStop.join();
return;
} catch (InterruptedException ignored) {
}
}
}
public synchronized StackTraceElement[] getStackTrace() {
return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT;
}
}
这个所谓的线程安全问题就在stop方法中的threadToStop.interrupt()
。在6.0开始,这里变为了interrupt(threadToStop)
,而interrupt
方法加了同步锁。
public synchronized void interrupt(Thread thread) {
if (thread == null) {
throw new IllegalStateException(“not running”);
}
thread.interrupt();
}
虽然崩溃不会出现了,但是问题依然存在,可谓治标不治本。通过这个问题也提醒我们,尽量避免重写finalize
方法,同时不要在其中有耗时操作。其实我们Android中的View都有实现finalize
方法,那么减少View的创建就是一种解决方法。
强烈推荐阅读:提升Android下内存的使用意识和排查能力、再谈Finalizer对象–大型App中内存与性能的隐性杀手
3.SchedulerPoolFactory
前一阵在用Android Studio的内存分析工具检测App时,发现每隔一秒,都会新分配出20多个实例,跟踪了一下发现是RxJava2中的SchedulerPoolFactory
创建的。
一般来说如果一个页面创建加载好后是不会再有新的内存分配,除非页面有动画、轮播图、EditText的光标闪动等页面变化。当然了在应用退到后台时,或者页面不可见时,我们会停止这些任务。保证不做这些无用的操作。然而我在后台时,这个线程池还在不断运行着,也就是说CPU在周期性负载,自然也会耗电。那么就要想办法优化一下了。
SchedulerPoolFactory
的作用是管理 ScheduledExecutorServices
的创建并清除。
SchedulerPoolFactory
部分源码如下:
static void tryStart(boolean purgeEnabled) {
if (purgeEnabled) {
for (;😉 { // 一个死循环
ScheduledExecutorService curr = PURGE_THREAD.get();
if (curr != null) {
return;
}
ScheduledExecutorService next = Executors.newScheduledThreadPool(1, new RxThreadFactory(“RxSchedulerPurge”));
if (PURGE_THREAD.compareAndSet(curr, next)) {
// RxSchedulerPurge线程池,每隔1s清除一次
next.scheduleAtFixedRate(new ScheduledTask(), PURGE_PERIOD_SECONDS, PURGE_PERIOD_SECONDS, TimeUnit.SECONDS);
return;
} else {
next.shutdownNow();
}
}
}
}
static final class ScheduledTask implements Runnable {
@Override
public void run() {
for (ScheduledThreadPoolExecutor e : new ArrayList(POOLS.keySet())) {
if (e.isShutdown()) {
POOLS.remove(e);
} else {
e.purge();//图中154行,purge方法可用于移除那些已被取消的Future。
}
}
}
}
我查了相关问题,在stackoverflow找到了此问题,同时也给RxJava提了Issue,得到了回复是可以使用:
// 修改周期时间为一小时
System.setProperty(“rx2.purge-period-seconds”, “3600”);
当然你也可以关闭周期清除:
System.setProperty(“rx2.purge-enabled”, false);
作用范围如下:
static final class PurgeProperties {
boolean purgeEnable;
int purgePeriod;
void load(Properties properties) {
if (properties.containsKey(PURGE_ENABLED_KEY)) {
purgeEnable = Boolean.parseBoolean(properties.getProperty(PURGE_ENABLED_KEY));
} else {
purgeEnable = true; // 默认是true
}
if (purgeEnable && properties.containsKey(PURGE_PERIOD_SECONDS_KEY)) {
try {
// 可以修改周期时间
purgePeriod = Integer.parseInt(properties.getProperty(PURGE_PERIOD_SECONDS_KEY));
} catch (NumberFormatException ex) {
purgePeriod = 1; // 默认是1s
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://img-blog.csdnimg.cn/img_convert/2ce34b251474f885a001d671bd4e3cd7.jpeg)
最后
感觉现在好多人都在说什么安卓快凉了,工作越来越难找了。又是说什么程序员中年危机啥的,为啥我这年近30的老农根本没有这种感觉,反倒觉得那些贩卖焦虑的都是瞎j8扯谈。当然,职业危机意识确实是要有的,但根本没到那种草木皆兵的地步好吗?
Android凉了都是弱者的借口和说辞。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
所以,最后这里放上我耗时两个月,将自己8年Android开发的知识笔记整理成的Android开发者必知必会系统学习资料笔记,上述知识点在笔记中都有详细的解读,里面还包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。
以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。
最后,赠与大家一句诗,共勉!
不驰于空想,不骛于虚声。不忘初心,方得始终。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。
最后,赠与大家一句诗,共勉!
不驰于空想,不骛于虚声。不忘初心,方得始终。