大家好,又见面了,我是你们的朋友全栈君。
相信很多搞机的朋友都玩过Xposed, 它实现了很多不可思议的功能。它是怎么实现的呢?这里就得提到我们的Hook技术了。
关于 Android 中的 Hook 机制,大致有两个方式:
- 要 root 权限,直接 Hook 系统,可以干掉所有的 App。
- 免 root 权限,但是只能 Hook 自身,对系统其它 App 无能为力。
时间所限,这里不展开了。
知识点:
今天主要讲的是利用hook技术,将点击事件劫持,做点啥呢?你可以随意,我这里是将双击给屏蔽了。免得点击过快打开多个界面。
1、寻找Hook点
这一步比较关键,也是个难点。Android中主要是依靠分析系统源码类来做到的,首先我们得找到被Hook的对象,我称之为Hook点;什么样的对象比较好Hook呢?自然是容易找到的对象。什么样的对象容易找到?静态变量和单例;在一个进程之内,静态变量和单例变量是相对不容易发生变化的,因此非常容易定位,而普通的对象则要么无法标志,要么容易改变。我们根据这个原则找到所谓的Hook。
去看setOnClickListener里面做了什么?
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
看完了上面,就能猜到我们设置的Listener最终是被赋值给ListenerInfo的mOnClickListener成员了,ListenerInfo的实例可以说是信息的载体,那么很简单,只要把mOnClickListener,在ListenerInfo中还有mOnLongClickListener,mOnFocusChangeListener两个成员,分别对应了长按事件与焦点变化事件,所以处理长按事件与焦点变化事件与此类似。
public class HookViewClickUtil {
public static HookViewClickUtil getInstance() {
return UtilHolder.mHookViewClickUtil;
}
private static class UtilHolder {
private static HookViewClickUtil mHookViewClickUtil = new HookViewClickUtil();
}
public static void hookView(View view) {
try {
Class viewClazz = Class.forName("android.view.View");
//事件监听器都是这个实例保存的
Method listenerInfoMethod = viewClazz.getDeclaredMethod("getListenerInfo");
if (!listenerInfoMethod.isAccessible()) {
listenerInfoMethod.setAccessible(true);
}
Object listenerInfoObj = listenerInfoMethod.invoke(view);
Class listenerInfoClazz = Class.forName("android.view.View$ListenerInfo");
Field onClickListenerField = listenerInfoClazz.getDeclaredField("mOnClickListener");
if (!onClickListenerField.isAccessible()) {
onClickListenerField.setAccessible(true);
}
View.OnClickListener mOnClickListener = (View.OnClickListener) onClickListenerField.get(listenerInfoObj);
//自定义代理事件监听器
View.OnClickListener onClickListenerProxy = new OnClickListenerProxy(mOnClickListener);
//更换
onClickListenerField.set(listenerInfoObj, onClickListenerProxy);
} catch (Exception e) {
e.printStackTrace();
}
}
//自定义的代理事件监听器
private static class OnClickListenerProxy implements View.OnClickListener {
private View.OnClickListener object;
private int MIN_CLICK_DELAY_TIME = 1000;
private long lastClickTime = 0;
private OnClickListenerProxy(View.OnClickListener object) {
this.object = object;
}
@Override
public void onClick(View v) {
//点击时间控制
long currentTime = Calendar.getInstance().getTimeInMillis();
if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) {
lastClickTime = currentTime;
Log.e("OnClickListenerProxy", "OnClickListenerProxy");
if (object != null) object.onClick(v);
}
}
}
}
使用起来也是非常简单,首先在MainActivity的View渲染完毕的时候进行注入,即在 getWindow().getDecorView().post()中。
public class MainActivity extends Activity {
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final View btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("MainActivity","Button 被点击了");
}
});
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
HookViewClickUtil.hookView(btn);
}
});
}
}
原文出处:https://blog.csdn.net/u013263323/article/details/55094822
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/141211.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...