1. Android中的IOC(DI)框架

1.1 ViewUtils简介(xUtils中的四大部分之一)

  • IOC: Inverse of Controller 控制反转。

  • DI: Dependency Inject 依赖注入

  • 完全注解方式就可以进行UI绑定和事件绑定。

  • 无需findViewById和setClickListener等。

1.2 ViewUtils使用

compile 'org.xutils:xutils:3.5.0'
compile 'com.jiechic.library:xUtils:2.6.14'
// xUtils的view注解要求必须提供id,以使代码混淆不受影响。
@ViewInject(R.id.textView)
TextView textView; //@ViewInject(vale=R.id.textView, parentId=R.id.parentView)
//TextView textView; @ResInject(id = R.string.label, type = ResType.String)
private String label; // 取消了之前使用方法名绑定事件的方式,使用id绑定不受混淆影响
// 支持绑定多个id @OnClick({R.id.id1, R.id.id2, R.id.id3})
// or @OnClick(value={R.id.id1, R.id.id2, R.id.id3}, parentId={R.id.pid1, R.id.pid2, R.id.pid3})
// 更多事件支持参见ViewCommonEventListener类和包com.lidroid.xutils.view.annotation.event。
@OnClick(R.id.test_button)
public void testButtonClick(View v) { // 方法签名必须和接口中的要求一致
...
}
...
//在Activity中注入:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ViewUtils.inject(this); //注入view和事件
...
textView.setText("some text...");
...
}
//在Fragment中注入:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.bitmap_fragment, container, false); // 加载fragment布局
ViewUtils.inject(this, view); //注入view和事件
...
}
//在PreferenceFragment中注入:
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ViewUtils.inject(this, getPreferenceScreen()); //注入view和事件
...
}
// 其他重载
// inject(View view);
// inject(Activity activity)
// inject(PreferenceActivity preferenceActivity)
// inject(Object handler, View view)
// inject(Object handler, Activity activity)
// inject(Object handler, PreferenceGroup preferenceGroup)
// inject(Object handler, PreferenceActivity preferenceActivity)

1.3 反射:调用(设置)类私有成员和私有方法

定义的User类:

package cn.Douzi.Annotation_Test;

public class User {

    @ViewInject(age=23, name="张三")
private String name; private int age; private String eat(String eat) {
System.out.println("eat: " + eat);
return eat + "真好吃";
} @Override
public String toString() {
// TODO Auto-generated method stub
return "User [name =" + name + ", age = " + age + "]";
}
}

反射调用私有对象,设置该类对象:

        Class cls = User.class;

        /**
* 2. 将这个字节码中的name字段获取到
*/
// cls.getField(name); //这个方法只能获取声明为public的字段
Field declaredField = cls.getDeclaredField("name");
Field declaredFieldAge = cls.getDeclaredField("age");
     declaredField.setAccessible(true); //设置允许访问,其实就是允许暴力反射
declaredFieldAge.setAccessible(true); //给user对象的declaredField设置为name
declaredField.set(user, name);
declaredFieldAge.set(user, age);

反射调用私有函数,设置该类函数:

        //通过反射调用User对象的eat方法
Method declaredMethod = cls.getDeclaredMethod("eat", String.class);
//暴力反射调用改方法
declaredMethod.setAccessible(true);
Object result = declaredMethod.invoke(user, "牛肉拉面"); # 设置函数参数列表 System.out.println(result);

1.4 反射 和 自定义注解类

自定义注解类:

package cn.Douzi.Annotation_Test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import javax.lang.model.element.Element; /**
* @Taget(ElementType.METHOD) //用于方法
*
*/
@Target(ElementType.FIELD) //用于限制当前自定义注解类作用的对象
//@Retention(RetentionPolicy.SOURCE) //该注解只会作用在源码阶段,当源码编译成字节码时注解信息就被清除
//@Retention(RetentionPolicy.CLASS) //该注解只会作用在字节码阶段,但是当虚拟机加载这个字节码时,注解信息被清除
@Retention(RetentionPolicy.RUNTIME) //该注解类一直保留到加载到虚拟机中
public @interface ViewInject { int age();
String name(); }
package cn.Douzi.Annotation_Test;

public class User {

    @ViewInject(age=23, name="张三")
private String name; private int age; private String eat(String eat) {
System.out.println("eat: " + eat);
return eat + "真好吃";
} @Override
public String toString() {
// TODO Auto-generated method stub
return "User [name =" + name + ", age = " + age + "]";
}
}

1.5 测试自定义注解类

