之前一直听说过Hook啥的,其实自己也没真实研究过到底是什么。最近研究Replugin才觉得深入研究这个东西。
Hook是一种思想,也就是将原来的事件,替换到我们自己的事件,方便我们做一些切入处理。目的是不修改原来的代码,同时也避免遗漏的N多类里面处理。
下面将展示一段android View Hook的事件处理。
案例如下:
版本1着急上架,冲忙着写的简单就上架了。
现在公司相对之前版本的点击事件,做点击统计。一个个加,不累死啊。
现在就想到了Hook到所有的View,增加点击统计。
那就在BaseActivity里面做统一的获取View处理。这样就形成全入侵注入事件,这样也不容易遗漏事件。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="energy.trinabess.com.retest.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:tag="button111111"
tools:layout_editor_absoluteX="90dp"
tools:layout_editor_absoluteY="186dp"
tools:ignore="MissingConstraints"/>
<Button
android:id="@+id/ButtonTO"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ButtonTO"
android:tag="button2222"
tools:layout_editor_absoluteX="90dp"
tools:layout_editor_absoluteY="265dp"
tools:ignore="MissingConstraints"/>
</LinearLayout>
java代码,同时里面的也做注释了
public class MainActivity extends AppCompatActivity {
private Button button;
private Button ButtonTO;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button= (Button) findViewById(R.id.button);
ButtonTO= (Button) findViewById(R.id.ButtonTO);
button.setOnClickListener(mOnClickListener);
ButtonTO.setOnClickListener(mOnClickListener);
// 模拟View事件注入,可在BaseActivity里面做统一处理!
injectionHookView(button);
injectionHookView(ButtonTO);
}
private View.OnClickListener mOnClickListener=new View.OnClickListener() {
@Override
public void onClick(View view) {
showToast(view.getTag().toString());
}
};
// Test Hook this Activity's View,First we must find all views has on Click Event!
public void injectionHookView(View view) {
try {
Class mClassView = Class.forName("android.view.View");
Method method = mClassView.getDeclaredMethod("getListenerInfo");
method.setAccessible(true);
// 获取getListenerInfo ListenerInfo
// mListenerInfo 是 android.view.View.ListenerInfo 是内部静态类。没办法,只能用顶级Objeck来代替
Object mListenerInfo = method.invoke(view);
Class mClassListenerInfo = Class.forName("android.view.View$ListenerInfo");
Field mFeildOnClickListener = mClassListenerInfo.getDeclaredField("mOnClickListener");
mFeildOnClickListener.setAccessible(true);
// 获取原来的Onclick 对象。 次对象即使上面new的mOnClickListener ,可debug看追踪地址
View.OnClickListener mOnClickListenerObject = (View.OnClickListener) mFeildOnClickListener.get(mListenerInfo);
if(mOnClickListenerObject == mOnClickListener) {
Log.d("mOnClickListener","mOnClickListener 地址和 mOnClickListenerObject 是一样的!");
}else {
Log.d("mOnClickListener","呵呵哒...");
}
// 新创建我们的自己的监听器,将Hook到系统的方法里面的,全面入侵监听,但是不破坏原有的代码。
View.OnClickListener onClickListenerProxy = new OnClickProxy(mOnClickListenerObject, new HookClick() {
@Override
public void doSomeThing(View view) {
showToast("HookClick 做一些其他事情处理");
}
});
// 替换到原有的View.OnClickListener !
mFeildOnClickListener.set(mListenerInfo, onClickListenerProxy);
} catch (Exception e) {
e.printStackTrace();
}
}
public class OnClickProxy implements View.OnClickListener{
private View.OnClickListener mOnClickListener; // 原始的对象,需要hook的对象
private HookClick mHook;
public OnClickProxy(View.OnClickListener mOnClickListener, HookClick mHook){
this.mOnClickListener = mOnClickListener;
this.mHook = mHook;
}
@Override
public void onClick(View v) {
if(mHook != null) {
// 先喝个小酒,吃个饭,再处理点击事件!
mHook.doSomeThing(v);
}
// 此处是原来的事件,恢复处理。也是核心Hook的东西
if(mOnClickListener != null) {
mOnClickListener.onClick(v);
}
}
}
// 想要做一些处理。
public interface HookClick{
public void doSomeThing(View view);
}
}
private void showToast(String mStr)
{
Toast.makeText(this,mStr,Toast.LENGTH_LONG).show();
}