├── README.md └── matrix_method_parser ├── .classpath ├── .gitignore ├── .project ├── .settings └── org.eclipse.jdt.core.prefs ├── README ├── matrix耗时方法解析.png └── matrix耗时方法解析工具.jar ├── libs ├── gson-2.8.5.jar ├── json-20190722.jar ├── reactive-streams-1.0.2.jar └── rxjava-2.2.11.jar └── src └── com └── matrix └── parser ├── Constant.java ├── MatrixParser.java ├── MethodIssueParser.java ├── model ├── GBC.java └── Issue.java └── util └── Util.java /README.md: -------------------------------------------------------------------------------- 1 | # matrix_evil_method_stack_parser 2 | matrix的耗时方法日志解析 3 | 4 | # README文件夹中有可执行的工具 5 | 6 | -------------------------------------------------------------------------------- /matrix_method_parser/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /matrix_method_parser/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | -------------------------------------------------------------------------------- /matrix_method_parser/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | matrix_method_parser 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /matrix_method_parser/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.8 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.source=1.8 12 | -------------------------------------------------------------------------------- /matrix_method_parser/README/matrix耗时方法解析.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilentSword2020/matrix_evil_method_stack_parser/518676273ad0d757b1a6c622ccb8c23b310d2156/matrix_method_parser/README/matrix耗时方法解析.png -------------------------------------------------------------------------------- /matrix_method_parser/README/matrix耗时方法解析工具.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilentSword2020/matrix_evil_method_stack_parser/518676273ad0d757b1a6c622ccb8c23b310d2156/matrix_method_parser/README/matrix耗时方法解析工具.jar -------------------------------------------------------------------------------- /matrix_method_parser/libs/gson-2.8.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilentSword2020/matrix_evil_method_stack_parser/518676273ad0d757b1a6c622ccb8c23b310d2156/matrix_method_parser/libs/gson-2.8.5.jar -------------------------------------------------------------------------------- /matrix_method_parser/libs/json-20190722.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilentSword2020/matrix_evil_method_stack_parser/518676273ad0d757b1a6c622ccb8c23b310d2156/matrix_method_parser/libs/json-20190722.jar -------------------------------------------------------------------------------- /matrix_method_parser/libs/reactive-streams-1.0.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilentSword2020/matrix_evil_method_stack_parser/518676273ad0d757b1a6c622ccb8c23b310d2156/matrix_method_parser/libs/reactive-streams-1.0.2.jar -------------------------------------------------------------------------------- /matrix_method_parser/libs/rxjava-2.2.11.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilentSword2020/matrix_evil_method_stack_parser/518676273ad0d757b1a6c622ccb8c23b310d2156/matrix_method_parser/libs/rxjava-2.2.11.jar -------------------------------------------------------------------------------- /matrix_method_parser/src/com/matrix/parser/Constant.java: -------------------------------------------------------------------------------- 1 | package com.matrix.parser; 2 | 3 | public final class Constant { 4 | public static final String TITLE = "Matrix方法耗时解析"; 5 | public static final String FILE_METHOD_MAPPING = "methodMapping.txt文件"; 6 | public static final String LABEL_ISSUE_CONTENT = "问题内容(json格式)"; 7 | public static final String LABEL_START_PARSER = "开始解析"; 8 | public static final String LABEL_SELECT_FILE = "选择文件"; 9 | public static final String LABEL_PARSER_RESULT = "解析结果"; 10 | public static final String test_matrix_method_mapping_error = "请选择methodMapping.txt文件"; 11 | public static final String test_matrix_issue_hint = "请输入问题内容"; 12 | public static final String test_matrix_parser_fail = "解析失败"; 13 | public static final String test_matrix_method_not_found = "没有找到对应的方法声明:可能methodMapping.txt和安装的APK不一致"; 14 | public static final String test_matrix_stack_not_found = "没有找到对应的stack:json数据格式可能不对"; 15 | public static final String test_matrix_result = "进程:\r\n%1$s\r\n\r\n具体的耗时场景:\r\n%2$s\r\n\r\n耗时:\r\n%3$sms\r\n\r\n\r\n耗时方法:\r\n%4$s\r\n\r\n\r\n方法调用的栈信息:\r\n%5$s\r\n\r\n\r\n详细信息:\r\n%6$s\r\n"; 16 | public static final String test_matrix_stack_line = "[%1$s 执行次数:%2$s 总耗时:%3$sms]\r\n\r\n"; 17 | public static final String[] matrix_method_scene = new String[] { "NORMAL: 普通慢函数场景", "ENTER: Activity进入场景", 18 | "ANR: anr超时场景", "FULL: 满buffer场景", "STARTUP: 启动耗时场景" }; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /matrix_method_parser/src/com/matrix/parser/MatrixParser.java: -------------------------------------------------------------------------------- 1 | package com.matrix.parser; 2 | 3 | import java.awt.GridBagLayout; 4 | import java.awt.event.ActionEvent; 5 | import java.awt.event.ActionListener; 6 | import java.awt.event.WindowAdapter; 7 | import java.awt.event.WindowEvent; 8 | import java.io.File; 9 | 10 | import javax.swing.JButton; 11 | import javax.swing.JFileChooser; 12 | import javax.swing.JFrame; 13 | import javax.swing.JLabel; 14 | import javax.swing.JOptionPane; 15 | import javax.swing.JScrollPane; 16 | import javax.swing.JTextArea; 17 | 18 | import com.matrix.parser.MethodIssueParser.ParserResult; 19 | import com.matrix.parser.model.GBC; 20 | import com.matrix.parser.util.Util; 21 | 22 | /** 23 | * 界面 24 | * 25 | * @author yangqh 26 | * 27 | */ 28 | public class MatrixParser { 29 | 30 | // 31 | private JFrame mainFrame; 32 | private JLabel containerLabel; 33 | 34 | // 35 | private JButton methodMappingChooser; 36 | private JLabel filePathLabel; 37 | private JTextArea issueArea; 38 | private JTextArea resultArea; 39 | 40 | // 41 | private MethodIssueParser methodIssueParser; 42 | 43 | public static void main(String[] args) { 44 | new MatrixParser().show(); 45 | } 46 | 47 | public MatrixParser() { 48 | initParser(); 49 | initUI(); 50 | } 51 | 52 | /** 53 | * 初始化解析器 54 | */ 55 | private void initParser() { 56 | methodIssueParser = new MethodIssueParser(new MethodIssueParser.Listener() { 57 | 58 | @Override 59 | public void onResult(ParserResult result) { 60 | // 显示解析结果 61 | if (resultArea != null) { 62 | String content = String.format(Constant.test_matrix_result, result.processName, result.scene, 63 | result.costTime, result.costStackKey, result.stackDetail, result.result); 64 | resultArea.setText(content); 65 | } 66 | } 67 | 68 | @Override 69 | public void onError(CharSequence msg) { 70 | // 显示错误提示 71 | JOptionPane.showMessageDialog(mainFrame, msg, "", JOptionPane.WARNING_MESSAGE); 72 | } 73 | }); 74 | } 75 | 76 | /** 77 | * 清理数据,退出界面 78 | */ 79 | private void clearAndExit() { 80 | if (methodIssueParser != null) { 81 | methodIssueParser.clear(); 82 | methodIssueParser = null; 83 | } 84 | System.exit(0); 85 | } 86 | 87 | /** 88 | * 显示界面 89 | */ 90 | private void show() { 91 | if (mainFrame != null) { 92 | mainFrame.setVisible(true); 93 | } 94 | } 95 | 96 | /** 97 | * 初始化ui 98 | */ 99 | private void initUI() { 100 | mainFrame = new JFrame(Constant.TITLE); 101 | mainFrame.addWindowListener(new WindowAdapter() { 102 | public void windowClosing(WindowEvent windowEvent) { 103 | // 点击关闭按钮 104 | clearAndExit(); 105 | } 106 | 107 | }); 108 | mainFrame.setBounds(200, 200, 500, 800); 109 | 110 | initComponents(); 111 | 112 | mainFrame.setVisible(true); 113 | } 114 | 115 | /** 116 | * 初始化子控件 117 | */ 118 | private void initComponents() { 119 | // 容器 120 | containerLabel = new JLabel("", JLabel.CENTER); 121 | containerLabel.setLayout(new GridBagLayout()); 122 | mainFrame.add(containerLabel); 123 | 124 | int row = 2; 125 | // methodMapping.txt文件 126 | JLabel methodMappingLabel = new JLabel(Constant.FILE_METHOD_MAPPING, JLabel.CENTER); 127 | containerLabel.add(methodMappingLabel, new GBC(0, row, 1, 1)); 128 | // 选择文件 129 | methodMappingChooser = new JButton(Constant.LABEL_SELECT_FILE); 130 | methodMappingChooser.setBounds(0, 0, 50, 64); 131 | methodMappingChooser.addActionListener(new ActionListener() { 132 | 133 | @Override 134 | public void actionPerformed(ActionEvent e) { 135 | selectFile(); 136 | } 137 | }); 138 | containerLabel.add(methodMappingChooser, new GBC(0, ++row, 1, 1)); 139 | 140 | // 文件路径 141 | filePathLabel = new JLabel("", JLabel.CENTER); 142 | containerLabel.add(filePathLabel, new GBC(0, ++row, 1, 1).setFill(GBC.BOTH)); 143 | 144 | // 日志内容标题 145 | JLabel issueLabel = new JLabel(Constant.LABEL_ISSUE_CONTENT, JLabel.CENTER); 146 | containerLabel.add(issueLabel, new GBC(0, ++row, 1, 1)); 147 | 148 | // 日志内容 149 | issueArea = new JTextArea(); 150 | JScrollPane jScrollPane = new JScrollPane(); 151 | jScrollPane.setViewportView(issueArea); 152 | containerLabel.add(jScrollPane, new GBC(0, ++row, 1, 1).setWeight(1, 0.5f).setFill(GBC.BOTH)); 153 | 154 | // 开始解析按钮 155 | JButton parserBtn = new JButton(Constant.LABEL_START_PARSER); 156 | parserBtn.setBounds(0, 0, 50, 64); 157 | parserBtn.addActionListener(new ActionListener() { 158 | 159 | @Override 160 | public void actionPerformed(ActionEvent e) { 161 | startParser(); 162 | } 163 | 164 | }); 165 | containerLabel.add(parserBtn, new GBC(0, ++row, 1, 1)); 166 | 167 | // 解析结果的标题 168 | JLabel resultLabel = new JLabel(Constant.LABEL_PARSER_RESULT, JLabel.CENTER); 169 | containerLabel.add(resultLabel, new GBC(0, ++row, 1, 1).setFill(GBC.BOTH)); 170 | 171 | // 解析结果 172 | jScrollPane = new JScrollPane(); 173 | resultArea = new JTextArea(); 174 | jScrollPane.setViewportView(resultArea); 175 | containerLabel.add(jScrollPane, new GBC(0, ++row, 1, 1).setWeight(1, 1).setFill(GBC.BOTH)); 176 | 177 | } 178 | 179 | /** 180 | * 开始解析 181 | */ 182 | private void startParser() { 183 | formatIssueTxt(); 184 | if (methodIssueParser != null && issueArea != null) { 185 | methodIssueParser.start(issueArea.getText()); 186 | } 187 | 188 | } 189 | 190 | /** 191 | * 格式化问题内容 192 | */ 193 | private void formatIssueTxt() { 194 | if (methodIssueParser != null && issueArea != null) { 195 | String result = methodIssueParser.getPrettyStr(issueArea.getText()); 196 | if (!Util.isEmpty(result)) { 197 | issueArea.setText(result); 198 | } 199 | } 200 | } 201 | 202 | /** 203 | * 选择文件 204 | */ 205 | private void selectFile() { 206 | JFileChooser fileChooser = new JFileChooser(); 207 | fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); 208 | fileChooser.showDialog(new JLabel(), Constant.LABEL_SELECT_FILE); 209 | File file = fileChooser.getSelectedFile(); 210 | if (file == null || file.isDirectory()) { 211 | return; 212 | } 213 | if (file.isFile()) { 214 | System.out.println(file.getAbsolutePath()); 215 | if (filePathLabel != null) { 216 | filePathLabel.setText(file.getAbsolutePath()); 217 | } 218 | if (methodIssueParser != null) { 219 | methodIssueParser.setMethodMappingFile(file); 220 | } 221 | 222 | } 223 | } 224 | 225 | } -------------------------------------------------------------------------------- /matrix_method_parser/src/com/matrix/parser/MethodIssueParser.java: -------------------------------------------------------------------------------- 1 | package com.matrix.parser; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.HashMap; 8 | import java.util.Locale; 9 | 10 | import org.json.JSONObject; 11 | 12 | import com.google.gson.Gson; 13 | import com.matrix.parser.model.Issue; 14 | import com.matrix.parser.util.Util; 15 | 16 | import io.reactivex.Observable; 17 | import io.reactivex.ObservableOnSubscribe; 18 | import io.reactivex.annotations.NonNull; 19 | import io.reactivex.annotations.Nullable; 20 | import io.reactivex.schedulers.Schedulers; 21 | 22 | /** 23 | * 方法耗时问题解析 24 | */ 25 | public class MethodIssueParser { 26 | 27 | private static final int MOTHOD_ID_INVILID = -1; 28 | 29 | private File methodMappingFile; 30 | private final HashMap methodMap = new HashMap<>(); 31 | private Listener listener; 32 | private Gson gson; 33 | 34 | public void clear() { 35 | methodMappingFile = null; 36 | listener = null; 37 | methodMap.clear(); 38 | gson = null; 39 | } 40 | 41 | public MethodIssueParser(Listener listener) { 42 | this.listener = listener; 43 | } 44 | 45 | public File getMethodMappingFile() { 46 | return methodMappingFile; 47 | } 48 | 49 | /** 50 | * 设置methodMapping.txt文件 51 | * 52 | * @param methodMappingFile 53 | */ 54 | public void setMethodMappingFile(File methodMappingFile) { 55 | this.methodMappingFile = methodMappingFile; 56 | } 57 | 58 | public void start(String issueJson) { 59 | 60 | // 判断methodMapping.txt是否存在 61 | if (!Util.isExists(methodMappingFile)) { 62 | if (listener != null) { 63 | listener.onError(Constant.test_matrix_method_mapping_error); 64 | } 65 | return; 66 | } 67 | 68 | if (Util.isEmpty(issueJson)) { 69 | if (listener != null) { 70 | listener.onError(Constant.test_matrix_issue_hint); 71 | } 72 | return; 73 | } 74 | 75 | Observable.create((ObservableOnSubscribe) emitter -> { 76 | ParserResult result = getParserResult(issueJson); 77 | if (result == null) { 78 | emitter.onError(new Throwable("result is null")); 79 | } else { 80 | emitter.onNext(result); 81 | emitter.onComplete(); 82 | } 83 | 84 | }).subscribeOn(Schedulers.newThread()).subscribe(result -> { 85 | if (listener != null && result != null) { 86 | listener.onResult(result); 87 | } 88 | }, throwable -> { 89 | if (throwable != null) { 90 | throwable.printStackTrace(); 91 | } 92 | if (listener != null) { 93 | listener.onError(String.format(Constant.test_matrix_parser_fail)); 94 | } 95 | 96 | }); 97 | 98 | } 99 | 100 | private ParserResult getParserResult(String issueJson) { 101 | 102 | // 去掉content中 key: nameValuePairs,把内容放到content下 103 | try { 104 | String nameValueKey = "nameValuePairs"; 105 | if (issueJson != null && issueJson.contains(nameValueKey)) { 106 | JSONObject issueObj = new JSONObject(issueJson); 107 | String contentKey = "content"; 108 | JSONObject contentObj = issueObj != null ? issueObj.optJSONObject(contentKey) : null; 109 | if (contentObj != null && contentObj.has(nameValueKey)) { 110 | JSONObject nameValuePairs = contentObj.optJSONObject(nameValueKey); 111 | if (nameValuePairs != null) { 112 | issueObj.put(contentKey, nameValuePairs); 113 | issueJson = issueObj.toString(); 114 | } 115 | } 116 | } 117 | } catch (Exception e) { 118 | e.printStackTrace(); 119 | } 120 | 121 | Issue issue = getIssue(issueJson); 122 | if (issue == null) { 123 | System.out.println("MethodIssueParser.getParserResult() issue is null"); 124 | return null; 125 | } 126 | try { 127 | // 初始化方法映射map 128 | initMethodMap(); 129 | 130 | // 开始解析相关的堆栈 131 | HashMap content = issue.getContent(); 132 | 133 | // stack 134 | String stackKeyName = "stack"; 135 | String stack = String.valueOf(content.get(stackKeyName)); 136 | stack = parserStack(stack); 137 | if (Util.isEmpty(stack)) { 138 | stack = Constant.test_matrix_stack_not_found; 139 | } 140 | content.put(stackKeyName, stack); 141 | 142 | // stackKey 143 | stackKeyName = "stackKey"; 144 | String stackKey = String.valueOf(content.get(stackKeyName)); 145 | String methodId = (Util.isEmpty(stackKey) || !stackKey.contains("|")) ? "" 146 | : stackKey.substring(0, stackKey.indexOf('|')); 147 | stackKey = methodMap.get(Util.getInt(methodId, MOTHOD_ID_INVILID)); 148 | if (Util.isEmpty(stackKey)) { 149 | stackKey = String.format(Constant.test_matrix_method_not_found); 150 | } 151 | content.put(stackKeyName, stackKey); 152 | 153 | // 154 | issue.setContent(content); 155 | 156 | // 157 | ParserResult result = new ParserResult(); 158 | result.processName = String.valueOf(content.get("process")); 159 | result.scene = getSceneStr(String.valueOf(content.get("detail"))); 160 | result.costTime = String.valueOf(content.get("cost")); 161 | result.costStackKey = stackKey; 162 | result.stackDetail = stack; 163 | result.result = getJsonStr(issue); 164 | return result; 165 | } catch (Throwable e) { 166 | System.out.println("MethodIssueParser.getParserResult() fail:" + e.getLocalizedMessage()); 167 | } 168 | return null; 169 | } 170 | 171 | /** 172 | * 获取耗时的场景描述 173 | * 174 | * @param name 175 | * @return 176 | */ 177 | private String getSceneStr(String name) { 178 | if (Util.isEmpty(name)) { 179 | return "scene name is null"; 180 | } 181 | try { 182 | return Scene.valueOf(name).getDesc(); 183 | } catch (Throwable e) { 184 | e.printStackTrace(); 185 | } 186 | return "scene name is unknown"; 187 | } 188 | 189 | private String parserStack(String stack) { 190 | if (Util.isEmpty(stack)) { 191 | return stack; 192 | } 193 | // 解析stack 194 | // stack每行的格式:stack层级,方法id,方法执行次数,方法执行总耗时 195 | String[] items = stack.split("\n"); 196 | if (Util.isEmpty(items)) { 197 | return stack; 198 | } 199 | StringBuilder builder = new StringBuilder(); 200 | String lineFormat = Constant.test_matrix_stack_line; 201 | for (String item : items) { 202 | if (Util.isEmpty(item)) { 203 | continue; 204 | } 205 | String[] temp = item.split(","); 206 | if (Util.isEmpty(temp) || temp.length < 4) { 207 | continue; 208 | } 209 | String method = methodMap.get(Util.getInt(temp[1], MOTHOD_ID_INVILID)); 210 | if (Util.isEmpty(method)) { 211 | method = String.format(Constant.test_matrix_method_not_found); 212 | } 213 | builder.append(String.format(Locale.getDefault(), lineFormat, method, temp[2], temp[3])); 214 | } 215 | return builder.toString(); 216 | 217 | } 218 | 219 | /** 220 | * 初始化方法映射map 221 | */ 222 | private void initMethodMap() { 223 | if (methodMap != null && methodMap.size() > 0) { 224 | // 已经初始化 225 | return; 226 | } 227 | if (methodMappingFile == null) { 228 | return; 229 | } 230 | FileInputStream fis = null; 231 | BufferedReader bufferReader = null; 232 | try { 233 | 234 | if (!methodMappingFile.exists()) { 235 | return; 236 | } 237 | fis = new FileInputStream(methodMappingFile); 238 | bufferReader = new BufferedReader(new InputStreamReader(fis, "UTF-8")); 239 | String line; 240 | while ((line = bufferReader.readLine()) != null) { 241 | // methodMapping.txt每行的格式:方法id,方法accessType,类名,方法名,方法描述; 242 | String[] items = line.split(","); 243 | if (Util.isEmpty(items)) { 244 | continue; 245 | } 246 | // 方法id->方法声明 247 | int key = Util.getInt(items[0], MOTHOD_ID_INVILID); 248 | if (key == MOTHOD_ID_INVILID) { 249 | System.out.println("MethodIssueParser initMethodMap methodId:" + items[0] + " can't parser to int"); 250 | continue; 251 | } 252 | methodMap.put(key, items[2]); 253 | } 254 | } catch (Exception e) { 255 | e.printStackTrace(); 256 | } finally { 257 | Util.closeQuietly(fis); 258 | Util.closeQuietly(bufferReader); 259 | } 260 | 261 | } 262 | 263 | /** 264 | * 获取issue 265 | * 266 | * @param issueJson 267 | * @return 268 | */ 269 | @Nullable 270 | private Issue getIssue(@NonNull String issueJson) { 271 | if (Util.isEmpty(issueJson)) { 272 | return null; 273 | } 274 | synchronized (this) { 275 | if (gson == null) { 276 | gson = Util.getGson(); 277 | } 278 | } 279 | try { 280 | return gson.fromJson(issueJson, Issue.class); 281 | } catch (Throwable e) { 282 | e.printStackTrace(); 283 | } 284 | return null; 285 | } 286 | 287 | /** 288 | * 获取issue 289 | * 290 | * @param issueJson 291 | * @return 292 | */ 293 | @SuppressWarnings("unchecked") 294 | @Nullable 295 | private HashMap getContent(@NonNull String contentJson) { 296 | if (Util.isEmpty(contentJson)) { 297 | return null; 298 | } 299 | synchronized (this) { 300 | if (gson == null) { 301 | gson = Util.getGson(); 302 | } 303 | } 304 | try { 305 | return gson.fromJson(contentJson, HashMap.class); 306 | } catch (Throwable e) { 307 | e.printStackTrace(); 308 | } 309 | return null; 310 | } 311 | 312 | /** 313 | * 获取优美的json格式 314 | * 315 | * @param json 316 | * @return 317 | */ 318 | public String getPrettyStr(String json) { 319 | if (Util.isEmpty(json)) { 320 | return null; 321 | } 322 | synchronized (this) { 323 | if (gson == null) { 324 | gson = Util.getGson(); 325 | } 326 | } 327 | try { 328 | return gson.toJson(gson.fromJson(json, Issue.class)); 329 | } catch (Throwable e) { 330 | e.printStackTrace(); 331 | } 332 | return null; 333 | } 334 | 335 | /** 336 | * 获取issue的json字符串 337 | * 338 | * @param issue 339 | * @return 340 | */ 341 | private String getJsonStr(@NonNull Issue issue) { 342 | if (issue == null) { 343 | return null; 344 | } 345 | synchronized (this) { 346 | if (gson == null) { 347 | gson = Util.getGson(); 348 | } 349 | } 350 | try { 351 | return gson.toJson(issue); 352 | } catch (Throwable e) { 353 | e.printStackTrace(); 354 | } 355 | return null; 356 | } 357 | 358 | /** 359 | * 获取methodMapping的文件路径 360 | * 361 | * @return 362 | */ 363 | public String getMethodMappingFilePath() { 364 | return methodMappingFile != null ? methodMappingFile.getAbsolutePath() : null; 365 | } 366 | 367 | /** 368 | * 耗时的场景 369 | */ 370 | public enum Scene { 371 | NORMAL, // 普通慢函数场景 372 | ENTER, // Activity进入场景 373 | ANR, // anr超时场景 374 | FULL, // 满buffer场景 375 | STARTUP;// 启动耗时场景 376 | 377 | public String getDesc() { 378 | return Util.getItemAt(Constant.matrix_method_scene, ordinal()); 379 | } 380 | } 381 | 382 | /** 383 | * 解析结果 384 | */ 385 | public static class ParserResult { 386 | /** 387 | * 进程名称 388 | */ 389 | public String processName; 390 | /** 391 | * 具体的耗时场景 392 | */ 393 | public String scene; 394 | /** 395 | * 耗时(单位 ms) 396 | */ 397 | public String costTime; 398 | /** 399 | * 耗时的方法信息 400 | */ 401 | public String costStackKey; 402 | /** 403 | * 方法调用的栈信息 404 | */ 405 | public String stackDetail; 406 | 407 | /** 408 | * 最详细的信息 409 | */ 410 | public String result; 411 | } 412 | 413 | public interface Listener { 414 | /** 415 | * 错误 416 | * 417 | * @param msg 418 | */ 419 | void onError(CharSequence msg); 420 | 421 | /** 422 | * 结果 423 | * 424 | * @param result 425 | */ 426 | void onResult(ParserResult result); 427 | } 428 | 429 | } 430 | -------------------------------------------------------------------------------- /matrix_method_parser/src/com/matrix/parser/model/GBC.java: -------------------------------------------------------------------------------- 1 | package com.matrix.parser.model; 2 | 3 | import java.awt.GridBagConstraints; 4 | import java.awt.Insets; 5 | 6 | public class GBC extends GridBagConstraints { 7 | 8 | private static final long serialVersionUID = 1L; 9 | 10 | // 初始化左上角位置 11 | public GBC(int gridx, int gridy) { 12 | this.gridx = gridx; 13 | this.gridy = gridy; 14 | } 15 | 16 | // 初始化左上角位置和所占行数和列数 17 | public GBC(int gridx, int gridy, int gridwidth, int gridheight) { 18 | this.gridx = gridx; 19 | this.gridy = gridy; 20 | this.gridwidth = gridwidth; 21 | this.gridheight = gridheight; 22 | } 23 | 24 | // 对齐方式 25 | public GBC setAnchor(int anchor) { 26 | this.anchor = anchor; 27 | return this; 28 | } 29 | 30 | // 是否拉伸及拉伸方向 31 | public GBC setFill(int fill) { 32 | this.fill = fill; 33 | return this; 34 | } 35 | 36 | // x和y方向上的增量 37 | public GBC setWeight(double weightx, double weighty) { 38 | this.weightx = weightx; 39 | this.weighty = weighty; 40 | return this; 41 | } 42 | 43 | // 外部填充 44 | public GBC setInsets(int distance) { 45 | this.insets = new Insets(distance, distance, distance, distance); 46 | return this; 47 | } 48 | 49 | // 外填充 50 | public GBC setInsets(int top, int left, int bottom, int right) { 51 | this.insets = new Insets(top, left, bottom, right); 52 | return this; 53 | } 54 | 55 | // 内填充 56 | public GBC setIpad(int ipadx, int ipady) { 57 | this.ipadx = ipadx; 58 | this.ipady = ipady; 59 | return this; 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /matrix_method_parser/src/com/matrix/parser/model/Issue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making wechat-matrix available. 3 | * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved. 4 | * Licensed under the BSD 3-Clause License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://opensource.org/licenses/BSD-3-Clause 9 | * 10 | * Unless required by applicable law or agreed to in writing, 11 | * software distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matrix.parser.model; 18 | 19 | import java.util.HashMap; 20 | 21 | /** 22 | * Data struct contains the issues 23 | * 24 | * Created by zhangshaowen on 2017/8/1. 25 | */ 26 | public class Issue { 27 | private Integer type; 28 | private String tag; 29 | private String key; 30 | private HashMap content; 31 | 32 | public static final String ISSUE_REPORT_TYPE = "type"; 33 | public static final String ISSUE_REPORT_TAG = "tag"; 34 | public static final String ISSUE_REPORT_PROCESS = "process"; 35 | public static final String ISSUE_REPORT_TIME = "time"; 36 | 37 | public Issue() { 38 | 39 | } 40 | 41 | public void setType(Integer type) { 42 | this.type = type; 43 | } 44 | 45 | public HashMap getContent() { 46 | return content; 47 | } 48 | 49 | public void setContent(HashMap content) { 50 | this.content = content; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return String.format("tag[%s]type[%d];key[%s];content[%s]", tag, type, key, content); 56 | } 57 | 58 | public void setKey(String key) { 59 | this.key = key; 60 | } 61 | 62 | public void setTag(String tag) { 63 | this.tag = tag; 64 | } 65 | 66 | public Integer getType() { 67 | return type; 68 | } 69 | 70 | public String getKey() { 71 | return key; 72 | } 73 | 74 | public String getTag() { 75 | return tag; 76 | } 77 | 78 | public void setType(int type) { 79 | this.type = type; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /matrix_method_parser/src/com/matrix/parser/util/Util.java: -------------------------------------------------------------------------------- 1 | package com.matrix.parser.util; 2 | 3 | import java.io.Closeable; 4 | import java.io.File; 5 | import java.io.IOException; 6 | 7 | import com.google.gson.ExclusionStrategy; 8 | import com.google.gson.FieldAttributes; 9 | import com.google.gson.Gson; 10 | import com.google.gson.GsonBuilder; 11 | 12 | import io.reactivex.annotations.Nullable; 13 | 14 | public final class Util { 15 | /** 16 | * 是否文件存在 17 | * 18 | * @param file 19 | * @return 20 | */ 21 | public static boolean isExists(File file) { 22 | return file != null && file.exists(); 23 | } 24 | 25 | /** 26 | * 关闭IO,不抛出异常 27 | * 28 | * @param closeable 可以为null 29 | */ 30 | public static void closeQuietly(@Nullable Closeable closeable) { 31 | if (closeable == null) 32 | return; 33 | try { 34 | closeable.close(); 35 | } catch (IOException ignored) { 36 | 37 | } 38 | } 39 | 40 | /** 41 | * 数组中指定index的item 42 | * 43 | * @param list 44 | * @param index 45 | * @param 46 | * @return 47 | */ 48 | @Nullable 49 | public static T getItemAt(@Nullable T[] list, int index) { 50 | if (list != null && index >= 0 && list.length > index) { 51 | return list[index]; 52 | } 53 | return null; 54 | } 55 | 56 | /** 57 | * 数组是否为空 58 | * 59 | * @param list 60 | * @param 61 | * @return 62 | */ 63 | public static boolean isEmpty(@Nullable T[] list) { 64 | return list == null || list.length <= 0; 65 | } 66 | 67 | /*** 68 | * string to int 69 | * 70 | * @param numberStr 数字字符串 71 | * @return 72 | */ 73 | public static int getInt(String numberStr, int defaultValue) { 74 | if (isEmpty(numberStr)) { 75 | return defaultValue; 76 | } 77 | try { 78 | return Integer.valueOf(numberStr); 79 | } catch (Exception e) { 80 | 81 | } 82 | return defaultValue; 83 | } 84 | 85 | /** 86 | * 是否为空或空串 87 | * 88 | * @param str 89 | * @return 90 | */ 91 | public static boolean isEmpty(String str) { 92 | return str == null || str.length() == 0; 93 | } 94 | 95 | /** 96 | * 获取Gson实例 97 | * 98 | * @return 99 | */ 100 | public static Gson getGson() { 101 | return new GsonBuilder().setPrettyPrinting().setExclusionStrategies(new ExclusionStrategy() { 102 | @Override 103 | public boolean shouldSkipField(FieldAttributes f) { 104 | // 对plugin字段不做序列化和反序列化 105 | return f != null && "plugin".equals(f.getName()); 106 | } 107 | 108 | @Override 109 | public boolean shouldSkipClass(Class clazz) { 110 | return false; 111 | } 112 | }).create(); 113 | } 114 | } 115 | --------------------------------------------------------------------------------