
?

?
1.ResourcePlugin init方法里创建执行activity泄漏检测的类ActivityRefWatcher,start方法里启动检测mWatcher.start();
public class ResourcePlugin extends Plugin {
private static final String TAG = "Matrix.ResourcePlugin";
//配置类
private final ResourceConfig mConfig;
//执行activity泄漏检测的类
private ActivityRefWatcher mWatcher = null;
public ResourcePlugin(ResourceConfig config) {
mConfig = config;
}
//todo 没有用到
public static void activityLeakFixer(Application application) {
// Auto break the path from Views in their holder to gc root when activity is destroyed.
application.registerActivityLifecycleCallbacks(new ActivityLifeCycleCallbacksAdapter() {
@Override
public void onActivityDestroyed(Activity activity) {
ActivityLeakFixer.fixInputMethodManagerLeak(activity);
ActivityLeakFixer.unbindDrawables(activity);
ActivityLeakFixer.fixViewLocationHolderLeakApi28(activity);
}
});
}
public ActivityRefWatcher getWatcher() {
return mWatcher;
}
@Override
public void init(Application app, PluginListener listener) {
super.init(app, listener);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
MatrixLog.e(TAG, "API is low Build.VERSION_CODES.ICE_CREAM_SANDWICH(14), ResourcePlugin is not supported");
unSupportPlugin();
return;
}
//执行activity泄漏检测的类
mWatcher = new ActivityRefWatcher(app, this);
}
@Override
public void start() {
super.start();
if (!isSupported()) {
MatrixLog.e(TAG, "ResourcePlugin start, ResourcePlugin is not supported, just return");
return;
}
//开启检测
mWatcher.start();
}
@Override
public void stop() {
super.stop();
if (!isSupported()) {
MatrixLog.e(TAG, "ResourcePlugin stop, ResourcePlugin is not supported, just return");
return;
}
//关掉检测
mWatcher.stop();
}
@Override
public void destroy() {
super.destroy();
if (!isSupported()) {
MatrixLog.e(TAG, "ResourcePlugin destroy, ResourcePlugin is not supported, just return");
return;
}
//destroy检测
mWatcher.destroy();
}
@Override
public String getTag() {
return SharePluginInfo.TAG_PLUGIN;
}
@Override
public void onForeground(boolean isForeground) {
MatrixLog.d(TAG, "onForeground: %s", isForeground);
if (isPluginStarted() && mWatcher != null) {
mWatcher.onForeground(isForeground);
}
}
public ResourceConfig getConfig() {
return mConfig;
}
}
2.ActivityRefWatcher具体检测的类
public class DestroyedActivityInfo {
//为每个已经ondestroy的activity设置一个key
public final String mKey;
//activity的名字
public final String mActivityName;
//已经调用onDestroy方法的activity的弱引用
public final WeakReference<Activity> mActivityRef;
//强制gc多次发现这个activity泄漏的次数
public int mDetectedCount = 0;
public DestroyedActivityInfo(String key, Activity activity, String activityName) {
mKey = key;
mActivityName = activityName;
mActivityRef = new WeakReference<>(activity);
}
}
//ok - HeapDump代表
public class HeapDump implements Serializable {
//Hprof文件
private final File mHprofFile;
//DestroyedActivityInfo类中为每个已经ondestroy的activity设置的key
private final String mRefKey;
//activity名字
private final String mActivityName;
public HeapDump(File hprofFile, String refKey, String activityName) {
mHprofFile = Preconditions.checkNotNull(hprofFile, "hprofFile");
mRefKey = Preconditions.checkNotNull(refKey, "refKey");
mActivityName = Preconditions.checkNotNull(activityName, "activityName");
}
public File getHprofFile() {
return mHprofFile;
}
public String getReferenceKey() {
return mRefKey;
}
public String getActivityName() {
return mActivityName;
}
}
public final class ResourceConfig {
public static final String TAG = "Matrix.ResourceConfig";
//应用在前台时,每分钟去检测是否有泄漏
private static final long DEFAULT_DETECT_INTERVAL_MILLIS = TimeUnit.MINUTES.toMillis(1);
//应用在后台时,每20分钟检测是否有泄漏
private static final long DEFAULT_DETECT_INTERVAL_MILLIS_BG = TimeUnit.MINUTES.toMillis(20);
//检测一个activity是否泄漏,是检测了10次它还没回收,就说明它泄漏了
private static final int DEFAULT_MAX_REDETECT_TIMES = 10;
//默认的模式是MANUAL_DUMP,手动dump
private static final DumpMode DEFAULT_DUMP_HPROF_MODE = DumpMode.MANUAL_DUMP;
private final IDynamicConfig mDynamicConfig;//todo,一个动态配置类
//dump模式
private final DumpMode mDumpHprofMode;
private final boolean mDetectDebugger;//是否在debugger模式支持
private final String mTargetActivity;//查看泄漏的Activity,在ManualDumpProcessor里会用到
private ResourceConfig(IDynamicConfig dynamicConfig, DumpMode dumpHprofMode, boolean detectDebuger, String targetActivity) {
this.mDynamicConfig = dynamicConfig;
this.mDumpHprofMode = dumpHprofMode;
this.mDetectDebugger = detectDebuger;
this.mTargetActivity = targetActivity;
}
//应用在前台时,每分钟去检测是否有泄漏
public long getScanIntervalMillis() {
return mDynamicConfig.get(IDynamicConfig.ExptEnum.clicfg_matrix_resource_detect_interval_millis.name(), DEFAULT_DETECT_INTERVAL_MILLIS);
}
//应用在后台时,每20分钟检测是否有泄漏
public long getBgScanIntervalMillis() {
return mDynamicConfig.get(IDynamicConfig.ExptEnum.clicfg_matrix_resource_detect_interval_millis_bg.name(), DEFAULT_DETECT_INTERVAL_MILLIS_BG);
}
//检测10次
public int getMaxRedetectTimes() {
return mDynamicConfig.get(IDynamicConfig.ExptEnum.clicfg_matrix_resource_max_detect_times.name(), DEFAULT_MAX_REDETECT_TIMES);
}
//检测模式
public DumpMode getDumpHprofMode() {
return mDumpHprofMode;
}
//手动dump模式的时候,跳转activity
public String getTargetActivity() {
return mTargetActivity;
}
//debug模式是否可以检测
public boolean getDetectDebugger() {
return mDetectDebugger;
}
//如果跳过触发Dump Hprof,甚至可以把监测步骤在现网环境启用,以发现测试阶段难以触发的Activity泄漏
public enum DumpMode {
NO_DUMP, // report only,只上报名字
AUTO_DUMP, // auto dump hprof,
MANUAL_DUMP, // notify only
SILENCE_ANALYSE, // dump and analyse hprof when screen off
FORK_DUMP, // fork dump hprof immediately TODO
FORK_ANALYSE, // fork dump and analyse hprof immediately TODO
}
public static final class Builder {
private DumpMode mDefaultDumpHprofMode = DEFAULT_DUMP_HPROF_MODE;
private IDynamicConfig dynamicConfig;
private String mTargetActivity;
private boolean mDetectDebugger = false;
public Builder dynamicConfig(IDynamicConfig dynamicConfig) {
this.dynamicConfig = dynamicConfig;
return this;
}
public Builder setAutoDumpHprofMode(DumpMode mode) {
mDefaultDumpHprofMode = mode;
return this;
}
public Builder setDetectDebuger(boolean enabled) {
mDetectDebugger = true;
return this;
}
public Builder setManualDumpTargetActivity(String targetActivity) {
mTargetActivity = targetActivity;
return this;
}
public ResourceConfig build() {
return new ResourceConfig(dynamicConfig, mDefaultDumpHprofMode, mDetectDebugger, mTargetActivity);
}
}
}
public class SharePluginInfo {
//plugin名字
public static final String TAG_PLUGIN = "memory";
//Hprof压缩文件位置?
public static final String ISSUE_RESULT_PATH = "resultZipPath";
//dump的模式
public static final String ISSUE_DUMP_MODE = "dump_mode";
//泄漏的activity的名字
public static final String ISSUE_ACTIVITY_NAME = "activity";
//泄漏的acitivity的唯一key
public static final String ISSUE_REF_KEY = "ref_key";
//refChain gc链
public static final String ISSUE_LEAK_DETAIL = "leak_detail";
//花了多长时间,dump+分析的消耗时间
public static final String ISSUE_COST_MILLIS = "cost_millis";
//进程名字
public static final String ISSUE_LEAK_PROCESS = "leak_process";
//没用到
public static final String ISSUE_NOTIFICATION_ID = "notification_id";
public static final class IssueType {
public static final int LEAK_FOUND = 0;
public static final int ERR_FILE_NOT_FOUND = 2;
public static final int ERR_ANALYSE_OOM = 3;
}
}
/**
* ActivityRefWatcher继承于FilePublisher,可以将检测出来的泄漏activity保存到SharedPreferences里,设置了一天过期日期
*/
public class ActivityRefWatcher extends FilePublisher implements Watcher {
private static final String TAG = "Matrix.ActivityRefWatcher";
private static final int CREATED_ACTIVITY_COUNT_THRESHOLD = 1;
//文件过期时间一天,FilePublisher
private static final long FILE_CONFIG_EXPIRED_TIME_MILLIS = TimeUnit.DAYS.toMillis(1);
//为每个destory activity设置的唯一key的前缀
private static final String ACTIVITY_REFKEY_PREFIX = "MATRIX_RESCANARY_REFKEY_";
//ResourcePlugin
private final ResourcePlugin mResourcePlugin;
private final RetryableTaskExecutor mDetectExecutor;//定位泄漏的Executor,重复循环检测
private final int mMaxRedetectTimes;//几次gc之后才确定是否属于泄漏了,10次
private final long mBgScanTimes;//在后台的时候20分钟扫描一次
private final long mFgScanTimes;//在前台的时候1分钟扫描一次
//处理线程
private final HandlerThread mHandlerThread;
//延时handler
private final Handler mHandler;
//存放destory的activity的链表
private final ConcurrentLinkedQueue<DestroyedActivityInfo> mDestroyedActivityInfos;
private final BaseLeakProcessor mLeakProcessor;//检测到泄漏了,处理程序
//dump模式
private final ResourceConfig.DumpMode mDumpHprofMode;
public static class ComponentFactory {//工厂
//创建循环重复检测的执行器,传入循环时间,后台线程
protected RetryableTaskExecutor createDetectExecutor(ResourceConfig config, HandlerThread handlerThread) {
return new RetryableTaskExecutor(config.getScanIntervalMillis(), handlerThread);
}
//发生泄漏后,创建处理器
protected BaseLeakProcessor createCustomLeakProcessor(ResourceConfig.DumpMode dumpMode, ActivityRefWatcher watcher) {
return null;
}
private BaseLeakProcessor createLeakProcess(ResourceConfig.DumpMode dumpMode, ActivityRefWatcher watcher) {
BaseLeakProcessor leakProcessor = createCustomLeakProcessor(dumpMode, watcher);
if (leakProcessor != null) {
return leakProcessor;
}
//根据模式,创建处理器
switch (dumpMode) {
case AUTO_DUMP:
return new AutoDumpProcessor(watcher);
case MANUAL_DUMP:
//manual会让用户跳到一个activity里处理
return new ManualDumpProcessor(watcher, watcher.getResourcePlugin().getConfig().getTargetActivity());
case SILENCE_ANALYSE:
return new SilenceAnalyseProcessor(watcher);
case NO_DUMP:
default:
return new NoDumpProcessor(watcher);
}
}
}
public ActivityRefWatcher(Application app,
final ResourcePlugin resourcePlugin) {
this(app, resourcePlugin, new ComponentFactory());
}
private ActivityRefWatcher(Application app,
ResourcePlugin resourcePlugin,
ComponentFactory componentFactory) {
super(app, FILE_CONFIG_EXPIRED_TIME_MILLIS, resourcePlugin.getTag(), resourcePlugin);
this.mResourcePlugin = resourcePlugin;
final ResourceConfig config = resourcePlugin.getConfig();//动态配置
//todo MatrixHandlerThread使用
mHandlerThread = MatrixHandlerThread.getNewHandlerThread("matrix_res", Thread.NORM_PRIORITY); // avoid blocking default matrix thread
mHandler = new Handler(mHandlerThread.getLooper());//创建一个单独线程,调度检测任务
mDumpHprofMode = config.getDumpHprofMode();//dump模式
mBgScanTimes = config.getBgScanIntervalMillis();//后台检测时间20分钟
mFgScanTimes = config.getScanIntervalMillis();//前台检测时间1分钟
mDetectExecutor = componentFactory.createDetectExecutor(config, mHandlerThread);//工厂创建调度任务器
mMaxRedetectTimes = config.getMaxRedetectTimes();//发现几次就说明泄漏了
mLeakProcessor = componentFactory.createLeakProcess(mDumpHprofMode, this);//泄漏之后处理器
mDestroyedActivityInfos = new ConcurrentLinkedQueue<>();//链表保存泄漏acitvity结构体
}
public void onForeground(boolean isForeground) {
if (isForeground) {
MatrixLog.i(TAG, "we are in foreground, modify scan time[%sms].", mFgScanTimes);
mDetectExecutor.clearTasks();
mDetectExecutor.setDelayMillis(mFgScanTimes);
mDetectExecutor.executeInBackground(mScanDestroyedActivitiesTask);
} else {
MatrixLog.i(TAG, "we are in background, modify scan time[%sms].", mBgScanTimes);
mDetectExecutor.setDelayMillis(mBgScanTimes);
}
}
private final Application.ActivityLifecycleCallbacks mRemovedActivityMonitor = new ActivityLifeCycleCallbacksAdapter() {
@Override
public void onActivityDestroyed(Activity activity) {
//记录已被destory的Activity
pushDestroyedActivityInfo(activity);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
triggerGc();
}
}, 2000);
}
};
@Override
public void start() {
stopDetect();
final Application app = mResourcePlugin.getApplication();
if (app != null) {
//注册监听
app.registerActivityLifecycleCallbacks(mRemovedActivityMonitor);
//开始检测
scheduleDetectProcedure();
MatrixLog.i(TAG, "watcher is started.");
}
}
@Override
public void stop() {
stopDetect();
MatrixLog.i(TAG, "watcher is stopped.");
}
private void stopDetect() {
final Application app = mResourcePlugin.getApplication();
if (app != null) {
//停止注册activity生命周期监听
app.unregisterActivityLifecycleCallbacks(mRemovedActivityMonitor);
//关掉检测
unscheduleDetectProcedure();
}
}
@Override
public void destroy() {
//停止检测
mDetectExecutor.quit();
//释放线程
mHandlerThread.quitSafely();
//释放处理器
mLeakProcessor.onDestroy();
MatrixLog.i(TAG, "watcher is destroyed.");
}
private void pushDestroyedActivityInfo(Activity activity) {
final String activityName = activity.getClass().getName();
//在NO_DUMP,AUTO_DUMP模式下,为什么?这两个模式下才执行以下逻辑,轻量级模式
if ((mDumpHprofMode == ResourceConfig.DumpMode.NO_DUMP || mDumpHprofMode == ResourceConfig.DumpMode.AUTO_DUMP)
&& !mResourcePlugin.getConfig().getDetectDebugger()
&& isPublished(activityName)) {//该Activity确认存在泄漏,已经上报了就return
MatrixLog.i(TAG, "activity leak with name %s had published, just ignore", activityName);
return;
}
final UUID uuid = UUID.randomUUID();
final StringBuilder keyBuilder = new StringBuilder();
//生成Activity实例的唯一标识
keyBuilder.append(ACTIVITY_REFKEY_PREFIX).append(activityName)
.append('_').append(Long.toHexString(uuid.getMostSignificantBits())).append(Long.toHexString(uuid.getLeastSignificantBits()));
final String key = keyBuilder.toString();
//构造一个数据结构,表示一个已被destroy的Activity
final DestroyedActivityInfo destroyedActivityInfo
= new DestroyedActivityInfo(key, activity, activityName);
//放入后续待检测的Activity list
mDestroyedActivityInfos.add(destroyedActivityInfo);
synchronized (mDestroyedActivityInfos) {
mDestroyedActivityInfos.notifyAll();
}
MatrixLog.d(TAG, "mDestroyedActivityInfos add %s", activityName);
}
/**
* 将mScanDestroyedActivitiesTask放到mDetectExecutor执行
*/
private void scheduleDetectProcedure() {
mDetectExecutor.executeInBackground(mScanDestroyedActivitiesTask);
}
/**
* mDetectExecutor是一个可以重复执行的
*/
private void unscheduleDetectProcedure() {
// mDetectExecutor是一个可以重复执行的,清空它里边的消息
mDetectExecutor.clearTasks();
//清空mDestroyedActivityInfos链表
mDestroyedActivityInfos.clear();
}
/**
* 可以重复执行的任务,execute返回RETRY,handler会将其重新发送到消息队列里,在一个后台线程里调用
*/
private final RetryableTask mScanDestroyedActivitiesTask = new RetryableTask() {
@Override
public Status execute() {
// If destroyed activity list is empty, just wait to save power.
//链表为空,说明destroy的activity为空,等待
if (mDestroyedActivityInfos.isEmpty()) {
MatrixLog.i(TAG, "DestroyedActivityInfo is empty! wait...");
synchronized (mDestroyedActivityInfos) {
try {
mDestroyedActivityInfos.wait();
} catch (Throwable ignored) {
// Ignored.
}
}
MatrixLog.i(TAG, "DestroyedActivityInfo is NOT empty! resume check");
return Status.RETRY;
}
// Fake leaks will be generated when debugger is attached.
//Debug调试模式,检测可能失效,直接return
if (Debug.isDebuggerConnected() && !mResourcePlugin.getConfig().getDetectDebugger()) {
MatrixLog.w(TAG, "debugger is connected, to avoid fake result, detection was delayed.");
return Status.RETRY;
}
// final WeakReference<Object[]> sentinelRef = new WeakReference<>(new Object[1024 * 1024]); // alloc big object
triggerGc();
triggerGc();
triggerGc();
// if (sentinelRef.get() != null) {
// // System ignored our gc request, we will retry later.
// MatrixLog.d(TAG, "system ignore our gc request, wait for next detection.");
// return Status.RETRY;
// }
final Iterator<DestroyedActivityInfo> infoIt = mDestroyedActivityInfos.iterator();
while (infoIt.hasNext()) {
//从链表里循环取出已经destroy的DestroyedActivityInfo信息
final DestroyedActivityInfo destroyedActivityInfo = infoIt.next();
//如果NO_DUMP AUTO_DUMP为什么这两个模式?才isPublished检测?
if ((mDumpHprofMode == ResourceConfig.DumpMode.NO_DUMP || mDumpHprofMode == ResourceConfig.DumpMode.AUTO_DUMP)
&& !mResourcePlugin.getConfig().getDetectDebugger()
&& isPublished(destroyedActivityInfo.mActivityName)) {//已经检查过了
MatrixLog.v(TAG, "activity with key [%s] was already published.", destroyedActivityInfo.mActivityName);
infoIt.remove();
continue;
}
triggerGc();
//为null的画,说明回收了,将其删除
if (destroyedActivityInfo.mActivityRef.get() == null) {
// The activity was recycled by a gc triggered outside.
MatrixLog.v(TAG, "activity with key [%s] was already recycled.", destroyedActivityInfo.mKey);
infoIt.remove();
continue;
}
//如果没回收,将其发现次数增加
++destroyedActivityInfo.mDetectedCount;
//如果发现次数小于10次的画,检查下一个
if (destroyedActivityInfo.mDetectedCount < mMaxRedetectTimes
&& !mResourcePlugin.getConfig().getDetectDebugger()) {
// Although the sentinel tell us the activity should have been recycled,
// system may still ignore it, so try again until we reach max retry times.
MatrixLog.i(TAG, "activity with key [%s] should be recycled but actually still exists in %s times, wait for next detection to confirm.",
destroyedActivityInfo.mKey, destroyedActivityInfo.mDetectedCount);
triggerGc();
continue;
}
MatrixLog.i(TAG, "activity with key [%s] was suspected to be a leaked instance. mode[%s]", destroyedActivityInfo.mKey, mDumpHprofMode);
//发现次数大于10次了,如果处理器为null,报空指针
if (mLeakProcessor == null) {
throw new NullPointerException("LeakProcessor not found!!!");
}
triggerGc();
//处理器处理泄漏的activiy
if (mLeakProcessor.process(destroyedActivityInfo)) {
MatrixLog.i(TAG, "the leaked activity [%s] with key [%s] has been processed. stop polling", destroyedActivityInfo.mActivityName, destroyedActivityInfo.mKey);
infoIt.remove();
}
}
triggerGc();
return Status.RETRY;
}
};
/**
* 返回根据dump模式,返回不同处理器
* @return
*/
public BaseLeakProcessor getLeakProcessor() {
return mLeakProcessor;
}
/**
* 返回ResourcePlugin
* @return
*/
public ResourcePlugin getResourcePlugin() {
return mResourcePlugin;
}
/**
* 返回DestroyedActivityInfo链表
*
* @return
*/
public Collection<DestroyedActivityInfo> getDestroyedActivityInfos() {
return mDestroyedActivityInfos;
}
/**
* triggerGc的时候
*/
public void triggerGc() {
MatrixLog.v(TAG, "triggering gc...");
//1.调用gc
Runtime.getRuntime().gc();
//2.睡眠100毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
MatrixLog.printErrStackTrace(TAG, e, "");
}
//3.运行Finalization方法
Runtime.getRuntime().runFinalization();
MatrixLog.v(TAG, "gc was triggered.");
}
}
3.FilePublisher可以将activity名字和发生泄漏的时间保存到SharedPreferences,设置了一天的过期日期
public class FilePublisher extends IssuePublisher {
private static final String TAG = "Matrix.FilePublisher";
private final long mExpiredTime;//一天过期日期
private final SharedPreferences.Editor mEditor;
private final HashMap<String, Long> mPublishedMap;//activity名字和发生泄漏的时间
private final Context mContext;
public FilePublisher(Context context, long expire, String tag, OnIssueDetectListener issueDetectListener) {
super(issueDetectListener);
this.mContext = context;
mExpiredTime = expire;//1天过期日期
SharedPreferences sharedPreferences = context.getSharedPreferences(tag + MatrixUtil.getProcessName(context), Context.MODE_PRIVATE);
mPublishedMap = new HashMap<>();//activity名字和发生泄漏的时间
long current = System.currentTimeMillis();
mEditor = sharedPreferences.edit();
HashSet<String> spKeys = null;
if (null != sharedPreferences.getAll()) {
spKeys = new HashSet<>(sharedPreferences.getAll().keySet());
}
if (null != spKeys) {
for (String key : spKeys) {
long start = sharedPreferences.getLong(key, 0);
long costTime = current - start;
if (start <= 0 || costTime > mExpiredTime) {
mEditor.remove(key);//超时则删除
} else {
mPublishedMap.put(key, start);//没有的话,添加到map里
}
}
}
if (null != mEditor) {
mEditor.apply();
}
}
public void markPublished(String key, boolean persist) {
if (key == null) {
return;
}
if (mPublishedMap.containsKey(key)) {//如果含有则返回
return;
}
final long now = System.currentTimeMillis();
mPublishedMap.put(key, now);
//如果保存文件里则保存到SharedPreferences里
if (persist) {
SharedPreferences.Editor e = mEditor.putLong(key, now);
if (null != e) {
e.apply();
}
}
}
@Override
public void markPublished(String key) {
markPublished(key, true);
}
@Override
public void unMarkPublished(String key) {
if (key == null) {
return;
}
if (!mPublishedMap.containsKey(key)) {
return;
}
mPublishedMap.remove(key);
SharedPreferences.Editor e = mEditor.remove(key);
if (null != e) {
e.apply();
}
}
@Override
public boolean isPublished(String key) {
if (!mPublishedMap.containsKey(key)) {
return false;
}
long start = mPublishedMap.get(key);
//时间判断,超过1天删除,并返回false
if (start <= 0 || (System.currentTimeMillis() - start) > mExpiredTime) {
SharedPreferences.Editor e = mEditor.remove(key);
if (null != e) {
e.apply();
}
mPublishedMap.remove(key);
return false;
}
return true;
}
public Context getContext() {
return mContext;
}
}
4.RetryableTaskExecutor再次执行某个任务的执行器,如果RetryableTask execute返回的是RETRY则重新执行,用在循环定时检测有没有内存泄漏
//再次执行某个任务的执行器
public class RetryableTaskExecutor {
//后台handler,将task发送到后台线程执行
private final Handler mBackgroundHandler;
//主线程handler,将task发送到主线程执行
private final Handler mMainHandler;
//执行时间delay时间
private long mDelayMillis;
public RetryableTaskExecutor(long delayMillis, HandlerThread handleThread) {
mBackgroundHandler = new Handler(handleThread.getLooper());
mMainHandler = new Handler(Looper.getMainLooper());
mDelayMillis = delayMillis;
}
//设置delay时间
public void setDelayMillis(long delayed) {
this.mDelayMillis = delayed;
}
//没有用
public void executeInMainThread(final RetryableTask task) {
postToMainThreadWithDelay(task, 0);
}
//发送到后台线程执行任务
public void executeInBackground(final RetryableTask task) {
postToBackgroundWithDelay(task, 0);
}
//清空任务
public void clearTasks() {
mBackgroundHandler.removeCallbacksAndMessages(null);
mMainHandler.removeCallbacksAndMessages(null);
}
//丢弃
public void quit() {
clearTasks();
}
//没有用
private void postToMainThreadWithDelay(final RetryableTask task, final int failedAttempts) {
mMainHandler.postDelayed(new Runnable() {
@Override
public void run() {
RetryableTask.Status status = task.execute();
if (status == RetryableTask.Status.RETRY) {
postToMainThreadWithDelay(task, failedAttempts + 1);
}
}
}, mDelayMillis);
}
/**
* 发送任务到后台线程
* @param task 代表任务,它的execute返回RETRY的时候,还需将其再次发送任务到后台线程,再次执行
* @param failedAttempts 传入失败次数
*/
private void postToBackgroundWithDelay(final RetryableTask task, final int failedAttempts) {
mBackgroundHandler.postDelayed(new Runnable() {
@Override
public void run() {
RetryableTask.Status status = task.execute();
if (status == RetryableTask.Status.RETRY) {
postToBackgroundWithDelay(task, failedAttempts + 1);
}
}
}, mDelayMillis);
}
/**
* 代表可以执行再次执行的任务
*/
public interface RetryableTask {
// 如果返回了RETRY,将其再次发送到线程执行
Status execute();
enum Status {
DONE, RETRY
}
}
}
5.BaseLeakProcessor 基础处理类
public class AndroidHeapDumper {
private static final String TAG = "Matrix.AndroidHeapDumper";
private final Context mContext;
//文件相关
private final DumpStorageManager mDumpStorageManager;
//主线程handler
private final Handler mMainHandler;
public AndroidHeapDumper(Context context, DumpStorageManager dumpStorageManager) {
this(context, dumpStorageManager, new Handler(Looper.getMainLooper()));
}
public AndroidHeapDumper(Context context, DumpStorageManager dumpStorageManager, Handler mainHandler) {
mContext = context;
mDumpStorageManager = dumpStorageManager;
mMainHandler = mainHandler;
}
//dump方法
public File dumpHeap(boolean isShowToast) {
//得到hprof文件
final File hprofFile = mDumpStorageManager.newHprofFile();
if (null == hprofFile) {
MatrixLog.w(TAG, "hprof file is null.");
return null;
}
final File hprofDir = hprofFile.getParentFile();
if (hprofDir == null) {
MatrixLog.w(TAG, "hprof file path: %s does not indicate a full path.", hprofFile.getAbsolutePath());
return null;
}
if (!hprofDir.canWrite()) {
MatrixLog.w(TAG, "hprof file path: %s cannot be written.", hprofFile.getAbsolutePath());
return null;
}
//1.5G 小于1.5G 空间返回
if (hprofDir.getFreeSpace() < 1.5 * 1024 * 1024 * 1024) {
MatrixLog.w(TAG, "hprof file path: %s free space not enough", hprofDir.getAbsolutePath());
return null;
}
//isShowToast 在AutoDumpProcessor模式会弹窗
if (isShowToast) {
final FutureResult<Toast> waitingForToast = new FutureResult<>();
showToast(waitingForToast);
if (!waitingForToast.wait(5, TimeUnit.SECONDS)) {//这里是等toast
MatrixLog.w(TAG, "give up dumping heap, waiting for toast too long.");
return null;
}
try {
//这里是真正dump的地方
Debug.dumpHprofData(hprofFile.getAbsolutePath());
cancelToast(waitingForToast.get());
return hprofFile;
} catch (Exception e) {
MatrixLog.printErrStackTrace(TAG, e, "failed to dump heap into file: %s.", hprofFile.getAbsolutePath());
return null;
}
} else {
try {
//这里是真正dump的地方
Debug.dumpHprofData(hprofFile.getAbsolutePath());
return hprofFile;
} catch (Exception e) {
MatrixLog.printErrStackTrace(TAG, e, "failed to dump heap into file: %s.", hprofFile.getAbsolutePath());
return null;
}
}
}
private void showToast(final FutureResult<Toast> waitingForToast) {
mMainHandler.post(new Runnable() {
@Override
public void run() {
final Toast toast = new Toast(mContext);
toast.setDuration(Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
LayoutInflater inflater = LayoutInflater.from(mContext);
toast.setView(inflater.inflate(R.layout.resource_canary_toast_wait_for_heapdump, null));
toast.show();
// Waiting for Idle to make sure Toast gets rendered.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {//等toast花了点时间
waitingForToast.set(toast);
return false;
}
});
}
});
}
private void cancelToast(final Toast toast) {
mMainHandler.post(new Runnable() {
@Override
public void run() {
toast.cancel();
}
});
}
public interface HeapDumpHandler {
void process(HeapDump result);
}
}
public class DumpStorageManager {
private static final String TAG = "Matrix.DumpStorageManager";
//后缀
public static final String HPROF_EXT = ".hprof";
//最多五个缓存的hprof
public static final int DEFAULT_MAX_STORED_HPROF_FILECOUNT = 5;
protected final Context mContext;
//最多缓存的hprof数量
protected final int mMaxStoredHprofFileCount;
public DumpStorageManager(Context context) {
this(context, DEFAULT_MAX_STORED_HPROF_FILECOUNT);
}
public DumpStorageManager(Context context, int maxStoredHprofFileCount) {
if (maxStoredHprofFileCount <= 0) {
throw new IllegalArgumentException("illegal max stored hprof file count: "
+ maxStoredHprofFileCount);
}
mContext = context;
mMaxStoredHprofFileCount = maxStoredHprofFileCount;
}
//新建一个hprof
public File newHprofFile() {
final File storageDir = prepareStorageDirectory();
if (storageDir == null) {
return null;
}
// 进程名+进程id+时间+后缀
final String hprofFileName = "dump"
+ "_" + MatrixUtil.getProcessName(mContext).replace(".", "_").replace(":", "_")
+ "_" + Process.myPid()
+ "_"
+ new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(new Date())
+ HPROF_EXT;
return new File(storageDir, hprofFileName);
}
//创建文件夹
private File prepareStorageDirectory() {
final File storageDir = getStorageDirectory();
if (!storageDir.exists() && (!storageDir.mkdirs() || !storageDir.canWrite())) {
MatrixLog.w(TAG, "failed to allocate new hprof file since path: %s is not writable.",
storageDir.getAbsolutePath());
return null;
}
final File[] hprofFiles = storageDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(HPROF_EXT);
}
});
if (hprofFiles != null && hprofFiles.length > mMaxStoredHprofFileCount) {
for (File file : hprofFiles) {
if (file.exists() && !file.delete()) {
MatrixLog.w(TAG, "faile to delete hprof file: " + file.getAbsolutePath());
}
}
}
return storageDir;
}
//获取存储位置
private File getStorageDirectory() {
final String sdcardState = Environment.getExternalStorageState();
File root = null;
if (Environment.MEDIA_MOUNTED.equals(sdcardState)) {
root = mContext.getExternalCacheDir();
} else {
root = mContext.getCacheDir();
}
final File result = new File(root, "matrix_resource");
MatrixLog.i(TAG, "path to store hprof and result: %s", result.getAbsolutePath());
return result;
}
}
public abstract class BaseLeakProcessor {
private static final String TAG = "Matrix.LeakProcessor.Base";
//检测泄漏
private final ActivityRefWatcher mWatcher;
//dump文件相关
private DumpStorageManager mDumpStorageManager;
//dump的类
private AndroidHeapDumper mHeapDumper;
//dump完了回调
private AndroidHeapDumper.HeapDumpHandler mHeapDumpHandler;
public BaseLeakProcessor(ActivityRefWatcher watcher) {
mWatcher = watcher;
}
public abstract boolean process(DestroyedActivityInfo destroyedActivityInfo);
public DumpStorageManager getDumpStorageManager() {
if (mDumpStorageManager == null) {
mDumpStorageManager = new DumpStorageManager(mWatcher.getContext());
}
return mDumpStorageManager;
}
public AndroidHeapDumper getHeapDumper() {
if (mHeapDumper == null) {
mHeapDumper = new AndroidHeapDumper(mWatcher.getContext(), getDumpStorageManager());
}
return mHeapDumper;
}
protected AndroidHeapDumper.HeapDumpHandler getHeapDumpHandler() {
if (mHeapDumpHandler == null) {
mHeapDumpHandler = new AndroidHeapDumper.HeapDumpHandler() {
@Override
public void process(HeapDump result) {
//裁剪Hprof并上传,process流程最终调用CanaryWorkerService进行裁剪和上报
CanaryWorkerService.shrinkHprofAndReport(mWatcher.getContext(), result);
}
};
}
return mHeapDumpHandler;
}
public ActivityRefWatcher getWatcher() {
return mWatcher;
}
public void onDestroy() {
}
/**
* todo
* SilenceAnalyseProcessor 调用了
* ManualDumpProcessor调用了
* @param hprofFile
* @param referenceKey
* @return
*/
protected ActivityLeakResult analyze(File hprofFile, String referenceKey) {
final HeapSnapshot heapSnapshot;
ActivityLeakResult result;
final ExcludedRefs excludedRefs = AndroidExcludedRefs.createAppDefaults(Build.VERSION.SDK_INT, Build.MANUFACTURER).build();
try {
heapSnapshot = new HeapSnapshot(hprofFile);
//todo 这里是这个函数的主要逻辑,使用ActivityLeakAnalyzer分析heapSnapshot,返回ActivityLeakResult
result = new ActivityLeakAnalyzer(referenceKey, excludedRefs).analyze(heapSnapshot);
} catch (IOException e) {
result = ActivityLeakResult.failure(e, 0);
}
return result;
}
final protected void publishIssue(int issueType, ResourceConfig.DumpMode dumpMode, String activity, String refKey, String detail, String cost) {
Issue issue = new Issue(issueType);
JSONObject content = new JSONObject();
try {
content.put(SharePluginInfo.ISSUE_DUMP_MODE, dumpMode.name());
content.put(SharePluginInfo.ISSUE_ACTIVITY_NAME, activity);
content.put(SharePluginInfo.ISSUE_REF_KEY, refKey);
content.put(SharePluginInfo.ISSUE_LEAK_DETAIL, detail);
content.put(SharePluginInfo.ISSUE_COST_MILLIS, cost);
} catch (JSONException jsonException) {
MatrixLog.printErrStackTrace(TAG, jsonException, "");
}
issue.setContent(content);
//todo matrix lib框架的功能
getWatcher().getResourcePlugin().onDetectIssue(issue);
}
}
6.NoDumpProcessor 可以用于线上的只上报名字
public class NoDumpProcessor extends BaseLeakProcessor {
private static final String TAG = "Matrix.LeakProcessor.NoDump";
public NoDumpProcessor(ActivityRefWatcher watcher) {
super(watcher);
}
@Override
public boolean process(DestroyedActivityInfo destroyedActivityInfo) {
// Lightweight mode, just report leaked activity name.
MatrixLog.i(TAG, "lightweight mode, just report leaked activity name.");
//标记泄漏了
getWatcher().markPublished(destroyedActivityInfo.mActivityName);
getWatcher().triggerGc();
//上报问题
publishIssue(SharePluginInfo.IssueType.LEAK_FOUND, ResourceConfig.DumpMode.NO_DUMP, destroyedActivityInfo.mActivityName, destroyedActivityInfo.mKey, "no dump", "0");
return true;
}
}
7.SilenceAnalyseProcessor在熄屏的时候处理
public class SilenceAnalyseProcessor extends BaseLeakProcessor {
private static final String TAG = "Matrix.LeakProcessor.SilenceAnalyse";
private final BroadcastReceiver mReceiver;
private boolean isScreenOff;
private boolean isProcessing;
public SilenceAnalyseProcessor(ActivityRefWatcher watcher) {
super(watcher);
//注册监听
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//设置标记位
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
isScreenOff = true;
MatrixLog.i(TAG, "[ACTION_SCREEN_OFF]");
} else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
isScreenOff = false;
MatrixLog.i(TAG, "[ACTION_SCREEN_ON]");
}
}
};
try {
watcher.getResourcePlugin().getApplication().registerReceiver(mReceiver, filter);
} catch (Throwable e) {
MatrixLog.printErrStackTrace(TAG, e, "");
}
}
@Override
public boolean process(DestroyedActivityInfo destroyedActivityInfo) {
//处理方法
return onLeak(destroyedActivityInfo.mActivityName, destroyedActivityInfo.mKey);
}
@Override
public void onDestroy() {
MatrixLog.i(TAG, "onDestroy: unregister receiver");
//解除注册
getWatcher().getResourcePlugin().getApplication().unregisterReceiver(mReceiver);
}
private boolean onLeak(final String activity, final String refString) {
MatrixLog.i(TAG, "[onLeak] activity=%s isScreenOff=%s isProcessing=%s", activity, isScreenOff, isProcessing);
//leak监听过了
if (getWatcher().isPublished(activity)) {
MatrixLog.i(TAG, "this activity has been dumped! %s", activity);
return true;
}
if (!isProcessing && isScreenOff) {
isProcessing = true;
getWatcher().triggerGc();
boolean res = dumpAndAnalyse(activity, refString);
if (res) {
//又将leak删除了,因为在silent模式下不支持isPublished,在ActivityRefWatcher里只有NO_DUMP,AUTO_DUMP才支持
getWatcher().markPublished(activity, false);
}
isProcessing = false;
return res;
}
return false;
}
// todo analyse
private boolean dumpAndAnalyse(String activity, String refString) {
long dumpBegin = System.currentTimeMillis();
File file = getHeapDumper().dumpHeap(false);
if (file == null || file.length() <= 0) {
publishIssue(SharePluginInfo.IssueType.ERR_FILE_NOT_FOUND, activity, refString, "file is null", "0");
MatrixLog.e(TAG, "file is null!");
return true;
}
MatrixLog.i(TAG, String.format("dump cost=%sms refString=%s path=%s",
System.currentTimeMillis() - dumpBegin, refString, file.getAbsolutePath()));
long analyseBegin = System.currentTimeMillis();
try {
// todo analyse 这里分析了
final ActivityLeakResult result = analyze(file, refString);
MatrixLog.i(TAG, String.format("analyze cost=%sms refString=%s",
System.currentTimeMillis() - analyseBegin, refString));
String refChain = result.toString();
if (result.mLeakFound) {
//这里分析了
publishIssue(SharePluginInfo.IssueType.LEAK_FOUND, activity, refString, refChain, String.valueOf(
System.currentTimeMillis() - dumpBegin));
MatrixLog.i(TAG, refChain);
} else {
MatrixLog.i(TAG, "leak not found");
}
} catch (OutOfMemoryError error) {
publishIssue(SharePluginInfo.IssueType.ERR_ANALYSE_OOM, activity, refString, "OutOfMemoryError", "0");
MatrixLog.printErrStackTrace(TAG, error.getCause(), "");
} finally {
file.delete();
}
return true;
}
private void publishIssue(int issueType, String activity, String refKey, String detail, String cost) {
publishIssue(issueType, ResourceConfig.DumpMode.SILENCE_ANALYSE, activity, refKey, detail, cost);
}
}
8.ManualDumpProcessor跳转到一个activity处理
/**
* X process leaked -> send notification -> main process activity
* -> dump and analyse in X process -> show result in main process activity
* Created by Yves on 2021/3/4
*/
public class ManualDumpProcessor extends BaseLeakProcessor {
private static final String TAG = "Matrix.LeakProcessor.ManualDump";
private static final int NOTIFICATION_ID = 0x110;
private final String mTargetActivity;
private final NotificationManager mNotificationManager;
private final List<DestroyedActivityInfo> mLeakedActivities = new ArrayList<>();
private boolean isMuted;
public ManualDumpProcessor(ActivityRefWatcher watcher, String targetActivity) {
super(watcher);
//处理activity
mTargetActivity = targetActivity;
//notification
mNotificationManager = (NotificationManager) watcher.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
//注册BroadcastReceiver
ManualDumpProcessorHelper.install(watcher.getContext(), this);
}
@Override
public boolean process(DestroyedActivityInfo destroyedActivityInfo) {
final Context context = getWatcher().getContext();
//gc了以下
getWatcher().triggerGc();
//如果回收了返回true
if (destroyedActivityInfo.mActivityRef.get() == null) {
MatrixLog.v(TAG, "activity with key [%s] was already recycled.", destroyedActivityInfo.mKey);
return true;
}
//保存了下泄漏的信息
mLeakedActivities.add(destroyedActivityInfo);
MatrixLog.i(TAG, "show notification for activity leak. %s", destroyedActivityInfo.mActivityName);
//?todo 不知道干哈的
if (isMuted) {
MatrixLog.i(TAG, "is muted, won't show notification util process reboot");
return true;
}
//发送通知
//通知点击之后跳转的activity
Intent targetIntent = new Intent();
targetIntent.setClassName(getWatcher().getContext(), mTargetActivity);
targetIntent.putExtra(SharePluginInfo.ISSUE_ACTIVITY_NAME, destroyedActivityInfo.mActivityName);
targetIntent.putExtra(SharePluginInfo.ISSUE_REF_KEY, destroyedActivityInfo.mKey);
targetIntent.putExtra(SharePluginInfo.ISSUE_LEAK_PROCESS, MatrixUtil.getProcessName(context));
//点击之后的响应pendingintent
PendingIntent pIntent = PendingIntent.getActivity(context, 0, targetIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//title
String dumpingHeapTitle = context.getString(R.string.resource_canary_leak_tip);
ResourceConfig config = getWatcher().getResourcePlugin().getConfig();
//content
String dumpingHeapContent =
String.format(Locale.getDefault(), "[%s] has leaked for [%s]min!!!",
destroyedActivityInfo.mActivityName,
TimeUnit.MILLISECONDS.toMinutes(
config.getScanIntervalMillis() * config.getMaxRedetectTimes()));
Notification.Builder builder = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
builder = new Notification.Builder(context, getNotificationChannelIdCompat(context));
} else {
builder = new Notification.Builder(context);
}
builder.setContentTitle(dumpingHeapTitle)
.setPriority(Notification.PRIORITY_DEFAULT)
.setStyle(new Notification.BigTextStyle().bigText(dumpingHeapContent))
.setContentIntent(pIntent)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.setWhen(System.currentTimeMillis());
Notification notification = builder.build();
//发送通知
mNotificationManager.notify(
NOTIFICATION_ID + destroyedActivityInfo.mKey.hashCode(), notification);
//上报了问题
publishIssue(destroyedActivityInfo.mActivityName, destroyedActivityInfo.mKey);
MatrixLog.i(TAG, "shown notification!!!3");
return true;
}
//创建通知channel
private String getNotificationChannelIdCompat(Context context) {
if (SDK_INT >= Build.VERSION_CODES.O) {
String channelName = "com.tencent.matrix.resource.processor.ManualDumpProcessor";
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel notificationChannel = notificationManager.getNotificationChannel(channelName);
if (notificationChannel == null) {
MatrixLog.v(TAG, "create channel");
notificationChannel = new NotificationChannel(channelName, channelName, NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(notificationChannel);
}
return channelName;
}
return null;
}
public void setMuted(boolean mute) {
isMuted = mute;
}
/**
* run in leaked process
*
* @param activity
* @param refString
* @return ManualDumpData 包含dump文件+引用链
*/
private ManualDumpData dumpAndAnalyse(String activity, String refString) {
long dumpBegin = System.currentTimeMillis();
getWatcher().triggerGc();
File file = getHeapDumper().dumpHeap(false);
if (file == null || file.length() <= 0) {
// publishIssue(SharePluginInfo.IssueType.ERR_FILE_NOT_FOUND, activity, refString, "file is null", "0");
MatrixLog.e(TAG, "file is null!");
return null;
}
MatrixLog.i(TAG, String.format("dump cost=%sms refString=%s path=%s",
System.currentTimeMillis() - dumpBegin, refString, file.getAbsolutePath()));
long analyseBegin = System.currentTimeMillis();
try {
final ActivityLeakResult result = analyze(file, refString);
MatrixLog.i(TAG, String.format("analyze cost=%sms refString=%s",
System.currentTimeMillis() - analyseBegin, refString));
String refChain = result.toString();
if (result.mLeakFound) {
// publishIssue(SharePluginInfo.IssueType.LEAK_FOUND, activity, refString, refChain, String.valueOf(System.currentTimeMillis() - dumpBegin));
MatrixLog.i(TAG, "leakFound,refcChain = %s", refChain);
return new ManualDumpData(file.getAbsolutePath(), refChain);
} else {
MatrixLog.i(TAG, "leak not found");
return new ManualDumpData(file.getAbsolutePath(), null);
}
} catch (OutOfMemoryError error) {
// publishIssue(SharePluginInfo.IssueType.ERR_ANALYSE_OOM, activity, refString, "OutOfMemoryError", "0");
MatrixLog.printErrStackTrace(TAG, error.getCause(), "");
}
return null;
}
//单纯上报问题
private void publishIssue(String activity, String refKey) {
publishIssue(SharePluginInfo.IssueType.LEAK_FOUND, ResourceConfig.DumpMode.MANUAL_DUMP, activity, refKey, "manual_dump", "0");
}
/**
* multi process dump helper.
* 多进程dump帮组类
*/
public static class ManualDumpProcessorHelper extends BroadcastReceiver {
//permission
private static final String DUMP_PERMISSION_SUFFIX = ".manual.dump";
private static final String ACTION_DUMP = "com.tencent.matrix.manual.dump";
private static final String ACTION_RESULT = "com.tencent.matrix.manual.result";
private static final String KEY_RESULT_PROCESS = "result_process";
private static final String KEY_LEAK_ACTIVITY = "leak_activity";
private static final String KEY_LEAK_PROCESS = "leak_process";
private static final String KEY_LEAK_REFKEY = "leak_refkey";
private static final String KEY_HPROF_PATH = "hprof_path";
private static final String KEY_REF_CHAIN = "ref_chain";
//是否注册了BroadcastReceiver
private static boolean hasInstalled;
private static ManualDumpProcessor sProcessor;
private static IResultListener sListener; // only not null in process who called dumpAndAnalyse
@Override
public void onReceive(final Context context, final Intent intent) {
if (intent == null) {
MatrixLog.e(TAG, "intent is null");
return;
}
MatrixHandlerThread.getDefaultHandler().postAtFrontOfQueue(new Runnable() {
@Override
public void run() {
if (ACTION_DUMP.equals(intent.getAction())) {
//进程
String leakProcess = intent.getStringExtra(KEY_LEAK_PROCESS);
String currentProcess = MatrixUtil.getProcessName(context);
//如果是这个进程泄漏了
if (!currentProcess.equals(leakProcess)) {
MatrixLog.v(TAG, "ACTION_DUMP: current process [%s] is NOT leaked process [%s]", currentProcess, leakProcess);
return;
}
// leaked process
MatrixLog.v(TAG, "ACTION_DUMP: current process [%s] is leaked process [%s]", currentProcess, leakProcess);
String leakActivity = intent.getStringExtra(KEY_LEAK_ACTIVITY);
String refKey = intent.getStringExtra(KEY_LEAK_REFKEY);
//dump信息并分析
ManualDumpData data = sProcessor.dumpAndAnalyse(leakActivity, refKey);
Intent resultIntent = new Intent(ACTION_RESULT);
if (data != null) {
resultIntent.putExtra(KEY_HPROF_PATH, data.hprofPath);
resultIntent.putExtra(KEY_REF_CHAIN, data.refChain);
}
String resultProcess = intent.getStringExtra(KEY_RESULT_PROCESS);
resultIntent.putExtra(KEY_RESULT_PROCESS, resultProcess);
context.sendBroadcast(resultIntent,
context.getPackageName() + DUMP_PERMISSION_SUFFIX);
} else if (ACTION_RESULT.equals(intent.getAction())) {
// result process
final String resultProcess = intent.getStringExtra(KEY_RESULT_PROCESS);
final String currentProcess = MatrixUtil.getProcessName(context);
if (!currentProcess.equals(resultProcess)) {
MatrixLog.v(TAG, "ACTION_RESULT: current process [%s] is NOT result process [%s]", currentProcess, resultProcess);
return;
}
MatrixLog.v(TAG, "ACTION_RESULT: current process [%s] is result process [%s]", currentProcess, resultProcess);
// generally, sListener must be NOT null
if (sListener == null) {
throw new NullPointerException("result listener is null!!!");
}
final String hprofPath = intent.getStringExtra(KEY_HPROF_PATH);
if (hprofPath == null) {
sListener.onFailed();
return;
}
final String refChain = intent.getStringExtra(KEY_REF_CHAIN);
sListener.onSuccess(hprofPath, refChain);
sListener = null;
}
}
});
}
//注册BroadcastReceiver
private static void install(Context context, ManualDumpProcessor processor) {
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_DUMP);
filter.addAction(ACTION_RESULT);
final String dumpPermission = context.getPackageName() + DUMP_PERMISSION_SUFFIX;
//注册BroadcastReceiver
context.registerReceiver(new ManualDumpProcessorHelper(), filter, dumpPermission, null);
MatrixLog.d(TAG, "[%s] DUMP_PERMISSION is %s", MatrixUtil.getProcessName(context), dumpPermission);
hasInstalled = true;
sProcessor = processor;
}
//nouse
public static void dumpAndAnalyse(Context context, String leakProcess, String activity, String refKey, IResultListener resultListener) {
if (!hasInstalled) {
throw new IllegalStateException("ManualDumpProcessorHelper was not installed yet!!! maybe your target activity is not running in right process.");
}
final String currentProcess = MatrixUtil.getProcessName(context);
if (currentProcess.equalsIgnoreCase(leakProcess)) {
// dump and analyze for current process
ManualDumpData data = sProcessor.dumpAndAnalyse(activity, refKey);
if (data == null) {
resultListener.onFailed();
} else {
resultListener.onSuccess(data.hprofPath, data.refChain);
}
} else {
sListener = resultListener;
MatrixLog.v(TAG, "[%s] send broadcast with permission: %s", currentProcess,
context.getPackageName() + DUMP_PERMISSION_SUFFIX);
Intent intent = new Intent(ACTION_DUMP);
intent.putExtra(KEY_LEAK_PROCESS, leakProcess);
intent.putExtra(KEY_LEAK_ACTIVITY, activity);
intent.putExtra(KEY_LEAK_REFKEY, refKey);
intent.putExtra(KEY_RESULT_PROCESS, currentProcess);
context.sendBroadcast(intent, context.getPackageName() + DUMP_PERMISSION_SUFFIX);
}
}
}
public interface IResultListener {
void onSuccess(String hprof, String leakReference);
void onFailed();
}
/**
* hprof文件路径
* 链
*/
public static class ManualDumpData {
public final String hprofPath;
public final String refChain;
public ManualDumpData(String hprofPath, String refChain) {
this.hprofPath = hprofPath;
this.refChain = refChain;
}
}
}
9.AutoDumpProcessor 会在本地裁剪,然后上传裁剪后的hprof压缩,上报
public class AutoDumpProcessor extends BaseLeakProcessor {
private static final String TAG = "Matrix.LeakProcessor.AutoDump";
public AutoDumpProcessor(ActivityRefWatcher watcher) {
super(watcher);
}
@Override
public boolean process(DestroyedActivityInfo destroyedActivityInfo) {
// dump
final File hprofFile = getHeapDumper().dumpHeap(true);
if (hprofFile != null) {
//标记
getWatcher().markPublished(destroyedActivityInfo.mActivityName);
getWatcher().triggerGc();
//
final HeapDump heapDump = new HeapDump(hprofFile, destroyedActivityInfo.mKey, destroyedActivityInfo.mActivityName);
getHeapDumpHandler().process(heapDump);
} else {
MatrixLog.i(TAG, "heap dump for further analyzing activity with key [%s] was failed, just ignore.",
destroyedActivityInfo.mKey);
}
return true;
}
}
11.CanaryWorkerService裁剪
//裁剪hprof,并上报
public class CanaryWorkerService extends MatrixJobIntentService {
private static final String TAG = "Matrix.CanaryWorkerService";
private static final int JOB_ID = 0xFAFBFCFD;
private static final String ACTION_SHRINK_HPROF = "com.tencent.matrix.resource.worker.action.SHRINK_HPROF";
private static final String EXTRA_PARAM_HEAPDUMP = "com.tencent.matrix.resource.worker.param.HEAPDUMP";
//入口
public static void shrinkHprofAndReport(Context context, HeapDump heapDump) {
final Intent intent = new Intent(context, CanaryWorkerService.class);
intent.setAction(ACTION_SHRINK_HPROF);
intent.putExtra(EXTRA_PARAM_HEAPDUMP, heapDump);
enqueueWork(context, CanaryWorkerService.class, JOB_ID, intent);
}
@Override
protected void onHandleWork(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_SHRINK_HPROF.equals(action)) {
try {
intent.setExtrasClassLoader(this.getClassLoader());
final HeapDump heapDump = (HeapDump) intent.getSerializableExtra(EXTRA_PARAM_HEAPDUMP);
if (heapDump != null) {
//裁剪并上报
doShrinkHprofAndReport(heapDump);
} else {
MatrixLog.e(TAG, "failed to deserialize heap dump, give up shrinking and reporting.");
}
} catch (Throwable thr) {
MatrixLog.printErrStackTrace(TAG, thr, "failed to deserialize heap dump, give up shrinking and reporting.");
}
}
}
}
private void doShrinkHprofAndReport(HeapDump heapDump) {
final File hprofDir = heapDump.getHprofFile().getParentFile();
//裁剪之后的Hprof文件名
final File shrinkedHProfFile = new File(hprofDir, getShrinkHprofName(heapDump.getHprofFile()));
//压缩文件
final File zipResFile = new File(hprofDir, getResultZipName("dump_result_" + android.os.Process.myPid()));
final File hprofFile = heapDump.getHprofFile();
ZipOutputStream zos = null;
try {
long startTime = System.currentTimeMillis();
//执行Hprof裁剪
new HprofBufferShrinker().shrink(hprofFile, shrinkedHProfFile);
MatrixLog.i(TAG, "shrink hprof file %s, size: %dk to %s, size: %dk, use time:%d",
hprofFile.getPath(), hprofFile.length() / 1024, shrinkedHProfFile.getPath(), shrinkedHProfFile.length() / 1024, (System.currentTimeMillis() - startTime));
//打成压缩包
zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipResFile)));
//记录一些设备信息
final ZipEntry resultInfoEntry = new ZipEntry("result.info");
//裁剪后的Hprof文件
final ZipEntry shrinkedHProfEntry = new ZipEntry(shrinkedHProfFile.getName());
zos.putNextEntry(resultInfoEntry);
final PrintWriter pw = new PrintWriter(new OutputStreamWriter(zos, StandardCharsets.UTF_8));
pw.println("# Resource Canary Result Infomation. THIS FILE IS IMPORTANT FOR THE ANALYZER !!");
pw.println("sdkVersion=" + Build.VERSION.SDK_INT);//系统版本
pw.println("manufacturer=" + Build.MANUFACTURER);//厂商信息
pw.println("hprofEntry=" + shrinkedHProfEntry.getName());//裁剪后Hprof文件名
pw.println("leakedActivityKey=" + heapDump.getReferenceKey());//泄漏Activity实例的key
pw.flush();
zos.closeEntry();
zos.putNextEntry(shrinkedHProfEntry);
copyFileToStream(shrinkedHProfFile, zos);
zos.closeEntry();
//原始数据删除
shrinkedHProfFile.delete();
hprofFile.delete();
MatrixLog.i(TAG, "process hprof file use total time:%d", (System.currentTimeMillis() - startTime));
//CanaryResultService执行上报逻辑
CanaryResultService.reportHprofResult(this, zipResFile.getAbsolutePath(), heapDump.getActivityName());
} catch (IOException e) {
MatrixLog.printErrStackTrace(TAG, e, "");
} finally {
closeQuietly(zos);
}
}
//返回压缩后的 _shrink.hprof
private String getShrinkHprofName(File origHprof) {
final String origHprofName = origHprof.getName();
final int extPos = origHprofName.indexOf(DumpStorageManager.HPROF_EXT);
final String namePrefix = origHprofName.substring(0, extPos);
return namePrefix + "_shrink" + DumpStorageManager.HPROF_EXT;
}
//返回zip todo 会不会重复
private String getResultZipName(String prefix) {
StringBuilder sb = new StringBuilder();
sb.append(prefix).append('_')
.append(new SimpleDateFormat("yyyyMMddHHmmss", Locale.ENGLISH).format(new Date()))
.append(".zip");
return sb.toString();
}
}
12.CanaryResultService发现问题了,上报
public class CanaryResultService extends MatrixJobIntentService {
private static final String TAG = "Matrix.CanaryResultService";
private static final int JOB_ID = 0xFAFBFCFE;
private static final String ACTION_REPORT_HPROF_RESULT = "com.tencent.matrix.resource.result.action.REPORT_HPROF_RESULT";
private static final String EXTRA_PARAM_RESULT_PATH = "RESULT_PATH";
private static final String EXTRA_PARAM_ACTIVITY = "RESULT_ACTIVITY";
//入口
public static void reportHprofResult(Context context, String resultPath, String activityName) {
final Intent intent = new Intent(context, CanaryResultService.class);
intent.setAction(ACTION_REPORT_HPROF_RESULT);
intent.putExtra(EXTRA_PARAM_RESULT_PATH, resultPath);
intent.putExtra(EXTRA_PARAM_ACTIVITY, activityName);
enqueueWork(context, CanaryResultService.class, JOB_ID, intent);
}
@Override
protected void onHandleWork(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_REPORT_HPROF_RESULT.equals(action)) {
final String resultPath = intent.getStringExtra(EXTRA_PARAM_RESULT_PATH);
final String activityName = intent.getStringExtra(EXTRA_PARAM_ACTIVITY);
if (resultPath != null && !resultPath.isEmpty()
&& activityName != null && !activityName.isEmpty()) {
//上报问题
doReportHprofResult(resultPath, activityName);
} else {
MatrixLog.e(TAG, "resultPath or activityName is null or empty, skip reporting.");
}
}
}
}
// notice: compatible
//上报问题
private void doReportHprofResult(String resultPath, String activityName) {
Issue issue = new Issue(SharePluginInfo.IssueType.LEAK_FOUND);
final JSONObject resultJson = new JSONObject();
try {
resultJson.put(SharePluginInfo.ISSUE_RESULT_PATH, resultPath);
resultJson.put(SharePluginInfo.ISSUE_ACTIVITY_NAME, activityName);
issue.setContent(resultJson);
} catch (Throwable thr) {
MatrixLog.printErrStackTrace(TAG, thr, "unexpected exception, skip reporting.");
}
Plugin plugin = Matrix.with().getPluginByClass(ResourcePlugin.class);
if (plugin != null) {
plugin.onDetectIssue(issue);
}
}
}
|