基础知识讲解
什么是DIP、IOC、DI、IOC容器
IOC容器的技术剖析
IOC中最基本的技术就是“反射(Reflection)”编程
我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言的的反射编程,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
实际开发用法
技术解析
看到具体实现,有很多朋友一定会觉得很熟悉的,这不是butterknife么?那说明朋友们还是经常会用到这些技术,因为butterknife在3之前及3的部分都是运用的“反射”技术,还有我们常用的EventBus等等都用到了这项技术,但是一个三方框架,可能百分之八十的技术是我们用不到的。
目的与优缺点
- IOC 技术核心是解耦,降低模块之间的关联
- 优点:代码少,加速开发
- 缺点:反射产生性能损耗
具体开发实现
核心技术:反射
- getMethod() 获取当前类和父类 public方法
- getDeclareMathods() 获取当前类所有方法
布局注解代码
package com.fly.newstart.ioc.annotetion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* .----.
* _.'__ `.
* .--(Q)(OK)---/$\
* .' @ /$$$\
* : , $$$$$
* `-..__.-' _.-\$/
* `;_: `"'
* .'"""""`.
* /, FLY ,\
* // \\
* `-._______.-'
* ___`. | .'___
* (______|______)
*
* 包 名 : com.fly.newstart.ioc
* 作 者 : FLY
* 创建时间 : 2019/4/24
* 描述: 布局注入注解
*/
@Target(ElementType.TYPE) //该注解作用于什么之上 ,对应枚举标识 METHOD-标识为类之上
@Retention(RetentionPolicy.RUNTIME)//jvm在运行时通过反射获取注解的值
//RUNTIME-jvm在运行时通过反射来完成的过程
//CLASS-在编译时进行一些预操作,并且注解会在class存在
//SOURCE-源码级的,主要是做一些检查检测操作
public @interface ContentView {
int value();
}
属性注解代码
package com.fly.newstart.ioc.annotetion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* .----.
* _.'__ `.
* .--(Q)(OK)---/$\
* .' @ /$$$\
* : , $$$$$
* `-..__.-' _.-\$/
* `;_: `"'
* .'"""""`.
* /, FLY ,\
* // \\
* `-._______.-'
* ___`. | .'___
* (______|______)
*
* 包 名 : com.fly.newstart.ioc.annotetion
* 作 者 : FLY
* 创建时间 : 2019/4/24
* 描述: 属性的注解
*/
@Target(ElementType.FIELD) //该注解作用于什么之上 ,对应枚举标识 FIELD-标识为属性之上
@Retention(RetentionPolicy.RUNTIME)//jvm在运行时通过反射获取注解的值
public @interface InjectView {
int value();
}
点击事件注解代码
package com.fly.newstart.ioc.annotetion;
import android.view.View;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* .----.
* _.'__ `.
* .--(Q)(OK)---/$\
* .' @ /$$$\
* : , $$$$$
* `-..__.-' _.-\$/
* `;_: `"'
* .'"""""`.
* /, FLY ,\
* // \\
* `-._______.-'
* ___`. | .'___
* (______|______)
*
* 包 名 : com.fly.newstart.ioc.annotetion
* 作 者 : FLY
* 创建时间 : 2019/4/24
* 描述: 点击事件的注解
*/
@Target(ElementType.METHOD) //该注解作用于什么之上 ,对应枚举标识 METHOD-标识为方法之上
@Retention(RetentionPolicy.RUNTIME)//jvm在运行时通过反射获取注解的值
@EventBase(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class, listenerCallBack = "onClick")
public @interface OnClick {
int[] value();
}
长按事件注解代码
package com.fly.newstart.ioc.annotetion;
import android.view.View;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* .----.
* _.'__ `.
* .--(Q)(OK)---/$\
* .' @ /$$$\
* : , $$$$$
* `-..__.-' _.-\$/
* `;_: `"'
* .'"""""`.
* /, FLY ,\
* // \\
* `-._______.-'
* ___`. | .'___
* (______|______)
*
* 包 名 : com.fly.newstart.ioc.annotetion
* 作 者 : FLY
* 创建时间 : 2019/4/24
* 描述: 长按事件的注解
*/
@Target(ElementType.METHOD) //该注解作用于什么之上 ,对应枚举标识 METHOD-标识为方法之上
@Retention(RetentionPolicy.RUNTIME)//jvm在运行时通过反射获取注解的值
@EventBase(listenerSetter = "setOnLongClickListener", listenerType = View.OnLongClickListener.class, listenerCallBack = "onLongClick")
public @interface OnLongClick { //注解对应方法需要boolean返回值
int[] value();
}
事件注解的注解代码
package com.fly.newstart.ioc.annotetion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* .----.
* _.'__ `.
* .--(Q)(OK)---/$\
* .' @ /$$$\
* : , $$$$$
* `-..__.-' _.-\$/
* `;_: `"'
* .'"""""`.
* /, FLY ,\
* // \\
* `-._______.-'
* ___`. | .'___
* (______|______)
*
* 包 名 : com.fly.newstart.ioc.annotetion
* 作 者 : FLY
* 创建时间 : 2019/4/24
* 描述: 事件注解的注解 用于封装点击事件规律的对象
*/
@Target(ElementType.ANNOTATION_TYPE) //该注解作用于什么之上 ,对应枚举标识 ANNOTATION_TYPE-标识为注解之上
@Retention(RetentionPolicy.RUNTIME)//jvm在运行时通过反射获取注解的值
public @interface EventBase {
//setxxxListener
String listenerSetter();
//new View.xxxListener
Class> listenerType();
//回调执行方法:onxxx()
String listenerCallBack();
}
注入管理类代码
package com.fly.newstart.ioc;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import com.fly.newstart.ioc.annotetion.ContentView;
import com.fly.newstart.ioc.annotetion.EventBase;
import com.fly.newstart.ioc.annotetion.InjectView;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
*
* .----.
* _.'__ `.
* .--(Q)(OK)---/$\
* .' @ /$$$\
* : , $$$$$
* `-..__.-' _.-\$/
* `;_: `"'
* .'"""""`.
* /, FLY ,\
* // \\
* `-._______.-'
* ___`. | .'___
* (______|______)
*
* 包 名 : com.fly.newstart.ioc
* 作 者 : FLY
* 创建时间 : 2019/4/24
* 描述: 注入管理类
*/
public class InjectManager {
/**
* 注解的初始注入
*
* @param activity
*/
public static void inject(Activity activity) {
//布局的注入
injectLayout(activity);
//控件的注入
injectView(activity);
//事件的注入
injectEvents(activity);
}
/**
* 布局的注入
*
* @param activity
*/
private static void injectLayout(Activity activity) {
//获取类
Class extends Activity> cla = activity.getClass();
//获取类上的注解
ContentView contentView = cla.getAnnotation(ContentView.class);
//获取注解的值
if (contentView != null) {
int layoutId = contentView.value();
// 执行方法:setContentView(R.layout.activity_ioc);
// 方法一:activity.setContentView(layoutId);
// 方法二:反射获取,高大上
try {
//setContentView 是父类的方法不能使用getDeclareMathods(),需要使用getMethod()
Method method = cla.getMethod("setContentView", int.class);
//执行方法
method.invoke(activity, layoutId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 控件的注入
*
* @param activity
*/
private static void injectView(Activity activity) {
//获取类
Class extends Activity> cla = activity.getClass();
//获取类的所有属性
Field[] fields = cla.getDeclaredFields();
// 循环 拿到每个属性
if (fields == null || fields.length < 1) return;
for (Field field : fields) {
// 获取每个属性的注解
InjectView injectView = field.getAnnotation(InjectView.class);
if (injectView != null) { //并不是所有属性都有注解
//获取注解的值
int viewId = injectView.value();
//执行方法:mBtn01 = findViewById(R.id.btnO1)
try {
Method method = cla.getMethod("findViewById", int.class); //findViewById 需要赋值
//执行方法 获取返回值
Object view = method.invoke(activity, viewId);
//设置私有属性的访问权限,默认为false
field.setAccessible(true);
//给属性赋值
field.set(activity, view);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 事件的注入
*
* @param activity
*/
private static void injectEvents(Activity activity) {
//获取类
Class extends Activity> cla = activity.getClass();
//获取类的所有方法,事件注解肯定是当前类
Method[] methods = cla.getDeclaredMethods();
if (methods == null || methods.length < 1) return;
for (Method method : methods) {
//获取每个方法的注解
Annotation[] annotations = method.getAnnotations();
//一个可能有多个注解
if (annotations == null || annotations.length < 1) continue;
for (Annotation annotation : annotations) {
//获取注解上的注解类型
Class extends Annotation> annotationType = annotation.annotationType();
if (annotationType != null) {
//通过EventBase注解,获取3个重要的规律
EventBase eventBase = annotationType.getAnnotation(EventBase.class);
//事件的3个规律
String listenerSetter = eventBase.listenerSetter();
Class> listenerType = eventBase.listenerType();
String listenerCallBack = eventBase.listenerCallBack();
//获取注解的值
try {
// 通过运行annotationType获取OnClick注解的value值
Method valueMethod = annotationType.getDeclaredMethod("value");
//运行OnClick注解的value方法获取value值
int[] viewIds = (int[]) valueMethod.invoke(annotation);
//打包之后,代理处理后续工作,可以兼容多种事件方式,不需要写接口
//创建拦截器
ListenerInvocationHandler handler = new ListenerInvocationHandler(activity);
handler.addMethod(listenerCallBack, method);
//创建代理
Object listene = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, handler);
if (viewIds == null || viewIds.length < 1) continue;
for (int viewId : viewIds) {
//控件的赋值,保证控件没有赋值也可使用
View view = activity.findViewById(viewId);
//获取set方法
Method setter = view.getClass().getMethod(listenerSetter, listenerType);
//执行方法,传入代理
setter.invoke(view, listene);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
事件处理拦截器代码
package com.fly.newstart.ioc;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
/**
*
* .----.
* _.'__ `.
* .--(Q)(OK)---/$\
* .' @ /$$$\
* : , $$$$$
* `-..__.-' _.-\$/
* `;_: `"'
* .'"""""`.
* /, FLY ,\
* // \\
* `-._______.-'
* ___`. | .'___
* (______|______)
*
* 包 名 : com.fly.newstart.ioc
* 作 者 : FLY
* 创建时间 : 2019/4/24
* 描述: 事件处理拦截器
*/
public class ListenerInvocationHandler implements InvocationHandler {
//需要拦截的对象
private Object target;
//需要拦截的方法集合
private HashMap methodhMap = new HashMap<>();
public ListenerInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (target != null) {
//获取需要拦截的方法名
String methodName = method.getName();
//重新赋值,将拦截的方法换为了自定义的方法
method = methodhMap.get(methodName);//从集合中判断是否需要拦截
if (method != null) {//确定找到了需要拦截的的方法,才执行自定义方法
if (method.getParameterTypes().length == 0) { //判断有无参数
return method.invoke(target);
} else return method.invoke(target, args);
}
}
return null;
}
/**
* 将需要拦截的方法加入集合
*
* @param methodName 需要拦截的方法名
* @param method 执行自定义的方法
*/
public void addMethod(String methodName, Method method) {
methodhMap.put(methodName, method);
}
}
测试Activity代码
package com.fly.newstart.ioc.text;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.fly.newstart.R;
import com.fly.newstart.common.base.BaseActivity;
import com.fly.newstart.ioc.annotetion.ContentView;
import com.fly.newstart.ioc.annotetion.InjectView;
import com.fly.newstart.ioc.annotetion.OnClick;
import com.fly.newstart.ioc.annotetion.OnLongClick;
// setContentView(R.layout.activity_ioc);
@ContentView(R.layout.activity_ioc)
public class IOCActivity extends BaseActivity {
@InjectView(R.id.btnO1) //mBtn01 = findViewById(R.id.btnO1)
private Button mBtn01;
@InjectView(R.id.btnO2)
private Button mBtn02;
@Override
protected void onResume() {
super.onResume();
Toast.makeText(this, mBtn01.getText().toString(), Toast.LENGTH_SHORT).show();
}
// mBtn01.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) { // }});
@OnClick({R.id.btnO1, R.id.btnO2})
public void show(View view) {
Toast.makeText(this, "shwo(View view)", Toast.LENGTH_SHORT).show();
}
// mBtn01.setOnLongClickListener(new View.OnLongClickListener() {@Override public boolean onLongClick(View v) { return false; }});
@OnLongClick({R.id.btnO1, R.id.btnO2})
public boolean showLong(View view) {
Toast.makeText(this, "shwoLong(View view)", Toast.LENGTH_SHORT).show();
return false;
}
// @OnClick({R.id.btnO1,R.id.btnO2}) //proxy 代理
// public void show(){
// Toast.makeText(this, mBtn01.getText().toString(), Toast.LENGTH_LONG).show();
// }
public void initView() {
//用注解实现,需要代理完成,监听回调的过程
//Android监听事件是有规律的
//setxxxListener
//new View.xxxListener
//回调执行方法:onxxx()
//将规律打包成一个对象,用代理去完成这个事件
//还需要用到AOP 面向切面的技术
mBtn01.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
mBtn02.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
mBtn01.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return false;
}
});
}
}
baseActivity代码
package com.fly.newstart.common.base;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import com.fly.newstart.R;
import com.fly.newstart.ioc.InjectManager;
/**
* .----.
* _.'__ `.
* .--(Q)(OK)---/$\
* .' @ /$$$\
* : , $$$$$
* `-..__.-' _.-\$/
* `;_: `"'
* .'"""""`.
* /, FLY ,\
* // \\
* `-._______.-'
* ___`. | .'___
* (______|______)
*
* 包 名 : com.fly.newstart.common.base
* 作 者 : FLY
* 创建时间 : 2017/8/7
* * 描述: Activity的基类 */ public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 帮助所有子类完成注入工作 InjectManager.inject(this); } }
难点剖析
布局注入与属性注入都比较简单,只是运用反射的基本知识,但是方法的注入就比较难以理解,其中还运用了Prixy(代理)和InvocationHandler(拦截)的技术。
代理的原理与用法
Android监听事件规律-三部曲
将规律打包成一个对象,用代理去完成这个事件,但是这样任然不能完成我们需要的功能,还需要用到AOP 面向切面的技术。
AOP原理和拦截
小结
- 主要了解IOC注入框架,含义及基础使用
- 注解的基本使用
- “反射”的常规用法,七种常用且基础的API理解
- 代理的思路与原理
- AOP原理与拦截