@Synchronized override fun expectWeaklyReachable(
watchedObject: Any,
description: String
) {
if (!isEnabled()) {
return
}
removeWeaklyReachableObjects()
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
val reference =
KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
SharkLog.d {
"Watching " +
(if (watchedObject is Class<*>) watchedObject.toString() else “instance of ${watchedObject.javaClass.name}”) +
(if (description.isNotEmpty()) " ($description)" else “”) +
" with key $key"
}
watchedObjects[key] = reference
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
上述代码的主要逻辑是:
-
移除弱可达的对象
-
将当前的
watchedObject
添加到KeyedWeakReference
当中 -
将这个weakReference保存到数组中
-
在
checkRetainedExecutor
中执行moveToRetained
方法
根据removeWeaklyReachableObjects
方法中原理,如果这个对象除了由ObjectWatcher
所添加的WeakReference以外,没有其他对象在引用它了,那么这个对象也就可以回收了,watchedObjects
也就可以移除他了。
private fun removeWeaklyReachableObjects() {
var ref: KeyedWeakReference?
do {
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
}
checkRetainedExecutor
其实是个单例对象,里面会通过handler来延迟5s
来执行方法。如果超过5s
则会触发LeakCanary
的泄漏检测机制。5s
只是个经验值应该,因为GC并不是实时发生,因而预留5s
交给GC操作。
触发了LeakCanary的泄漏检测之后,则会执行HeapDumpTrigger
的dumpHeap
方法,在获取到了.hprof
文件之后,调用HeapAnalyzerService.runAnalysis()
给出分析结果。 关于.hprof
文件的分析,不是本文重点,具体可以参考hprof文件协议。其分析基本也就是根据GC Root去寻找泄漏的对象,大体流程图如下。
在Android中常见的内存泄漏
单例
单例所导致的内存泄漏几乎是在android开发中最为常见的内存泄漏问题了。
public class Singleton {
private static Singleton singleton;
private Context context;
private Singleton(Context context) {
this.context = context;
}
public static Singleton getInstance(Context context) {
if (singleton == null) {
singleton = new Singleton(context);
}
return singleton;
}
}
在上面的代码中,如果在执行getInstance
方法的时候,传入的是activity的对象,那么该activity对象就没法被及时回收,导致内存泄漏,可以考虑传入ApplicationContext,或者把context放入到方法变量中。
非静态内部类(包括匿名内部类)
非静态内部类会默认持有外部类的引用,如果它的生命周期长于外部类时,就会导致内存泄漏。 在android开发,这种情况常常见于Handler的使用。
尽可能避免使用静态变量
class MainActivity : AppCompatActivity() {
companion object {
@JvmStatic
private var info: StaticInfo? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
info = StaticInfo(this)
}
class StaticInfo(activity: MainActivity) {
}
在上述代码中,info
是一个静态变量,但是它持有了activity
的引用,由于静态变量的生命周期要比activity
的生命周期长,导致activity
无法及时回收,造成内存泄漏。
资源未关闭造成的内存泄漏
诸如cursor、inputStream等对象一定要注意及时关闭
try {
}catch (e:Exception) {
}finally {
// 可以在finally方法里把cursor等对象进行关闭
}
集合中的对象未及时清理造成的内存泄漏
val list = ArrayList()
例如,如果一个list中存放的是activity对象,就会可能导致activity无法及时回收。如果该list是静态对象的话,不及时移除activity的话,就更会产生内存泄漏了。
webview造成的内存泄漏
因为webview在加载完网页后,它的callback会持有activity的引用,造成webview的内存无法释放。可以在activity的onDestroy()方法中移除该webview,并调用webview.destroy()。
未取消注册或回调造成的内存泄漏
在android中回调是使用非常多的,但如果在注册回调的时候,传入了context对象,则需要注 意及时取消回调,否则就可能会出现内存泄漏。例如eventbus和广播。
内存优化
-
使用
intentService
代替Service
,或者service执行完记得及时停止 -
在系统资源紧张的时候,尽可能多释放一些非重要的资源(如图片的内存缓存)
class MyApp : Application() {
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
// 可以在这里做些内存释放的工作
}
}
- 避免
bitmap
的滥用
如果不是必须操作bitmap,对于图片加载,我们可以使用一些优秀的第三方库来进行加载。使用bitmap记得复用和及时回收。
- 使用针对内存优化过的数据容器
在大多数场景下,可以使用SparseArray代替HashMap等可以一定程度上减少内存使用。
- 使用多进程
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
总结
首先是感觉自己的基础还是不够吧,大厂好像都喜欢问这些底层原理。
另外一部分原因在于资料也还没有看完,一面时凭借那份资料考前突击恶补个几天居然也能轻松应对(在这里还是要感谢那份资料,真的牛),于是自我感觉良好,资料就没有怎么深究下去了。
之前的准备只涉及了Java、Android、计网、数据结构与算法这些方面,面对面试官对其他基础课程的考察显得捉襟见肘。
下一步还是要查漏补缺,进行针对性复习。
最后的最后,那套资料这次一定要全部看完,是真的太全面了,各个知识点都涵盖了,几乎我面试遇到的所有问题的知识点这里面都有!在这里也免费分享给大家,希望大家不要犯和我一样的错误呀!!!一定要看完!
获取方式:点击我的GitHub
d、计网、数据结构与算法这些方面,面对面试官对其他基础课程的考察显得捉襟见肘。
下一步还是要查漏补缺,进行针对性复习。
最后的最后,那套资料这次一定要全部看完,是真的太全面了,各个知识点都涵盖了,几乎我面试遇到的所有问题的知识点这里面都有!在这里也免费分享给大家,希望大家不要犯和我一样的错误呀!!!一定要看完!
[外链图片转存中…(img-xIPHanw0-1711046455617)]
[外链图片转存中…(img-r5jSmCJT-1711046455617)]
[外链图片转存中…(img-58xIsvt7-1711046455618)]
获取方式:点击我的GitHub