由于Android如今有三种Fragment
androidx.fragment.app
android.app.fragment
android.support.v4.app.Fragment
leakCanary通过反射先去查抄是否引入上面三种Fragment,如果有就反射创建对应的watcher参加到 fragmentDestroyWatchers中
private fun getWatcherIfAvailable( fragmentClassName: String, watcherClassName: String, reachabilityWatcher: ReachabilityWatcher): ((Activity) -> Unit)? { return if (classAvailable(fragmentClassName) && classAvailable(watcherClassName) ) { val watcherConstructor = Class.forName(watcherClassName).getDeclaredConstructor(ReachabilityWatcher::class.java) @Suppress("UNCHECKED_CAST") watcherConstructor.newInstance(reachabilityWatcher) as (Activity) -> Unit } else { null }}2.2.2 Fragment内存泄漏查抄机遇
(1)application注册activity生命周期回调
(2)当监听到ctivity被创建时,获取该activity的对应的fragmentManager创建fragment的生命周期观察者
(3)当onFragmentViewDestroyed/onFragmentDestroyed触发时,遍历集合然后查抄是否可以采取Fragment实例
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { override fun onFragmentViewDestroyed( fm: FragmentManager, fragment: Fragment ) { val view = fragment.view if (view != null) { reachabilityWatcher.expectWeaklyReachable( view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " + "(references to its views should be cleared to prevent leaks)" ) } } override fun onFragmentDestroyed( fm: FragmentManager, fragment: Fragment ) { reachabilityWatcher.expectWeaklyReachable( fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback" ) }}2.2.3 查抄哪些ViewModel内存泄漏
既然fragment/activity被销毁了,fragment/activity对象被采取了,那么fragment/activity绑定的全部viewmodel实例也应该销毁,所以leakCanary增加了viewmodel的内存查抄
(1)监听当activity被创建时,绑定一个间谍viewmodel实例
//AndroidXFragmentDestroyWatcheroverride fun invoke(activity: Activity) { if (activity is FragmentActivity) { val supportFragmentManager = activity.supportFragmentManager supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true) ViewModelClearedWatcher.install(activity, reachabilityWatcher) }}(2)监听当fragment被创建时,绑定一个间谍viewmodel实例
//AndroidXFragmentDestroyWatcher##fragmentLifecycleCallbacksoverride fun onFragmentCreated( fm: FragmentManager, fragment: Fragment, savedInstanceState: Bundle?) { ViewModelClearedWatcher.install(fragment, reachabilityWatcher)}2.2.4 ViewModel内存泄漏查抄机遇
(1)利用反射获得fragment/activity绑定的viewModel集合
(2)当leakcanary绑定的viewmodel生命周期走到onCleared时,就去查抄全部viewmodel实例是否可以采取(这边就是为啥作者取名叫spy)
//ViewModelClearedWatcheroverride fun onCleared() { viewModelMap?.values?.forEach { viewModel -> reachabilityWatcher.expectWeaklyReachable( viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback" ) }}2.3 RootViewWatcher
view触发onViewDetachedFromWindow查抄是否采取View实例
利用Curtains获得视图变化,查抄全部被添加到phoneWindow上面的,windowLayoutParams.title为Toast大概是Tooltip,大概除PopupWindow之外的全部view.
//RootViewWatcherrootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener { val watchDetachedView = Runnable { reachabilityWatcher.expectWeaklyReachable( rootView, "${rootView::class.java.name} received View#onDetachedFromWindow() callback" ) } override fun onViewAttachedToWindow(v: View) { WindowManager.LayoutParams.TYPE_PHONE mainHandler.removeCallbacks(watchDetachedView) } override fun onViewDetachedFromWindow(v: View) { mainHandler.post(watchDetachedView) }})2.4 ServiceWatcher
service触发onDestroy查抄是否采取Service实例
private fun onServiceDestroyed(token: IBinder) { servicesToBeDestroyed.remove(token)?.also { serviceWeakReference -> serviceWeakReference.get()?.let { service -> reachabilityWatcher.expectWeaklyReachable( service, "${service::class.java.name} received Service#onDestroy() callback" ) } }}3.怎样判定内存泄漏
利用Shark分析工具分析hprof文件
(8)这里通过解析hprof文件天生heapAnalysis对象.SharkLog打印并存入数据库
override fun onHeapAnalyzed(heapAnalysis: HeapAnalysis) { SharkLog.d { "\u200B\n${LeakTraceWrapper.wrap(heapAnalysis.toString(), 120)}" } val db = LeaksDbHelper(application).writableDatabase val id = HeapAnalysisTable.insert(db, heapAnalysis) db.releaseReference()...}5.内存泄漏误报
Java虚拟机的主流垃圾采取器采取的是可达性分析算法, 可达性算法是通过从GC root往外遍历,如果从root节点无法遍历该节点表明该节点对应的对象处于可采取状态. 反之不会采取.
public class MainActivity2 extends FragmentActivity { Fragment mFragmentA; Fragment mFragmentB; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); mFragmentA = new FragmentA(); mFragmentB = new FragmentB(); findViewById(R.id.buttona).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { replaceFragment(mFragmentA); } }); findViewById(R.id.buttonb).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { replaceFragment(mFragmentB); } }); } private void replaceFragment(Fragment fragment) { getSupportFragmentManager().beginTransaction() .replace(R.id.container, fragment).commit(); }}以fragment为例,leakcanary认为fragment走onDestory了,就应该释放fragment.但是这种环境真的是内存泄漏么?
├─ com.example.MainActivity2 instance │ Leaking: NO (Activity#mDestroyed is false) │ ↓ MainActivity2.mFragmentA │ ~~~~~~~~~~ ╰→ com.example.FragmentA instance Leaking: YES (ObjectWatcher was watching this because com.example.FragmentA received Fragment#onDestroy() callback and Fragment#mFragmentManager is null) key = 216c8cf8-2cdb-4509-84e9-8404afefffeb watchDurationMillis = 3804 retainedDurationMillis = -1 key = eaa41c88-bccb-47ac-8fb7-46b27dec0356 watchDurationMillis = 6113 retainedDurationMillis = 1112 key = 77d5f271-382b-42ec-904b-1e8a6d4ab097 watchDurationMillis = 7423 retainedDurationMillis = 2423 key = 8d79952f-a300-4830-b513-62e40cda8bba watchDurationMillis = 15771 retainedDurationMillis = 10765 13858 bytes retained by leaking objects Signature: f1d17d3f6aa4713d4de15a4f465f97003aa7根据堆栈信息,leakcanary认为fragmentA走了onDestory应该要采取这个fragmentA对象,但是发现还被MainActivity2对象持有无法采取,然后判定是内存泄漏. 放在我们这个逻辑里面,fragment不释放是对的. 只不外这种实现不是内存最佳罢了.