Hook中文名"鉤子",主要作用是在事件傳遞過程中對事件進行攔截、修改、監聽,將自身的代碼動態性替換進去,當這些方法被調用時,保證執行的是我們自己的代碼,已達到我們預期的效果。
我們先看一個需求: 在不修改一下代碼的情況下,通過Hook把((Button) v).getText()的內容給改為“migill”
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "" + ((Button) v).getText(), Toast.LENGTH_SHORT).show();
}
});
}
通過看源碼(如下幾幅圖所示),我們想要動態的把((Button) v).getText()的內容給改了,那么我們就要使用動態代理監聽View.OnClickListener這個接口,并把view的mListenerInfo中的mOnClickListener替換為我們自己的動態代理(通過反射mOnClickListenerField.set(mListenerInfo, mOnClickListenerProxy));
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "" + ((Button) v).getText(), Toast.LENGTH_SHORT).show();
}
});
//在不修改以上代碼的情況下,通過Hook把((Button) v).getText()的內容給改了
try {
hook(button);
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "Hook失敗" + e.toString(), Toast.LENGTH_SHORT).show();
}
}
private void hook(View view) throws Exception {
//獲取view的mListenerInfo對象
Class mViewClass = Class.forName("android.view.View");
Method getListenerInfoMethod = mViewClass.getDeclaredMethod("getListenerInfo");
getListenerInfoMethod.setAccessible(true);
Object mListenerInfo = getListenerInfoMethod.invoke(view);
//獲取ListenerInfo對象中的mOnClickListener屬性
Class mListenerInfoClass = Class.forName("android.view.View$ListenerInfo");
Field mOnClickListenerField = mListenerInfoClass.getField("mOnClickListener");
final Object mOnClickListenerObj = mOnClickListenerField.get(mListenerInfo);
//監聽onClick 當用戶點擊按鈕的時候——>onClick(View v),我們自己要先攔截這個事件
Object mOnClickListenerProxy = Proxy.newProxyInstance(MainActivity.class.getClassLoader(),//1、加載的類
new Class[]{View.OnClickListener.class},//2、要監聽的接口,監聽什么接口,就返回什么接口
new InvocationHandler() {//3、監聽方法里面的回調
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e("migill", "攔截到了 OnClickListener的方法了");
//加入自己的邏輯
Button buttonProxy = new Button(MainActivity.this);
buttonProxy.setText("migill");
//method方法就是onClick(View v)
return method.invoke(mOnClickListenerObj, buttonProxy);
}
});
//替換的我們自己的動態代理
mOnClickListenerField.set(mListenerInfo, mOnClickListenerProxy);
}
}