hook按钮的点击事件setOnClickListener
首先查看源码setOnClickListener,
/**
* Register a callback to be invoked when this view is clicked. If this view is not
* clickable, it becomes clickable.
*
* @param l The callback that will run
*
* @see #setClickable(boolean)
*/
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
把点击事件赋值给了getListenerInfo().mOnClickListener
getListenerInfo()这个方法是创建一个ListenerInfo对象
@UnsupportedAppUsage
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
ListenerInfo类如下
static class ListenerInfo {
/**
* Listener used to dispatch focus change events.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
@UnsupportedAppUsage
protected OnFocusChangeListener mOnFocusChangeListener;
/**
* Listeners for layout change events.
*/
private ArrayList mOnLayoutChangeListeners;
protected OnScrollChangeListener mOnScrollChangeListener;
/**
* Listeners for attach events.
*/
private CopyOnWriteArrayList mOnAttachStateChangeListeners;
/**
* Listener used to dispatch click events.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
@UnsupportedAppUsage
public OnClickListener mOnClickListener;
/**
* Listener used to dispatch long click events.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
@UnsupportedAppUsage
protected OnLongClickListener mOnLongClickListener;
/**
* Listener used to dispatch context click events. This field should be made private, so it
* is hidden from the SDK.
* {@hide}
*/
protected OnContextClickListener mOnContextClickListener;
/**
* Listener used to build the context menu.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
@UnsupportedAppUsage
protected OnCreateContextMenuListener mOnCreateContextMenuListener;
@UnsupportedAppUsage
private OnKeyListener mOnKeyListener;
@UnsupportedAppUsage
private OnTouchListener mOnTouchListener;
@UnsupportedAppUsage
private OnHoverListener mOnHoverListener;
@UnsupportedAppUsage
private OnGenericMotionListener mOnGenericMotionListener;
@UnsupportedAppUsage
private OnDragListener mOnDragListener;
private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;
OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
OnCapturedPointerListener mOnCapturedPointerListener;
private ArrayList mUnhandledKeyListeners;
private WindowInsetsAnimationListener mWindowInsetsAnimationListener;
/**
* This lives here since it's only valid for interactive views.
*/
private List mSystemGestureExclusionRects;
/**
* Used to track {@link #mSystemGestureExclusionRects}
*/
public RenderNode.PositionUpdateListener mPositionUpdateListener;
}
思路:
替换这个getListenerInfo().mOnClickListener
public class MainActivity extends AppCompatActivity {
private AppBarConfiguration appBarConfiguration;
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
binding.fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
binding.btnTest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "" + ((Button) view).getText(), Toast.LENGTH_SHORT).show();
}
});
// 在不修改以上代码的情况下,通过Hook把 ((Button) v).getText() 内容给修改
try {
hook(binding.btnTest); // button就是View对象
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "Hook失败" + e.toString(), Toast.LENGTH_SHORT).show();
}
}
private void hook(View view) throws Exception {
Class mViewClass = Class.forName("android.view.View");
Method getListenerInfoMethod = mViewClass.getDeclaredMethod("getListenerInfo");
getListenerInfoMethod.setAccessible(true); // 授权
// 执行方法
Object mListenerInfo = getListenerInfoMethod.invoke(view);
// 替 换 public OnClickListener mOnClickListener; 替换我们自己的
Class mListenerInfoClass = Class.forName("android.view.View$ListenerInfo");
Field mOnClickListenerField = mListenerInfoClass.getField("mOnClickListener");
final Object mOnClickListenerObj = mOnClickListenerField.get(mListenerInfo); // 需要@1对象
// 1.监听 onClick,当用户点击按钮的时候-->onClick, 我们自己要先拦截这个事件
// 动态代理
// mOnClickListener 本质是==OnClickListener
Object mOnClickListenerProxy = Proxy.newProxyInstance(MainActivity.class.getClassLoader(), // 1加载器
new Class[]{View.OnClickListener.class}, // 2要监听的接口,监听什么接口,就返回什么接口
new InvocationHandler() { // 3监听接口方法里面的回调
/**
*
* void onClick(View v);
*
* onClick ---> Method
* View v ---> Object[] args
*
* @param proxy
* @param method
* @param args
* @return
* @throws
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 加入了自己逻辑
Log.d("hook", "拦截到了 OnClickListener的方法了");
Button button = new Button(MainActivity.this);
button.setText("同学们大家好....");
// 让系统程序片段 --- 正常继续的执行下去
return method.invoke(mOnClickListenerObj, button);
}
});
// 狸猫换太子 把系统的 mOnClickListener 换成 我们自己写的 动态代理
mOnClickListenerField.set(mListenerInfo, mOnClickListenerProxy); // 替换的 我们自己的动态代理
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
return NavigationUI.navigateUp(navController, appBarConfiguration)
|| super.onSupportNavigateUp();
}
}