如何获取自定义注解对象(User)中参数

        Class cls = User.class;

        /**
* 2. 将这个字节码中的name字段获取到
*/
// cls.getField(name); //这个方法只能获取声明为public的字段
Field declaredField = cls.getDeclaredField("name");
Field declaredFieldAge = cls.getDeclaredField("age"); /**
* 3. 将当前字段上的注解对象获取到
*/
ViewInject viewInject = declaredField.getAnnotation(ViewInject.class);
if (viewInject != null) {
/*
* 4.获取自定义注解对象中的参数
*/
int age = viewInject.age();
String name = viewInject.name();
System.out.println("name: " + name + ", age = " + age);
}

测试是否能获取到注解对象(完整代码)

package cn.Douzi.Annotation_Test;

import java.lang.reflect.Field;

public class AnnotationMainTest {

    public static void main(String[] args) throws NoSuchFieldException, SecurityException {

        //获取User类中name字段上的自定义注解的值,然后将该值的age通过反射设置给User对象的age属性
//将name设置给User对象的name属性
User user = new User();
/**
* 反射:
* 1. 先去获取到User的字节码
*/
// user.getClass();
// User.class
// Class.forName("xxxx")
Class cls = User.class; /**
* 2. 将这个字节码中的name字段获取到
*/
// cls.getField(name); //这个方法只能获取声明为public的字段
Field declaredField = cls.getDeclaredField("name"); /**
* 3. 将当前字段上的注解对象获取到
*/
ViewInject viewInject = declaredField.getAnnotation(ViewInject.class);
if (viewInject != null) {
/*
* 4.获取自定义注解对象中的参数
*/
int age = viewInject.age();
String name = viewInject.name();
System.out.println("name: " + name + ", age = " + age);
}
else {
System.out.println("字段上无自定义注解");
} } }

1.6 通过暴力反射将获取到的注解值设置给User对象 (完整代码)

package cn.Douzi.Annotation_Test;

import java.lang.reflect.Field;

public class AnnotationMainTest {

    public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {

        //获取User类中name字段上的自定义注解的值,然后将该值的age通过反射设置给User对象的age属性
//将name设置给User对象的name属性
User user = new User();
/**
* 反射:
* 1. 先去获取到User的字节码
*/
// user.getClass();
// User.class
// Class.forName("xxxx")
Class cls = User.class; /**
* 2. 将这个字节码中的name字段获取到
*/
// cls.getField(name); //这个方法只能获取声明为public的字段
Field declaredField = cls.getDeclaredField("name");
Field declaredFieldAge = cls.getDeclaredField("age"); /**
* 3. 将当前字段上的注解对象获取到
*/
ViewInject viewInject = declaredField.getAnnotation(ViewInject.class);
if (viewInject != null) {
/*
* 4.获取自定义注解对象中的参数
*/
int age = viewInject.age();
String name = viewInject.name();
System.out.println("name: " + name + ", age = " + age); /*
* 5. 通过反射将这个两个值设置给User对象
*/
declaredField.setAccessible(true); //设置允许访问,其实就是允许暴力反射
declaredFieldAge.setAccessible(true); //给user对象的declaredField设置为name
declaredField.set(user, name);
declaredFieldAge.set(user, age); System.out.println(user.toString()); }
else {
System.out.println("字段上无自定义注解");
} } }

1.7 通过反射调用User对象的eat方法 (完整代码)

package cn.Douzi.Annotation_Test;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; public class AnnotationMainTest { public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { //获取User类中name字段上的自定义注解的值,然后将该值的age通过反射设置给User对象的age属性
//将name设置给User对象的name属性
User user = new User();
/**
* 反射:
* 1. 先去获取到User的字节码
*/
// user.getClass();
// User.class
// Class.forName("xxxx")
Class cls = User.class; /**
* 2. 将这个字节码中的name字段获取到
*/
// cls.getField(name); //这个方法只能获取声明为public的字段
Field declaredField = cls.getDeclaredField("name");
Field declaredFieldAge = cls.getDeclaredField("age"); /**
* 3. 将当前字段上的注解对象获取到
*/
ViewInject viewInject = declaredField.getAnnotation(ViewInject.class);
if (viewInject != null) {
/*
* 4.获取自定义注解对象中的参数
*/
int age = viewInject.age();
String name = viewInject.name();
System.out.println("name: " + name + ", age = " + age); /*
* 5. 通过反射将这个两个值设置给User对象
*/
declaredField.setAccessible(true); //设置允许访问,其实就是允许暴力反射
declaredFieldAge.setAccessible(true); //给user对象的declaredField设置为name
declaredField.set(user, name);
declaredFieldAge.set(user, age); System.out.println(user.toString());
}
else {
System.out.println("字段上无自定义注解");
} //通过反射调用User对象的eat方法
Method declaredMethod = cls.getDeclaredMethod("eat", String.class);
//暴力反射调用改方法
declaredMethod.setAccessible(true);
Object result = declaredMethod.invoke(user, "牛肉拉面"); System.out.println(result);
} }

