4、分析 Hook 点
分析 View 组件的 setOnClickListener 方法 , 最终将 OnClickListener l 点击监听器设置到哪 ?
getListenerInfo() 获取的是 ListenerInfo 类型的对象 , 其中就封装了 OnClickListener mOnClickListener 成员 , 点击监听器就是设置在这里 ;
知道了定义位置 , 就可以通过反射获取 mOnClickListener 对应的成员字段 Field ;
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { static class ListenerInfo { /** * Listener used to dispatch click events. * This field should be made private, so it is hidden from the SDK. * {@hide} */ @UnsupportedAppUsage public OnClickListener mOnClickListener; } /** * Interface definition for a callback to be invoked when a view is clicked. */ public interface OnClickListener { /** * Called when a view has been clicked. * * @param v The view that was clicked. */ void onClick(View v); } }
5、反射 ListenerInfo 并设置新的 OnClickListener 监听器
获取 ListenerInfo 中的 public OnClickListener mOnClickListener 成员 , 并重新设置新的成员 , 注入业务逻辑 ;
① 先根据全类名获取 android.view.View$ListenerInfo 字节码对象 ;
// ① 先根据全类名获取 ListenerInfo 字节码 Class<?> clazz = null; try { clazz = Class.forName("android.view.View$ListenerInfo"); } catch (ClassNotFoundException e) { e.printStackTrace(); }
② 获取 android.view.View$ListenerInfo 中的 mOnClickListener 成员
// ② 获取 android.view.View.ListenerInfo 中的 mOnClickListener 成员 Field field = null; try { field = clazz.getField("mOnClickListener"); } catch (NoSuchFieldException e) { e.printStackTrace(); }
③ 设置该字段访问性, 执行所有的反射方法 , 设置成员变量 之前 , 都要设置可见性 ;
// ③ 设置该字段访问性, 执行所有的反射方法 , 设置成员变量 之前 , 都要设置可见性 field.setAccessible(true);
④ 获取 mOnClickListener 成员变量 ;
// ④ 获取 mOnClickListener 成员变量 View.OnClickListener mOnClickListener = null; try { mOnClickListener = (View.OnClickListener) field.get(mListenerInfo); } catch (IllegalAccessException e) { e.printStackTrace(); }
⑤ 修改 View 的 ListenerInfo 成员的 mOnClickListener 成员 , 重新设置一个自定义的 View.OnClickListener 监听器 , 在该监听器的 onClick 方法中 , 调用之前获取的 监听器的 onClick 方法 , 此外还可以在该点击方法前后注入开发者自定义的业务逻辑 ;
// ⑤ 修改 View 的 ListenerInfo 成员的 mOnClickListener 成员 try { View.OnClickListener finalMOnClickListener = mOnClickListener; field.set(mListenerInfo, new View.OnClickListener(){ @Override public void onClick(View v) { Log.i(TAG, "Hook Before"); finalMOnClickListener.onClick(view); Log.i(TAG, "Hook After"); } }); } catch (IllegalAccessException e) { e.printStackTrace(); }
三、完整代码示例
完整代码示例 :
package com.example.plugin_hook; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 获取按钮 , 并未按钮组件设置点击事件 Button button = findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.i(TAG, "Button OnClickListener onClick"); } }); hook(button); } /** * hook Button 组件的 getListenerInfo 方法 * @param view */ private void hook(View view){ // 获取 View 的 getListenerInfo 方法 Method getListenerInfo = null; try { getListenerInfo = View.class.getDeclaredMethod("getListenerInfo"); } catch (NoSuchMethodException e) { e.printStackTrace(); } // 执行所有的反射方法 , 设置成员变量 之前 , 都要设置可见性 getListenerInfo.setAccessible(true); // 执行 View view 对象的 getListenerInfo 方法 Object mListenerInfo = null; try { mListenerInfo = getListenerInfo.invoke(view); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } // 反射获取 OnClickListener 成员 // ① 先根据全类名获取 ListenerInfo 字节码 Class<?> clazz = null; try { clazz = Class.forName("android.view.View$ListenerInfo"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // ② 获取 android.view.View.ListenerInfo 中的 mOnClickListener 成员 Field field = null; try { field = clazz.getField("mOnClickListener"); } catch (NoSuchFieldException e) { e.printStackTrace(); } // ③ 设置该字段访问性, 执行所有的反射方法 , 设置成员变量 之前 , 都要设置可见性 field.setAccessible(true); // ④ 获取 mOnClickListener 成员变量 View.OnClickListener mOnClickListener = null; try { mOnClickListener = (View.OnClickListener) field.get(mListenerInfo); } catch (IllegalAccessException e) { e.printStackTrace(); } // ⑤ 修改 View 的 ListenerInfo 成员的 mOnClickListener 成员 // 其中 ListenerInfo 成员 是 try { View.OnClickListener finalMOnClickListener = mOnClickListener; field.set(mListenerInfo, new View.OnClickListener(){ @Override public void onClick(View v) { Log.i(TAG, "Hook Before"); finalMOnClickListener.onClick(view); Log.i(TAG, "Hook After"); } }); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
执行结果 :
2021-06-17 11:19:07.513 12251-12251/com.example.plugin_hook I/MainActivity: Hook Before 2021-06-17 11:19:07.513 12251-12251/com.example.plugin_hook I/MainActivity: Button OnClickListener onClick 2021-06-17 11:19:07.513 12251-12251/com.example.plugin_hook I/MainActivity: Hook After