简介
Resource Canary主要是用来检测Activit级别的内存泄漏、以及重复创建的冗余Bitmap。
地址
使用
1. 项目依赖
- 在项目根目录下的 gradle.properties中配置要依赖的Matrix版本号
MATRIX_VERSION=2.0.5
- 在项目根目录下的build.gradle文件添加Matrix依赖
dependencies {
classpath ("com.tencent.matrix:matrix-gradle-plugin:${MATRIX_VERSION}") { changing = true }
}
- 在 app/build.gradle 文件中添加 Matrix 各模块的依赖
apply plugin: 'com.tencent.matrix-plugin'
dependencies {
implementation group: "com.tencent.matrix", name: "matrix-android-lib", version: MATRIX_VERSION, changing: true
implementation group: "com.tencent.matrix", name: "matrix-android-commons", version: MATRIX_VERSION, changing: true
implementation group: "com.tencent.matrix", name: "matrix-resource-canary-android", version: MATRIX_VERSION, changing: true
implementation group: "com.tencent.matrix", name: "matrix-resource-canary-common", version: MATRIX_VERSION, changing: true
}
2. 必要代码 (参考 Matrix中sample-android示例代码)
- 实现PluginListener,接收Matrix处理后的数据(参考TestPluginListener)
public class PluginListener extends DefaultPluginListener {
public static final String TAG = "Matrix.PluginListener";
public SoftReference<Context> softReference;
private final Handler mHandler = new Handler(Looper.getMainLooper());
public PluginListener(Context context) {
super(context);
softReference = new SoftReference<>(context);
}
@Override
public void onReportIssue(Issue issue) {
super.onReportIssue(issue);
MatrixLog.e(TAG, issue.toString());
}
}
Matrix 项目本身是没有可视化页面,onReportIssue()方法会输出解析后数据。
- 实现动态配置接口,可修改Matrix内部参数(参考DynamicConfigImplDemo类)
public class DynamicConfigImpl implements IDynamicConfig {
private static final String TAG = "Matrix.DynamicConfigImplDemo";
public DynamicConfigImpl() {
}
public boolean isMatrixEnable() {
return true;
}
public boolean isDumpHprof() {
return true;
}
@Override
public String get(String key, String defStr) {
if ((ExptEnum.clicfg_matrix_resource_detect_interval_millis.name().equals(key) || ExptEnum.clicfg_matrix_resource_detect_interval_millis_bg.name().equals(key))) {
Log.d("DynamicConfig", "Matrix.ActivityRefWatcher: clicfg_matrix_resource_detect_interval_millis 10s");
return String.valueOf(TimeUnit.SECONDS.toMillis(5));
}
if (ExptEnum.clicfg_matrix_resource_max_detect_times.name().equals(key)) {
Log.d("DynamicConfig", "Matrix.ActivityRefWatcher: clicfg_matrix_resource_max_detect_times 5");
return String.valueOf(3);
}
return defStr;
}
@Override
public int get(String key, int defInt) {
if (MatrixEnum.clicfg_matrix_resource_max_detect_times.name().equals(key)) {
MatrixLog.i(TAG, "key:" + key + ", before change:" + defInt + ", after change, value:" + 2);
return 2;
}
if (MatrixEnum.clicfg_matrix_trace_fps_report_threshold.name().equals(key)) {
return 10000;
}
if (MatrixEnum.clicfg_matrix_trace_fps_time_slice.name().equals(key)) {
return 12000;
}
return defInt;
}
@Override
public long get(String key, long defLong) {
if (MatrixEnum.clicfg_matrix_trace_fps_report_threshold.name().equals(key)) {
return 10000L;
}
if (MatrixEnum.clicfg_matrix_resource_detect_interval_millis.name().equals(key)) {
MatrixLog.i(TAG, key + ", before change:" + defLong + ", after change, value:" + 2000);
return 2000;
}
return defLong;
}
@Override
public boolean get(String key, boolean defBool) {
return defBool;
}
@Override
public float get(String key, float defFloat) {
return defFloat;
}
}
- 在Application的继承类中,对Matrix进行初始化(参考MatrixApplication)
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
DynamicConfigImpl dynamicConfig = new DynamicConfigImpl();
Matrix.Builder builder = new Matrix.Builder(this);
ResourcePlugin resourcePlugin = configureResourcePlugin(dynamicConfig);
builder.plugin(resourcePlugin);
Matrix.init(builder.build());
resourcePlugin.start();
}
private ResourcePlugin configureResourcePlugin(DynamicConfigImplDemo dynamicConfig) {
Intent intent = new Intent();
ResourceConfig.DumpMode mode = ResourceConfig.DumpMode.MANUAL_DUMP;
MatrixLog.i(TAG, "Dump Activity Leak Mode=%s", mode);
intent.setClassName(this.getPackageName(), "应用包名.ManualDumpActivity");
ResourceConfig resourceConfig = new ResourceConfig.Builder()
.dynamicConfig(dynamicConfig)
.setAutoDumpHprofMode(mode)
.setManualDumpTargetActivity(ManualDumpActivity.class.getName())
.setManufacture(Build.MANUFACTURER)
.build();
ResourcePlugin.activityLeakFixer(this);
return new ResourcePlugin(resourceConfig);
}
}
- 创建ManualDumpActivity,显示泄露Activity相关信息
public class ManualDumpActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manual_dump);
TextView leak_activity=findViewById(R.id.leak_activity);
leak_activity.setText("泄漏的Activity:"+getIntent().getStringExtra(SharePluginInfo.ISSUE_ACTIVITY_NAME));
TextView leak_process=findViewById(R.id.leak_process);
leak_process.setText("泄漏的进程:"+getIntent().getStringExtra(SharePluginInfo.ISSUE_LEAK_PROCESS));
TextView reference_chain=findViewById(R.id.reference_chain);
ManualDumpProcessor.ManualDumpData data =
getIntent().getParcelableExtra(SharePluginInfo.ISSUE_DUMP_DATA);
if (data!=null){
reference_chain.setText("泄漏的堆栈:"+data.refChain);
}
}
}
至此,ResourceCanary就已成功集成到你的项目中,并且开始收集和分析性能相关异常数据。
检测内存泄漏场景
以Handler 内存泄漏为例,Handler的Message被存储在MessageQueue中,有些Message并不能马上处理掉,它们在MessageQueue中存在的时间会很长,这就会导致Handler无法被回收。如果Handler是非静态的,则Handler 也会导致引用它的Activity 或者Service 不能被回收。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.acttivity_test_resource_trace);
Button btn=findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mHandler.sendMessageDelayed(Message.obtain(),60000);
finish();
}
});
}
Handler mHandler=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
检测效果:
|