2. 自定义ViewUtils (Android)

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.douzi.myviewutils.MainActivity"> <TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!!!!"
android:textSize="20dp"
/> <TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World2222222222!!!!"
android:textSize="20dp"
/> <Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="OKKKK"
/> </LinearLayout>

注解类:

package com.douzi.myviewutils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* Created by Douzi on 2019/3/11.
*/ @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ViewInject { int value(); //这里必须叫value,否则绑定的时候需要写: xx=R.id.xx }

实现 ViewUtils的inject方法(使用反射)

package com.douzi.myviewutils;

import android.app.Activity;
import android.view.View; import java.lang.reflect.Field; /**
* Created by Douzi on 2019/3/11.
*/ public class ViewUtils { public static void inject(Activity activity) { //绑定控件
try {
bindView(activity);
} catch (IllegalAccessException e) {
e.printStackTrace();
} } private static void bindView(Activity activity) throws IllegalAccessException {
/**
* 1. 获取Activity的字节码
*/
Class clazz = activity.getClass();
/**
* 2. 获取到该字节码中所有的Filed
*/
Field[] declaredFields = clazz.getDeclaredFields(); /**
* 3. 判断哪些是我们想要的字段(只有添加了ViewInject注解的字段)
*/
for (Field field : declaredFields) {
//获取字段上面的注解
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {
/**
* 4. 获取当前注解的值
*/
int resId = viewInject.value();
/**
* 5. 通过调用Activity的findViewById方法,获取当前id为resId的控件
*/
View view = activity.findViewById(resId);
/**
* 6. 将当前的View设置给当前的Filed
*/
field.setAccessible(true);
//给Activity对象的filed字段设置值为view对象
field.set(activity, view); }
else {
// nothing
}
}
} }

测试注解的功能

package com.douzi.myviewutils;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView; public class MainActivity extends AppCompatActivity { @ViewInject(R.id.tv1)
TextView textView; @ViewInject(R.id.tv2)
TextView textView2; private int count; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); ViewUtils.inject(this); textView.setText("成功了!!");
textView2.setText("成功了2!!"); }
}

绑定点击事件实现(实现ViewUtils的OnClick实现)

注解类

package com.douzi.myviewutils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* Created by Douzi on 2019/3/11.
*/ @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick { int value(); }

ViewUtils里添加函数

public static void bindOnClick(final Activity activity) {
/**
* 1. 获取字节码
*/
Class clazz = activity.getClass();
/**
* 2. 获取字节码中所有的方法
*/
Method[] methods = clazz.getDeclaredMethods(); /**
* 3. 遍历方法, 找出方法上声明了OnClick注解的方法
*/
for (final Method method : methods) { /**
* 4. 获取当前方法的上的注解
*/
OnClick onClick = method.getAnnotation(OnClick.class); if (onClick != null) {
/**
* 5. 获取注解中的值
*/
int resId = onClick.value();
/**
* 6. 获取到id为resId的View
*/
final View view = activity.findViewById(resId);
/**
* 7. 将当前的View绑定点击事件
*/
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/**
* 8. 通过反射调用当前的用户的方法
*/
//设置可以
method.setAccessible(true); try {
method.invoke(activity, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
}
} }
public static void inject(Activity activity) {

        //绑定控件
try {
//绑定界面
bindView(activity); //绑定方法
bindOnClick(activity);
} catch (IllegalAccessException e) {
e.printStackTrace();
} }

测试注解功能

package com.douzi.myviewutils;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast; public class MainActivity extends AppCompatActivity { @ViewInject(R.id.tv1)
TextView textView; @ViewInject(R.id.tv2)
TextView textView2; private int count; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); ViewUtils.inject(this); textView.setText("成功了!!");
textView2.setText("成功了2!!"); } @OnClick(R.id.btn)
private void clickMe(View view) {
Toast.makeText(this, "点击我了", Toast.LENGTH_LONG).show();
} }

