Hook中文名"鉤子",主要作用是在事件傳遞過(guò)程中對(duì)事件進(jìn)行攔截、修改碱茁、監(jiān)聽(tīng),將自身的代碼動(dòng)態(tài)性替換進(jìn)去,當(dāng)這些方法被調(diào)用時(shí),保證執(zhí)行的是我們自己的代碼,已達(dá)到我們預(yù)期的效果零聚。
我們先看一個(gè)需求: 在不修改一下代碼的情況下,通過(guò)Hook把((Button) v).getText()的內(nèi)容給改為“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();
}
});
}
通過(guò)看源碼(如下幾幅圖所示)江锨,我們想要?jiǎng)討B(tài)的把((Button) v).getText()的內(nèi)容給改了评也,那么我們就要使用動(dòng)態(tài)代理監(jiān)聽(tīng)View.OnClickListener這個(gè)接口,并把view的mListenerInfo中的mOnClickListener替換為我們自己的動(dòng)態(tài)代理(通過(guò)反射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();
}
});
//在不修改以上代碼的情況下锋玲,通過(guò)Hook把((Button) v).getText()的內(nèi)容給改了
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對(duì)象
Class mViewClass = Class.forName("android.view.View");
Method getListenerInfoMethod = mViewClass.getDeclaredMethod("getListenerInfo");
getListenerInfoMethod.setAccessible(true);
Object mListenerInfo = getListenerInfoMethod.invoke(view);
//獲取ListenerInfo對(duì)象中的mOnClickListener屬性
Class mListenerInfoClass = Class.forName("android.view.View$ListenerInfo");
Field mOnClickListenerField = mListenerInfoClass.getField("mOnClickListener");
final Object mOnClickListenerObj = mOnClickListenerField.get(mListenerInfo);
//監(jiān)聽(tīng)onClick 當(dāng)用戶點(diǎn)擊按鈕的時(shí)候——>onClick(View v)景用,我們自己要先攔截這個(gè)事件
Object mOnClickListenerProxy = Proxy.newProxyInstance(MainActivity.class.getClassLoader(),//1、加載的類
new Class[]{View.OnClickListener.class},//2惭蹂、要監(jiān)聽(tīng)的接口伞插,監(jiān)聽(tīng)什么接口,就返回什么接口
new InvocationHandler() {//3盾碗、監(jiān)聽(tīng)方法里面的回調(diào)
@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);
}
});
//替換的我們自己的動(dòng)態(tài)代理
mOnClickListenerField.set(mListenerInfo, mOnClickListenerProxy);
}
}