Hook技術: Hook就是有一段程序邏輯一直走下去,我們可以捕獲到其中間的一些邏輯,加於處理然後再讓他接着執行下去;
比如Android裏面的setOnclickListener這個方法. 正常我們是這樣操作的
TextView textView = findViewById(R.id.act_invoke_tv);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(InvokeOnClickActivity.this, ((TextView)v).getText(), Toast.LENGTH_SHORT).show();
}
});
怎麼在不改變這段代碼的情況下,把裏面的的Toast內容給替換掉
分析思路: 1肯定要用到動態代碼,動態代碼用到的是Proxy這個類
Object proxyInstance = Proxy.newProxyInstance(getClassLoader(), new Class[]{View.OnClickListener.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Toast.makeText(InvokeOnClickActivity.this, "已經hokd到方法了", Toast.LENGTH_SHORT).show();
return method.invoke(mOnClickListener, null);
}
});
等我們通過反射獲取到textView設置的mClickListener的時候就將其替換
首先我們在setOnclickListener的時候傳的是一個View.OnClickListener過去
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
這方法裏面將我們傳入的OnclickListener附值給了getListenerInfo()獲取的類的一個成員變量,而getListenerInfo()這個方法獲取的是ListenerInfo這個類,也就是說,在這裏操作的是將我們設置的OnClickListener傳給了ListenerInfo,是他裏面的一個成員變量,
所以我們第一步就是獲取到View裏面的getListenerInfo這個方法
Class.forName這個方法可以獲取到一個Class類,然後通過這個Class類獲取到getListenerInfo這個方法,然後再執行這個方法代碼如下
Class<?> viewClass = Class.forName("android.view.View");
Method getListenerInfoMethod = viewClass.getDeclaredMethod("getListenerInfo");
getListenerInfo.setAccessible(true);
Object listenerInfo = getListenerInfoMethod.invoke(textView)
listenerInfo則就是通過VIew裏的getListenerInfo獲取到的對象 因爲OnClickListener附給了ListenerInfo裏的一個成員變量,這裏我們繼續通過反射獲取到它的成員變量mOnClickListener
Object listenerInfoClass = Class.forName(android.view.View$ListenerInfo);
//獲取到名字爲mOnClickListener的成員變量
Field onClickListenerField = listenerInfoClass.getDeclaredField("mOnClickListener");
//獲取到原來設置的OnClickListener,其實可以不用這個,只是爲了不影響正常邏輯,因爲我們代理完了要重新
//執行原來的方法
Object mOnClickListener = onClickListenerField.get(listenerInfo);
最後一步就是替換操作了
onClickListenerField.set(listenerInfo,proxyInstance);這樣就遠的成自己的了,當我們點擊Teview的點擊事件的時候就會先走到我們代碼的proxyInstance裏面的invoke方法,