Android面试基础(一)IOC(DI)框架(ViewUtils)讲解_反射和自定义注解类的更多相关文章

  1. NET Core应用中实现与第三方IoC/DI框架的整合?

    NET Core应用中实现与第三方IoC/DI框架的整合? 我们知道整个ASP.NET Core建立在以ServiceCollection/ServiceProvider为核心的DI框架上,它甚至提供 ...

  2. 【Android 面试基础知识点整理】

    针对Android面试中常见的一些知识点整理,Max 仅仅是个搬运工.感谢本文中引用文章的各位作者,给大家分享了这么多优秀文章.对于当中的解析,是原作者个人见解,有错误和不准确的地方,也请大家积极指正 ...

  3. 如何在ASP.NET Core应用中实现与第三方IoC/DI框架的整合?

    我们知道整个ASP.NET Core建立在以ServiceCollection/ServiceProvider为核心的DI框架上,它甚至提供了扩展点使我们可以与第三方DI框架进行整合.对此比较了解的读 ...

  4. C++基础 (5) 第五天 重载new delete () 只能操作符 自定义string类

    1 昨日回顾 1.static 对整个类共享 可以直接用 类::方法 调用 如果是私有的 可以提供一个静态的访问静态成员的方法 2 自定义的数组类-重载操作符[] 3 重载new和delete 4 重 ...

  5. Android面试题目及其答案

    转自:http://blog.csdn.net/wwj_748/article/details/8868640 Android面试题目及其答案 1.Android dvm的进程和Linux的进程, 应 ...

  6. Android面试题目2

    1. 请描述下Activity的声明周期. onCreate->onStart->onRemuse->onPause->onStop->onRestart->onD ...

  7. 【Android面试】Android面试题集锦 (陆续更新)(最新2012-6-18) eoe上看到的

    ===============eoeAndroid社区推荐:======================= 1.Android开发新浪面试题[开发者必看哦]下载地址 http://www.eoeand ...

  8. 设计模式之初识IoC/DI(六)

    本篇和大家一起学习IoC和DI即控制反转和依赖注入. 当然听上去这词语非常的专业,真不知道是怎么组出来的,看上去难归看上去难,但稍微理解一下也就这么回事了. 首先我们要明白IoC/DI干嘛用的,不然别 ...

  9. Android自定义注解

    1.元注解   概念:用来定义其他注解的注解,自定义注解的时候,需要使用它来定义我们的注解.   在jdk 1.5之后提供了 java.lang.annotation 来支持注解功能   常见的四种元 ...

随机推荐

  1. 关于thinkphp5URL重写

    可以通过URL重写隐藏应用的入口文件index.php,下面是相关服务器的配置参考: [ Apache ] httpd.conf配置文件中加载了mod_rewrite.so模块 AllowOverri ...

  2. Codeforces Bubble Cup 11 J. Moonwalk challenge加强版

    题意 有一棵 \(n\) 个节点的树,每条边上有一个字符,有 \(m\) 次询问. 每次会选定两个点 \(u, v\) , \(u\) 到 \(v\) 的路径上的字符形成了一个字符串 \(T\) ,再 ...

  3. Leetcode 345. 反转字符串中的元音字母 By Python

    编写一个函数,以字符串作为输入,反转该字符串中的元音字母. 示例 1: 输入: "hello" 输出: "holle" 示例 2: 输入: "leet ...

  4. html概述和基本结构

    html概述 HTML是 HyperText Mark-up Language 的首字母简写,意思是超文本标记语言,超文本指的是超链接,标记指的是标签,是一种用来制作网页的语言,这种语言由一个个的标签 ...

  5. 152. Maximum Product Subarray 以及 讨论【最大连续子序列】

    题目大意: 连续最大子段积 题目思路: 最大值只能产生在一个正数x一个正数,一个负数乘一个负数,所以维护两个值,一个区间最大值,一个最小值 其他的话: 在讨论这个问题之前,我先来说一说大一刚开学就学了 ...

  6. BZOJ3456 城市规划 【多项式求ln】

    题目链接 BZOJ3456 题解 真是一道经典好题,至此已经写了分治\(NTT\),多项式求逆,多项式求\(ln\)三种写法 我们发现我们要求的是大小为\(n\)无向联通图的数量 而\(n\)个点的无 ...

  7. 「SHOI2016」黑暗前的幻想乡 解题报告

    「SHOI2016」黑暗前的幻想乡 sb题想不出来,应该去思考原因,而不是自暴自弃 一开始总是想着对子树做dp,但是状态压不起去,考虑用容斥消减一些条件变得好统计,结果越想越乱. 期间想过矩阵树定理, ...

  8. 「SDOI2014」重建 解题报告

    「SDOI2014」重建 题意 给一个图\(G\),两点\((u,v)\)有边的概率是\(p_{u,v}\),求有\(n-1\)条边通行且组成了一颗树的概率是多少. 抄了几个矩阵树定理有趣的感性说法 ...

  9. linux deb系 rpm系 配置永久IP

    rpm: 1.IP a 查看网卡名 ens256 2.uuidgen ens256 生成UUID 3./etc/sysconfig/network-scripts add ifcfg-ens256 4 ...

  10. Python3 与 C# 扩展之~基础拓展

      上次知识回顾:https://www.cnblogs.com/dotnetcrazy/p/9278573.html 代码裤子:https://github.com/lotapp/BaseCode ...