【Android】注解、反射、泛型实战操作

示例1:实现ButerKnife库的自动获取view

声明view的注解:

java 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectView {
    @IdRes int value(); //view id
}

在activity中获取注解value实现findViewById:

java 复制代码
public class InjectUtil {

    public static void injectView(Activity activity) {
        Class<? extends Activity> aClass = activity.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field: declaredFields) {
            if (field.isAnnotationPresent(InjectView.class)) { //如果该field含有InjectView类注解
                InjectView annotation = field.getAnnotation(InjectView.class); //获取该注解类型对象
                View view = activity.findViewById(annotation.value());
                field.setAccessible(true);
                try {
                    field.set(activity, view);  //将该view设置给该属性对象
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

使用:

java 复制代码
class MainActivity : ComponentActivity() {
    @InjectView(R.id.btn_click)
    private var button: Button ?= null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        InjectUtil.injectView(this)
}
示例2:实现ARouter库的autowire获取传递数据

Autowired注解:

java 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {
    String value();
}

获取activity的intent,从带有Autowired注解的filed中获取intent的key,将对象设置给该filed。

java 复制代码
public class InjectUtil {

    public static void injectAutowired(Activity activity) {
        Class<? extends Activity> aClass = activity.getClass();
        Intent intent = activity.getIntent();
        Bundle extras = intent.getExtras();
        if (extras == null) {
            return;
        }

        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field: declaredFields) {
            if (field.isAnnotationPresent(Autowired.class)) {
                Autowired annotation = field.getAnnotation(Autowired.class);
                String key = TextUtils.isEmpty(annotation.value()) ? field.getName() : annotation.value(); //如果设置了value,key就是value,否则key就用name
                if (!key.isEmpty()) {
                    Object obj = extras.get(key);
                    field.setAccessible(true);
                    try {
                        field.set(activity, obj);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}

使用:

java 复制代码
public class MainActivity2 extends ComponentActivity {

    @Autowired("name")
    String name;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        InjectUtil.injectAutowired(this);

        setContentView(R.layout.activity_main2);

        Toast.makeText(getApplicationContext(), "我的名字: " + name, Toast.LENGTH_LONG).show();
    }
}
示例3:获取某个类某个方法的所有泛型和返回类型

Person类:

java 复制代码
public class Person {
    private String userName;
    private String password;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public List<Person> method(List<Person> persons) {
        return new ArrayList<>(persons);
    }
}
java 复制代码
public class ReflectionGeneric {

    @RequiresApi(api = Build.VERSION_CODES.P)
    public static void inject() throws NoSuchMethodException, ClassNotFoundException {
        Method method = Person.class.getMethod("method", List.class);
        Type[] genericParameterTypes = method.getGenericParameterTypes();

        //获取泛型类型
        for (Type genericParameterType : genericParameterTypes) {
            //获取泛型里面的实际参数类型
            Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                //获取非String类型的类
                if (actualTypeArgument.getTypeName().equals("java.lang.String")) {
                    String classNamePath = actualTypeArgument.getTypeName();
                    Class<?> aClass = Class.forName(classNamePath);
                    //获得所有属性
                    Arrays.stream(aClass.getDeclaredFields()).forEach(System.out::println);
                }
            }
        }

        //获取返回类型
        Type genericReturnType = method.getGenericReturnType();
        Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
        Type actualTypeArgument = actualTypeArguments[0];
        if (actualTypeArgument.getTypeName().equals("java.lang.String")) {
            //获取参数全类名
            String classNamePath = actualTypeArgument.getTypeName();
            //根据类路径获取class文件
            Class<?> returnClass = Class.forName(classNamePath);
            //获得所有属性
            Arrays.stream(returnClass.getDeclaredFields()).forEach(System.out::println);
        }

    }

}
示例4:实现Hilt库的自动注入对象

单例对象注解和非单例对象注解:

java 复制代码
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SimpleInject {

}

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SimpleSingleInject {

}
java 复制代码
/**
 * 自动注入创建对象
 */
public class Hilt {
    public static Map<Class<?>, Object> instances = new ConcurrentHashMap<>();
    public static <T> T getSingleInstance(Class<T> cls) {
        try {
            //判断是否走单例
            if (!cls.isAnnotationPresent(SimpleSingleInject.class)){
                return getInstance(cls);
            }
            Object obj = instances.get(cls);
            if (null != obj) {
                return (T)obj;
            }
            //使用类锁锁代码块
            synchronized (cls) {
                if (null == instances.get(cls)) {
                    obj = getInstance(cls);
                    instances.put(cls, obj);
                }

            }
            return (T)obj;
        }
        catch (Exception e) {
            throw new RuntimeException();
        }
    }

    public static<T> T getInstance (Class<T> cls){
        //如果该class中含有SimpleInject注解的filed,则反射创建该filed对象设置给该class
        try {
            T obj = cls.newInstance();
            //返回本类申明的字段包括非public,不包括父类
            Field[] declaredFields = cls.getDeclaredFields();
            for (Field f : declaredFields) {
                //判断字段是否包含指定注解类型
                if (f.isAnnotationPresent(SimpleInject.class)) {
                    //判断字段是否为私有
                    if (!f.isAccessible()) {
                        f.setAccessible(true);
                    }
                    //再次递归调用赋值
                    f.set(obj, getInstance(f.getType()));
                }

            }
            return obj;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

提供注解对象的module:

java 复制代码
public class Module {

    @SimpleInject
    Person person;
}

使用:

java 复制代码
public class MainActivity3 extends ComponentActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main2);

        Module singleInstance = Hilt.getSingleInstance(Module.class);

        singleInstance.person.setUserName("haha");
        singleInstance.person.setPassword("123456");

        Toast.makeText(getApplicationContext(), "我的名字: " + singleInstance.person.getUserName() + " 我的密码:" + singleInstance.person.getPassword(), Toast.LENGTH_LONG).show();
    }
}
示例5:实现Retrofit库动态代理获取请求方法的各个数据

GET、POST请求注解

java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
    String value();
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface POST {
    String value();
}

Retrofit的简单实现:动态代理获取method的GET、POST注解,发起请求:

java 复制代码
public class Retrofit {
    private OkHttpClient mOkHttpClient;
    private static final MediaType MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");

    public Retrofit(OkHttpClient mOkHttpClient) {
        this.mOkHttpClient = mOkHttpClient;
    }

    public <T> T create(Class<T> service) {
        Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service}, new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                Annotation[] annotations = method.getAnnotations();
                for (Annotation annotation: annotations) {
                    if (annotation instanceof GET) {
                        return parseGet(((GET) annotation).value());
                    } else if (annotation instanceof POST){
                        return parsePost(((POST) annotation).value(), method, objects);
                    }
                }
                return null;
            }
        });
        return null;
    }

    /**
     * 处理GET请求信息获取并发起请求
     * @param url
     * @return
     */
    private Call parseGet(String url) {
        Request request = new Request.Builder()
                .get()
                .url(url)
                .build();
        return mOkHttpClient.newCall(request);
    }

    /**
     * 处理POST请求信息获取并发起请求
     * @param url
     * @return
     */
    private Call parsePost(String url, Method method, Object[] args) {
        Type[] genericParameterTypes = method.getGenericParameterTypes();//获取方法泛型
        if (genericParameterTypes.length > 0) {
            Object o = new Gson().fromJson((String) args[0], genericParameterTypes[0]);
            Request request = new Request.Builder()
                    .url(url)
                    .post(RequestBody.create(MEDIA_TYPE, o.toString()))
                    .build();
            return mOkHttpClient.newCall(request);
        }
        return null;
    }
}
相关推荐
2501_915106324 分钟前
iOS 打包 IPA 全流程详解,签名配置、工具选择与跨平台上传实战指南
android·macos·ios·小程序·uni-app·cocoa·iphone
超低空9 分钟前
Android MediaSession深度解析:车载音乐播放器完整案例
android·架构·客户端
00后程序员张10 分钟前
iOS 混淆实操指南多工具组合实现 IPA 混淆、加固与发布治理 IPA 加固
android·ios·小程序·https·uni-app·iphone·webview
xiaoshiquan12061 小时前
as强制过滤指定依赖版本库,解决该依赖不同版本冲突
android
2501_929157683 小时前
Switch 20.5.0系统最新PSP模拟器懒人包
android·游戏·ios·pdf
用户094 小时前
Kotlin Flow的6个必知高阶技巧
android·面试·kotlin
用户094 小时前
Flutter插件与包的本质差异
android·flutter·面试
用户094 小时前
Jetpack Compose静态与动态CompositionLocal深度解析
android·面试·kotlin
聆风吟º7 小时前
【Spring Boot 报错已解决】别让端口配置卡壳!Spring Boot “Binding to target failed” 报错解决思路
android·java·spring boot
非专业程序员Ping15 小时前
HarfBuzz概览
android·ios·swift·font