android 反射以及应用

这是效果图,通过反射hook了所有的view的点击事件,有时候我们项目做完了,但是领导要求按钮不能重复点击,大家可能会使用时间来简单的过滤,比如说2s内不能重复点击啊等等!那么我们不可能所有的界面一个点击事件的加吧,这时候就可以利用反射hook住所有的onclickListener事件

先来简单复习一下反射的基础知识

第一步,怎样获取要反射的类对象?注意这里是类对象要区别类和对象的关系。有如下三种方式(采用大家都经常用的一个类Student)
   第一种:Student.class;
   第二种:new Student().getClass();
   第三种:Class.forName(包名+类名);
前面两种都有一个致命的缺点,需要导入进来的包,才能拿到这个对象,用的最广泛的是第三种Class.forName();所以我们关键介绍第三种。
首先是Student的源码
package cms.dialog.easymining.com.lib;
public class Student {

private String name;
private int age;

public Student() {}

private Student(String name){
this.name = name;
}

public Student(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
System.out.println("调用getName" + name);
return name;
}

public int getAge() {
System.out.println("调用getAge" + age);
return age;
}

public void setMName(String name) {
this.name = name;
System.out.println("name是" + name);
}

private void setNameAndAge(String name, int age) {
this.age = age;
this.name = name;
System.out.println("setNameAndAge" + name + "--" + age);
}

}
/***
 * 采用默认构造方法实例化对象
 */
Class mClass = Class.forName("cms.dialog.easymining.com.lib.Student");
Constructor constructor = mClass.getConstructor();
Object mStu = constructor.newInstance();
/***
 * 采用参数构造
 */
Class mClass = Class.forName("cms.dialog.easymining.com.lib.Student");
Constructor constructor1 = mClass.getConstructor(String.class,int.class);
constructor1.setAccessible(true);
Object object = constructor1.newInstance("晓强",18);
/***
 * 调用private的有参构造方法
 */
Class mClass = Class.forName("cms.dialog.easymining.com.lib.Student");
Constructor constructor  = mClass.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Object object = constructor.newInstance("罗志祥");

这是两种实例化反射对象的方法
中间的constructor1.setAccessible方法的意思是获取到反射的权限,如果构造方法为私有的则需要设置为true。

第二步反射调用方法
/***
 * 调用无参的方法
 */
Method nameMethod = mClass.getDeclaredMethod("getName");
nameMethod.setAccessible(true);
nameMethod.invoke(object);
/***
 * 反射调用setMName方法
 */
Method getNameMethod = mClass.getDeclaredMethod("setMName",String.class);
getNameMethod.invoke(mStu,"吴彦祖");

/***
 * 反射调用setNameAndAge
 */
Method setNameAndAgeMethod = mClass.getDeclaredMethod("setNameAndAge",String.class,int.class);
setNameAndAgeMethod.setAccessible(true);
setNameAndAgeMethod.invoke(mStu,"阿娇",18);

第三步反射设置属性的值
/***
 * 反射调用属性
 */
Field nameField = mClass.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(mStu,"谢霆锋");

留下一些思考,如何反射内部类?如果把反射应用在android里面。

应用:反射android里面所有的onclickListener事件

package chihane.jdaddressselector.demo.hook;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import chihane.jdaddressselector.demo.R;

public class HookListenerActivity extends Activity {

    @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hook_listener);

        Button button = (Button) this.findViewById(R.id.id_btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                Toast.makeText(getApplicationContext(), "点击事件", Toast.LENGTH_SHORT).show();
            }
        });

        getWindow().getDecorView().post(new Runnable() {
            @Override public void run() {
                View decorView = getWindow().getDecorView();
                HookClickListenerUtils.getInstance().hookDecorViewClick(decorView);
            }
        });
    }


    @Override public void onAttachedToWindow() {
        super.onAttachedToWindow();
        Log.e("onAttachedToWindow", "onAttachedToWindow");
        // 第二种方式
//        View decorView = getWindow().getDecorView();
//        HookClickListenerUtils.getInstance().hookDecorViewClick(decorView);
    }

    @Override public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        Log.e("onWindowFocusChanged", "onWindowFocusChanged中" + hasFocus);

    }


}
package chihane.jdaddressselector.demo.hook;

import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import java.lang.reflect.Field;
import java.lang.reflect.Method;


public class HookClickListenerUtils {

    private static HookClickListenerUtils mHookClickListenerUtils;

    private HookClickListenerUtils() {
    }

    public static HookClickListenerUtils getInstance() {
        synchronized ("getInstance") {
            if (mHookClickListenerUtils == null) {
                mHookClickListenerUtils = new HookClickListenerUtils();
            }
        }
        return mHookClickListenerUtils;
    }

    /***
     * 递归调用
     * @param decorView
     */
    public void hookDecorViewClick(View decorView) {
        if (decorView instanceof ViewGroup) {
            int count = ((ViewGroup) decorView).getChildCount();
            for (int i = 0; i < count; i++) {
                if (((ViewGroup) decorView).getChildAt(i) instanceof ViewGroup) {
                    hookDecorViewClick(((ViewGroup) decorView).getChildAt(i));
                } else {
                    hookViewClick(((ViewGroup) decorView).getChildAt(i));
                }
            }
        } else {
            hookViewClick(decorView);
        }
    }

    public void hookViewClick(View view) {
        try {
            Class viewClass = Class.forName("android.view.View");
            Method getListenerInfoMethod = viewClass.getDeclaredMethod("getListenerInfo");
            if (!getListenerInfoMethod.isAccessible()) {
                getListenerInfoMethod.setAccessible(true);
            }
            Object listenerInfoObject = getListenerInfoMethod.invoke(view);// 反射view中的getListenerInfo方法

            // 第二步:获取到view中的ListenerInfo中的mOnClickListener属性
            Class mListenerInfoClass = Class.forName("android.view.View$ListenerInfo");
            Field mOnClickListenerField = mListenerInfoClass.getDeclaredField("mOnClickListener");
            mOnClickListenerField.setAccessible(true);
            // 这里为何传入的View.OnClickListener为Field.get()的值
            mOnClickListenerField.set(listenerInfoObject, new HookClickListener((View.OnClickListener) mOnClickListenerField.get(listenerInfoObject)));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static class HookClickListener implements View.OnClickListener {

        private View.OnClickListener onClickListener;

        public HookClickListener(View.OnClickListener onClickListener) {
            this.onClickListener = onClickListener;
        }

        @Override public void onClick(View v) {
            Toast.makeText(v.getContext(), "hook住点击事件了,禽兽", Toast.LENGTH_SHORT).show();
            if (onClickListener != null) {
                onClickListener.onClick(v);
            }
        }
    }

}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <Button
        android:id="@+id/id_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="点击事件"
        />

</LinearLayout>

反射这块经常用到,像cglib,hook以及一些框架底层都有使用,还有最开始接触java的时候,使用的Spring框架都有使用反射。所以这块还是挺重要的。

参考链接
java基础反射
知乎
反射在android的调用
反射activity界面的所有onclick事件
https://www.jianshu.com/p/dfc6e3989511
反射包括recyclerview里面的onclick事件
https://blog.csdn.net/zhongwn/article/details/54381337






猜你喜欢

转载自blog.csdn.net/u010648159/article/details/79673655