@@ -19,58 +19,98 @@ package com.sensorsdata.analytics.android.plugin
1919
2020import org.objectweb.asm.MethodVisitor
2121import org.objectweb.asm.Opcodes
22+ import org.objectweb.asm.Type
23+ import org.objectweb.asm.commons.AdviceAdapter
2224
23- class SensorsAnalyticsWebViewMethodVisitor extends MethodVisitor implements Opcodes {
25+ /**
26+ * 判断逻辑:
27+ *
28+ * 如果当前方法所在的类是 WebView 的子类,并且被处理的方法是目标方法中的一个就不处理;
29+ * 否则就判断 owner 是否是 WebView 的子类,如果是就处理,否则不处理。
30+ */
31+ class SensorsAnalyticsWebViewMethodVisitor extends AdviceAdapter implements Opcodes {
2432
2533 private SensorsAnalyticsTransformHelper transformHelper
2634 private Class webView
2735 private Class x5WebView
2836 private boolean isPreviousX5WebView = false
2937 private static X5WebViewStatus x5WebViewStatus = X5WebViewStatus . NOT_INITIAL
30- private static final def TARGET_NAME_DESC = [" loadUrl(Ljava/lang/String;)V" , " loadUrl(Ljava/lang/String;Ljava/util/Map;)V" ,
31- " loadData(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" ,
32- " loadDataWithBaseURL(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" ,
33- " postUrl(Ljava/lang/String;[B)V" ]
34- private static final def VIEW_DESC = " Landroid/view/View;"
35- private static final def OWNER_WHITE_SET = new HashSet ([" android/webkit/WebView" , " com/tencent/smtt/sdk/WebView" ])
38+ // 目标方法
39+ private static final List<String > TARGET_NAME_DESC = [" loadUrl(Ljava/lang/String;)V" , " loadUrl(Ljava/lang/String;Ljava/util/Map;)V" ,
40+ " loadData(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" ,
41+ " loadDataWithBaseURL(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" ,
42+ " postUrl(Ljava/lang/String;[B)V" ]
43+ private static final String VIEW_DESC = " Landroid/view/View;"
44+ private static final HashSet OWNER_WHITE_SET = new HashSet ([" android/webkit/WebView" , " com/tencent/smtt/sdk/WebView" ])
3645 private String className
3746 private String superName
47+ private String methodNameDesc
48+ private boolean shouldSkip = false
3849
39-
40- SensorsAnalyticsWebViewMethodVisitor (MethodVisitor mv , SensorsAnalyticsTransformHelper transformHelper , String className , String superName ) {
41- super (SensorsAnalyticsUtil . ASM_VERSION , mv)
50+ SensorsAnalyticsWebViewMethodVisitor (MethodVisitor mv , int access , String name , String desc , SensorsAnalyticsTransformHelper transformHelper , String className , String superName ) {
51+ super (SensorsAnalyticsUtil . ASM_VERSION , mv, access, name, desc)
4252 this . transformHelper = transformHelper
4353 this . className = className
4454 this . superName = superName
55+ this . methodNameDesc = name + desc
56+ // 如果当前方法符合目标定义,而且此类是 WebView 的子类,那么就跳过 visitMethodInsn 的指令
57+ if (TARGET_NAME_DESC . contains(methodNameDesc) && isAssignableWebView(className)) {
58+ shouldSkip = true
59+ }
4560 }
4661
4762 @Override
4863 void visitMethodInsn (int opcode , String owner , String name , String desc , boolean itf ) {
49- if (TARGET_NAME_DESC . contains(name + desc)) {
50- if (! checkWebViewChild(className)) {
64+ if (shouldSkip) {
65+ super . visitMethodInsn(opcode, owner, name, desc, itf)
66+ return
67+ }
68+ try {
69+ if (opcode != INVOKESTATIC && TARGET_NAME_DESC . contains(name + desc)) {
70+ // 解决 NoClassDefError 问题
71+ if (superName == " com/tencent/smtt/sdk/WebViewClient" ) {
72+ super . visitMethodInsn(opcode, owner, name, desc, itf)
73+ return
74+ }
5175 if (isAssignableWebView(owner)) {
52- opcode = INVOKESTATIC
53- owner = SensorsAnalyticsHookConfig . SENSORS_ANALYTICS_API
76+ Type [] argTypes = Type . getArgumentTypes(desc)
77+ List<Integer > positionList = new ArrayList<> ()
78+ // 依次复制操作数栈顶的元素到局部变量表中保存
79+ for (int index = 0 ; index < argTypes. length; index++ ) {
80+ int position = newLocal(argTypes[index])
81+ storeLocal(position)
82+ positionList. add(position)
83+ }
84+ int ownerPosition = newLocal(Type . getObjectType(owner))
85+ storeLocal(ownerPosition)
86+ positionList. add(ownerPosition)
87+ // 将局部变量表中的数据压入操作数栈中触发原有的方法
88+ positionList. reverseEach { tmp ->
89+ loadLocal(tmp)
90+ }
91+ super . visitMethodInsn(opcode, owner, name, desc, itf)
92+ // 将局部变量表中的数据压入操作数栈中触发我们需要插入的方法
93+ positionList. reverseEach { tmp ->
94+ loadLocal(tmp)
95+ }
5496 desc = reStructureDesc(desc)
97+ // 为保持新 SDK 使用旧版插件问题,会使用新 SDK loadUrl + 2 后缀的方法
98+ mv. visitMethodInsn(INVOKESTATIC , SensorsAnalyticsHookConfig . SENSORS_ANALYTICS_API , name + " 2" , desc, false )
99+ return
55100 }
56101 }
102+ } catch (Throwable throwable) {
103+ Logger . warn(" Can not auto handle webview, if you have any questions, please contact our technical services: classname:${ className} , method:${ methodNameDesc} , exception: ${ throwable} " )
57104 }
58105 super . visitMethodInsn(opcode, owner, name, desc, itf)
59106 }
60107
61108 /**
62- * 判断是否是 WebView 的子类,避免 WebView 子类中调用 load* 方法,导致的递归调用
109+ * 判断方法的 owner 是否是 WebView 的子类
63110 *
64- * @param className 当前被处理的类
65- * @return true 是 WebView 的子类,false 非 WebView 子类
111+ * @param owner
112+ * @return true 表示是 WebView 的子类,否则不是
66113 */
67- private boolean checkWebViewChild (String className ) {
68- if (superName == " com/tencent/smtt/sdk/WebViewClient" ) {
69- return false
70- }
71- isAssignableWebView(className)
72- }
73-
74114 private boolean isAssignableWebView (String owner ) {
75115 try {
76116 if (OWNER_WHITE_SET . contains(owner)) {
0 commit comments