//WeakHashMap的Key-Value回收原理 还是依赖ref+Queue
class MemoryLeakTrack private constructor() : ITracker {
private val mHandler = Handler(CollieHandlerThread.looper)
private val mActivityStringWeakHashMap = WeakHashMap<Activity, String>()
private val mSimpleActivityLifecycleCallbacks: SimpleActivityLifecycleCallbacks =
object : SimpleActivityLifecycleCallbacks() {
override fun onActivityDestroyed(activity: Activity) {
super.onActivityDestroyed(activity)
mActivityStringWeakHashMap[activity] = activity.javaClass.simpleName
}
override fun onActivityStopped(activity: Activity) {
super.onActivityStopped(activity)
// 退后台,GC 找LeakActivity
if (!isInBackGround()) {
return
}
mHandler.postDelayed({
mallocBigMem()
Runtime.getRuntime().gc()
}, 1000)
mHandler.postDelayed(Runnable {
try {
if (!isInBackGround()) {
return@Runnable
}
// 分配大点内存促进GC
mallocBigMem()
Runtime.getRuntime().gc()
SystemClock.sleep(100)
System.runFinalization()
val hashMap = HashMap<String, Int>()
for ((key) in mActivityStringWeakHashMap) {
val name = key.javaClass.simpleName
val value = hashMap[name]
if (value == null) {
hashMap[name] = 1
} else {
hashMap[name] = value + 1
}
}
if (mMemoryListeners.size > 0) {
for ((key, value) in hashMap) {
for (listener in mMemoryListeners) {
listener.onLeakActivity(key, value)
}
}
}
} catch (ignored: Exception) {
}
}, 10000)
}
}
override fun destroy(application: Application) {
Collie.instance.removeActivityLifecycleCallbacks(mSimpleActivityLifecycleCallbacks)
mHandler.removeCallbacksAndMessages(null)
}
override fun startTrack(application: Application) {
Collie.instance.addActivityLifecycleCallbacks(mSimpleActivityLifecycleCallbacks)
mHandler.postDelayed(object : Runnable {
override fun run() {
if (mMemoryListeners.size > 0 && !isInBackGround()) {
val trackMemoryInfo = collectMemoryInfo(application)
for (listener in mMemoryListeners) {
listener.onCurrentMemoryCost(trackMemoryInfo)
}
}
mHandler.postDelayed(this, (30 * 1000).toLong())
}
}, (30 * 1000).toLong())
}
override fun pauseTrack(application: Application) {}
private val mMemoryListeners: MutableSet<ITrackMemoryListener> = HashSet()
fun addOnMemoryLeakListener(leakListener: ITrackMemoryListener) {
mMemoryListeners.add(leakListener)
}
fun removeOnMemoryLeakListener(leakListener: ITrackMemoryListener) {
mMemoryListeners.remove(leakListener)
}
interface ITrackMemoryListener {
fun onLeakActivity(activity: String?, count: Int)
fun onCurrentMemoryCost(trackMemoryInfo: TrackMemoryInfo?)
}
private fun collectMemoryInfo(application: Application): TrackMemoryInfo {
if (TextUtils.isEmpty(display)) {
display =
"" + application.resources.displayMetrics.widthPixels + "*" + application.resources.displayMetrics.heightPixels
}
val activityManager =
application.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
// 系统内存
val memoryInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memoryInfo)
val systemMemory = SystemMemory()
systemMemory.availMem = memoryInfo.availMem shr 20
systemMemory.totalMem = memoryInfo.totalMem shr 20
systemMemory.lowMemory = memoryInfo.lowMemory
systemMemory.threshold = memoryInfo.threshold shr 20
//java内存
val rt = Runtime.getRuntime()
//进程Native内存
val appMemory = AppMemory()
val debugMemoryInfo = Debug.MemoryInfo()
Debug.getMemoryInfo(debugMemoryInfo)
appMemory.nativePss = (debugMemoryInfo.nativePss shr 10).toLong()
appMemory.dalvikPss = (debugMemoryInfo.dalvikPss shr 10).toLong()
appMemory.totalPss = (debugMemoryInfo.totalPss shr 10).toLong()
appMemory.mMemoryInfo = debugMemoryInfo
val trackMemoryInfo = TrackMemoryInfo()
trackMemoryInfo.systemMemoryInfo = systemMemory
trackMemoryInfo.appMemory = appMemory
trackMemoryInfo.procName = getProcessName(application)
trackMemoryInfo.display = display
trackMemoryInfo.activityCount = getSize()
return trackMemoryInfo
}
private fun mallocBigMem() {
val leakHelpBytes = ByteArray(4 * 1024 * 1024)
var i = 0
while (i < leakHelpBytes.size) {
leakHelpBytes[i] = 1
i += 1024
}
}
companion object {
@Volatile
private var sInstance: MemoryLeakTrack? = null
@JvmStatic
val instance: MemoryLeakTrack?
get() {
if (sInstance == null) {
synchronized(MemoryLeakTrack::class.java) {
if (sInstance == null) {
sInstance = MemoryLeakTrack()
}
}
}
return sInstance
}
private var display: String? = null
}
class Collie private constructor() {
private val mHandler: Handler = Handler(CollieHandlerThread.looper)
private val mITrackListener: ITrackFpsListener
private val mITrackMemoryLeakListener: ITrackMemoryListener
private val mIBatteryListener: IBatteryListener
private val mCollieListeners: MutableList = ArrayList()
private val mActivityLifecycleCallbacks = HashSet<Application.ActivityLifecycleCallbacks>()
fun addActivityLifecycleCallbacks(callbacks: Application.ActivityLifecycleCallbacks) {
mActivityLifecycleCallbacks.add(callbacks)
}
fun removeActivityLifecycleCallbacks(callbacks: Application.ActivityLifecycleCallbacks) {
mActivityLifecycleCallbacks.remove(callbacks)
}
private val mActivityLifecycleCallback: Application.ActivityLifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
push(activity)
for (item in mActivityLifecycleCallbacks) {
item.onActivityCreated(activity, bundle)
}
}
override fun onActivityStarted(activity: Activity) {
markStart()
for (item in mActivityLifecycleCallbacks) {
item.onActivityStarted(activity)
}
}
override fun onActivityResumed(activity: Activity) {
for (item in mActivityLifecycleCallbacks) {
item.onActivityResumed(activity)
}
}
override fun onActivityPaused(activity: Activity) {
for (item in mActivityLifecycleCallbacks) {
item.onActivityPaused(activity)
}
}
override fun onActivityStopped(activity: Activity) {
markStop()
for (item in mActivityLifecycleCallbacks) {
item.onActivityStopped(activity)
}
}
override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {
for (item in mActivityLifecycleCallbacks) {
item.onActivitySaveInstanceState(activity, bundle)
}
}
override fun onActivityDestroyed(activity: Activity) {
for (item in mActivityLifecycleCallbacks) {
item.onActivityDestroyed(activity)
}
pop(activity)
}
}
fun init(
application: Application,
config: Config,
listener: CollieListener
) {
application.registerActivityLifecycleCallbacks(mActivityLifecycleCallback)
mCollieListeners.add(listener)
if (config.useTrafficTrack) {
trafficStatsListener = object : ITrackTrafficStatsListener {
override fun onTrafficStats(activity: Activity, value: Long) {
for (collieListener in mCollieListeners) {
collieListener.onTrafficStats(activity, value)
}
}
}
TrafficStatsTracker.startTrack(application)
}
// 开始内存监测
if (config.useMemTrack) {
MemoryLeakTrack.instance!!.startTrack(application)
MemoryLeakTrack.instance!!.addOnMemoryLeakListener(mITrackMemoryLeakListener)
}
if (config.useFpsTrack) {
FpsTracker.getInstance().setTrackerListener(mITrackListener)
FpsTracker.getInstance().startTrack(application)
}
if (config.showDebugView) {
DebugHelper.instance!!.startTrack(application)
}
if (config.useBatteryTrack) {
BatteryStatsTracker.instance!!.addBatteryListener(mIBatteryListener)
BatteryStatsTracker.instance!!.startTrack(application)
}
if (config.useStartUpTrack) {
iLaunchTrackListener = object : ILaunchTrackListener {
override fun onActivityFocusableCost(
activity: Activity?,
duration: Long,
finishNow: Boolean
) {
for (collieListener in mCollieListeners) {
collieListener.onActivityFocusableCost(activity, duration, finishNow)
}
}
override fun onAppColdLaunchCost(duration: Long, processName: String?) {
for (collieListener in mCollieListeners) {
collieListener.onAppColdLaunchCost(duration, processName)
}
}
override fun onActivityLaunchCost(
activity: Activity?,
duration: Long,
finishNow: Boolean
) {
for (collieListener in mCollieListeners) {
collieListener.onActivityLaunchCost(activity, duration, finishNow)
}
}
}
LauncherTracker.startTrack(application)
}
}
fun stop(application: Application) {
application.unregisterActivityLifecycleCallbacks(mActivityLifecycleCallback)
CollieHandlerThread.quitSafely()
}
companion object {
@Volatile
private var sInstance: Collie? = null
@JvmStatic
val instance: Collie
get() {
if (sInstance == null) {
synchronized(Collie::class.java) {
if (sInstance == null) {
sInstance = Collie()
}
}
}
return sInstance!!
}
}
init {
mITrackListener = object : ITrackFpsListener {
override fun onFpsTrack(
activity: Activity,
currentCostMils: Long,
currentDropFrame: Long,
isInFrameDraw: Boolean,
averageFps: Long
) {
val currentFps =
if (currentCostMils == 0L) 60 else Math.min(60, 1000 / currentCostMils)
mHandler.post {
if (currentDropFrame > 1) DebugHelper.instance!!.update(
"""实时fps $currentFps
丢帧 $currentDropFrame
1s平均fps $averageFps
本次耗时 $currentCostMils"“”
)
for (collieListener in mCollieListeners) {
collieListener.onFpsTrack(
activity,
currentCostMils,
currentDropFrame,
isInFrameDraw,
averageFps
)
}
}
}
override fun onANRAppear(activity: Activity?) {
for (collieListener in mCollieListeners) {
collieListener.onANRAppear(activity)
}
}
}
mITrackMemoryLeakListener = object : ITrackMemoryListener {
override fun onLeakActivity(activity: String?, count: Int) {
// Log.v(“Collie”, "内存泄露 " + activity + " 数量 " + count);
for (collieListener in mCollieListeners) {
collieListener.onLeakActivity(activity, count)
}
}
override fun onCurrentMemoryCost(trackMemoryInfo: TrackMemoryInfo?) {
// Log.v(“Collie”, "内存 " + trackMemoryInfo.procName + " java内存 "
// + trackMemoryInfo.appMemory.dalvikPss + " native内存 " +
// trackMemoryInfo.appMemory.nativePss);
for (collieListener in mCollieListeners) {
collieListener.onCurrentMemoryCost(trackMemoryInfo)
}
}
}
mIBatteryListener = object : IBatteryListener {
override fun onBatteryCost(batteryInfo: BatteryInfo?) {
for (collieListener in mCollieListeners) {
collieListener.onBatteryCost(batteryInfo)
}
}
}
}
}
public class LabApplication extends Application {
public static long sLaunchCost;
@Override
public void onCreate() {
super.onCreate();
sApplication = this;
Router.initBrowserRouter(this);
Router.initActivityRouter(getApplicationContext());
Collie.getInstance().init(this, new Config(false, true, true, true, true, true), new CollieListener() {
@Override
public void onActivityFocusableCost(Activity activity, long duration, boolean finishNow) {
Log.v("Collie", "Activity 获取焦点" + activity + " "+ duration );
}
@Override
public void onTrafficStats(Activity activity, long value) {
Log.v("Collie", "" + activity.getClass().getSimpleName() + " 流量消耗 " + value * 1.0f / (1024 * 1024) + "M");
}
@Override
public void onBatteryCost(BatteryInfo batteryInfo) {
Log.v("Collie", " 电量流量消耗 " + batteryInfo.cost);
}
@Override
public void onAppColdLaunchCost(long duration, String processName) {
Log.v("Collie", "启动耗时 " + duration + " processName " + processName);
sLaunchCost = duration;
}
@Override
public void onActivityLaunchCost(Activity activity, long duration, boolean finishNow) {
Log.v("Collie", "activity启动耗时 " + activity + " " + duration + " finishNow " + finishNow);
}
@Override
public void onLeakActivity(String activity, int count) {
Log.v("Collie", "内存泄露 " + activity + " 数量 " + count);
}
@Override
public void onCurrentMemoryCost(TrackMemoryInfo trackMemoryInfo) {
Log.v("Collie", "内存 " + trackMemoryInfo.procName + " java内存 "
+ trackMemoryInfo.appMemory.dalvikPss + " native内存 " +
trackMemoryInfo.appMemory.nativePss);
}
@Override
public void onFpsTrack(Activity activity, long currentCostMils, long currentDropFrame, boolean isInFrameDraw, long averageFps) {
if (currentDropFrame >= 2)
Log.v("Collie", "Activity " + activity + " 掉帧 " + currentDropFrame + " 是否因为Choro 绘制掉帧 " + isInFrameDraw + " 1s 平均帧率" + averageFps);
}
@Override
public void onANRAppear(Activity activity) {
Log.v("Collie", "Activity " + activity + " ANR ");
}
});
}
private static Application sApplication;
public static Application getContext() {
return sApplication;
}
public static void startTrace(Context context) {
File file = new File(context.getExternalFilesDir("android"), SystemClock.uptimeMillis()+"methods.trace");
Debug.startMethodTracing(file.getAbsolutePath(), 300 * 1024 * 1024);
}
}