詳細講解下Hook技術,以Hook點擊事件來示範

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方法,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章