Android HooK !

本文介绍了一种在 Android 开发中使用 Hook 技术对所有 View 的点击事件进行统计的方法,通过在 BaseActivity 中统一处理,实现了全入侵式的事件注入,有效避免了对每个单独组件进行点击统计的繁琐工作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前一直听说过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();
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值