一.概述
Interceptor拦截器,当我们在做一个项目的时候,会有很多需要重新使用的方法或者功能,当然,我们一般做的是封装,或者在基类里面实现,然后子类基础,就避免了很多重复的操作,但是有时候,这些也不是很简便,还是需要做一些额外的代码操作,例如,登录失效,需要跳到登录页面重新登录的时候,有很多页面都有这个需求,一个个的去实现,又太麻烦,增添很多不必要的代码,还有当项目很多页面需要定位功能的时候,又得重复的写,像这种需求的时候,我们就可以用到拦截器了,基于Java注解的形式来使用。
先看两个人逛窑子
第一个带了杜蕾斯的
第2个没带杜蕾斯的
GIF.gif二.基类的定义和思想
- BaseApplication
/**
* Created by asus on 2017/1/1.
* Application基类,子类添加自己的拦截器
*/
public abstract class BaseApplication extends Application {
private static Interceptors interceptors = new Interceptors();
@Override
public void onCreate() {
super.onCreate();
configInterceptor(interceptors);
}
public static final Interceptors getInterceptors() {
return interceptors;
}
//子类实现的方法,添加自己的拦截器数组
public abstract void configInterceptor(Interceptors interceptors);
}
2.BaseActivity
public class BaseActivity extends AppCompatActivity implements IVew {
BaseApplication application;
//子类实现禁止onCreate方法
@Override
protected final void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
application = (BaseApplication) getApplication();
onInitView(savedInstanceState);
}
//禁止子类的onResume方法 并配置拦截器
@Override
protected final void onResume() {
super.onResume();
Interceptor[] globalInters = InterceptorBuilder.NULL_INTERS;
Interceptors interceptors = application.getInterceptors();//获取拦截器集合
if (interceptors != null) {
globalInters = interceptors.getInterceptorArray();
}
Interceptor[] finalInters = InterceptorBuilder.build(globalInters, getClass());
new Invocation(this, finalInters).invoke();
}
//子类获取对象
@Override
public Activity getViewActivity() {
return this;
}
//设置主题
@Override
public void setWindowTheme() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
@Override
public void onInitView(Bundle savedInstanceState) {
}
@Override
public void resume() {
}
}
3.VIew接口,便于实现一些操作,例如禁止子类的onCreate等方法,使用接口的方法
/**
* Created by asus on 2017/1/1.
*
* 为了方便其他地方的调用采用接口的形式
*/
public interface IVew {
//获取子类的对象
Activity getViewActivity();
//设置窗口样式 会在onCreate之前调用
void setWindowTheme();
//初始化控件;子类不能重写onCreate
void onInitView(Bundle savedInstanceState);
//视图可见;子类不能重写onResume
void resume();
}
4.每一个应用的拦截器数组
final public class Interceptors {
private final List<Interceptor> globalActionInterceptor = new ArrayList<>();
//添加一个初始化全局拦截器(例如定位,登录等,可以初始化全局)
public Interceptors add(Interceptor globalInterceptor) {
if (globalInterceptor != null)
this.globalActionInterceptor.add(globalInterceptor);
return this;
}
//获取当前应用所有的拦截器
public Interceptor[] getInterceptorArray() {
Interceptor[] result = globalActionInterceptor.toArray(new Interceptor[globalActionInterceptor.size()]);
return result == null ? new Interceptor[0] : result;
}
}
三.拦截器的定义
1.定义一个拦截器接口Interceptor
public interface Interceptor {
//并传入一个拦截器的拦截方法,做一些拦截判断操作
void intercept(Invocation invocation);
}
2.拦截器调度Invocation
/**
* 拦截器调度
* Created by asus on 2017/1/1.
*/
public class Invocation {
private IVew view;
private Interceptor[] inters;
private int index = 0;
public Invocation(IVew view, Interceptor[] interceptors) {
this.view = view;
this.inters = interceptors;
}
public void invoke(){
//如果拦截器数组有值,则回调当前拦截器的操作
if(index<inters.length){
inters[index++].intercept(this);
//当当前拦截器执行完之后,又调用invoke方法,执行下一个拦截器,如果还有下一个拦截器,
//继续执行intercept,否则才去回调当前activity的resume方法
}else if(index++==inters.length){
view.resume();
}
}
public IVew getView() {
return view;
}
}
3.定义自己的拦截器(我这里是伪代码登录拦截器)
public class LoginInterceptor implements Interceptor {
@Override
public void intercept(Invocation inv) {
Activity viewActivity = inv.getView().getViewActivity();
//如果登录了继续回调到invoke中执行下一个拦截器
if(MyApplication.isLoaded()){//执行自己的操作判断,我这里是判断伪代码是否登录
inv.invoke();
}else{//如果没有登录就跳转到登录页面,finish当前Activity
Intent intent = new Intent(viewActivity, LoginActivity.class);
viewActivity.startActivity(intent);
viewActivity.finish();
}
}
}
四.拦截器的使用
1.自定义注释,来构造一个拦截器,当类执行的时候,会执行到BaseActivity的onResume方法中还记得上面的onResume吗
//禁止子类的onResume方法
@Override
protected final void onResume() {
super.onResume();
//先定义一个长度为0的数组,避免全局数组为null
Interceptor[] globalInters = InterceptorBuilder.NULL_INTERS;
//获取全局的拦截器集合
Interceptors interceptors = application.getInterceptors();
if (interceptors != null) {
globalInters = interceptors.getInterceptorArray();
}
//构造所有的拦截器(包括全局的当前Activity中的注释拦截器)
Interceptor[] finalInters = InterceptorBuilder.build(globalInters, getClass());
new Invocation(this, finalInters).invoke();//执行全局和当前类的拦截器
}
2.拦截器的构造(通过类注释的方法来构造,当然也可以通过获取类的方法,这样比较直观,直接在类上面来定义)
public class InterceptorBuilder {
public static final Interceptor[] NULL_INTERS = new Interceptor[0];
private static Map<Class<? extends Interceptor>, Interceptor> intersMap = new HashMap<>();
/**
* 构建 Interceptors.
* finalInters = globalInters + classInters
*/
public static Interceptor[] build(Interceptor[] globalInters, Class<?> targetClass) {
//1.存入全局的拦截器,添加到Map集合中
for (Interceptor inter : globalInters)
intersMap.put(inter.getClass(), inter);
//2.获取Before注释的拦截器,如果有添加到Map集合中
Interceptor[] classInters = createInterceptors(targetClass.getAnnotation(Before.class));
// 3.判断是否有Clear注释
Clear clear = targetClass.getAnnotation(Clear.class);
//① 如果为null,就说明没有Clear ,直接将全局和Before构造成一个新的拦截器数组
if (clear == null) {
Interceptor[] result = new Interceptor[globalInters.length + classInters.length];
int index = 0;
for (Interceptor inter : globalInters)
result[index++] = inter;
for (Interceptor inter : classInters)
result[index++] = inter;
return result;
}
Class<? extends Interceptor>[] clearInters = clear.value();
// ② 有Clear拦截器的标志,但是后面没有拦截器参数,意味着清楚所有其他的全局拦截器
// (全局拦截器有很多,当不需要时,clear标志清除),只保留当前类Before拦截器
if (clearInters.length == 0)
return classInters;
// ③ 有Clear拦截器的标志,后面也有Clear拦截器 则删除Map中Clear指定的拦截器,返回
Interceptor[] temp = new Interceptor[globalInters.length + classInters.length];
int index = 0;
for (Interceptor inter : globalInters)
temp[index++] = inter;
for (Interceptor inter : classInters)
temp[index++] = inter;
int removeCount = 0;
for (int i=0; i<temp.length; i++) {
for (Class<? extends Interceptor> ci : clearInters) {
if (temp[i].getClass() == ci) {//如果全局和当前类中有Clear标志的拦截器,置为null
temp[i] = null;
removeCount++;
break;
}
}
}
Interceptor[] result = new Interceptor[temp.length - removeCount];//创建一个新的所有的拦截器数组,进行返回
index = 0;
for (Interceptor inter : temp)
if (inter != null)
result[index++] = inter;
return result;
}
private static Interceptor[] createInterceptors(Before beforeAnnotation) {
//为before为null,返回长度为0的数组
if (beforeAnnotation == null)
return NULL_INTERS;
Class<? extends Interceptor>[] interceptorClasses = beforeAnnotation.value();
//before注释后面没有拦截器的时候,返回长度为0的数组
if (interceptorClasses.length == 0)
return NULL_INTERS;
//如果Map集合,全局拦截器没有该类上面的拦截器,就加入Map集合
Interceptor[] result = new Interceptor[interceptorClasses.length];
try {
for (int i=0; i<result.length; i++) {
result[i] = intersMap.get(interceptorClasses[i]);
if (result[i] == null) {
result[i] = (Interceptor)interceptorClasses[i].newInstance();
intersMap.put(interceptorClasses[i], result[i]);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return result;
}
}
3.自定义注释(Before和Clear接口,写在一起了)
@Inherited //可继承
@Retention(RetentionPolicy.RUNTIME)//保留的时间
@Target({ElementType.TYPE, ElementType.METHOD})//作用范围,类,接口方法等
public @interface Before {
Class<? extends Interceptor>[] value();
}
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Clear {
Class<? extends Interceptor>[] value() default {};
}
五.简单例子来体现拦截器的好处
假如我们要去大保健(相当于我们项目中的操作,需要查看我卡里还有多少钱,但是你必须登录才可以查看),大保健需要杜蕾斯,如果不使用拦截器,你每次去一次大保健,都要去买一次杜蕾斯,很麻烦,而你使用了拦截器的话,就相当于你在口袋里面已经装好了杜蕾斯,只需要大保健的时候拿出来就ok,这样就方便了很多。接下来就是大保健的实战。。。
1.MainActivity,点击按钮,去大保健,首先必须使用Clear,清除掉,(因为在全局默认配置了拦截器),否则也会拦截掉,就会一直卡着,因为我都还没准备去呢,你就给我拦截
@Clear(LoginInterceptor.class)
public class MainActivity extends BaseActivity implements View.OnClickListener {
@Override
public void onInitView(Bundle savedInstanceState) {
super.onInitView(savedInstanceState);
setContentView(R.layout.activity_main);
Button bt_has_login = (Button) findViewById(R.id.bt_has_login);
Button bt_no_login = (Button) findViewById(R.id.bt_no_login);
bt_has_login.setOnClickListener(this);
bt_no_login.setOnClickListener(this);
}
@Override
public void onClick(View view) {
//设置一个变量来处理事件,例如你的登录Token,用于判断Token有没有失效,失效则去登录
if (view.getId() == R.id.bt_has_login) {
MyApplication.setLoaded(true);//我有杜蕾斯
} else if (view.getId() == R.id.bt_no_login) {
MyApplication.setLoaded(false);//我没有杜蕾斯
}
go_yaozi();
}
//大保健
private void go_yaozi() {
Intent intent = new Intent(MainActivity.this, BeautifulGirlActivity.class);
startActivity(intent);
}
}
2.窑子来了
//在进窑子之前,判断是否有杜蕾斯,这里就会走入LoginInterceptor中
@Before(LoginInterceptor.class)
public class BeautifulGirlActivity extends BaseActivity {
@Override
public void onInitView(Bundle savedInstanceState) {
super.onInitView(savedInstanceState);
setContentView(R.layout.activity_girl);
}
}
我们再来看看LoginInterceptor中的处理
public class LoginInterceptor implements Interceptor {
@Override
public void intercept(Invocation inv) {
Activity viewActivity = inv.getView().getViewActivity();
if(MyApplication.isLoaded()){//如果已经有杜蕾斯了,就去继续执行下一步
inv.invoke();
}else{//否则,回去拿杜蕾斯
Intent intent = new Intent(viewActivity, LoginActivity.class);
viewActivity.startActivity(intent);
viewActivity.finish();
}
}
}
逛窑子逛到这里就逛完了,使用起来非常方便,当一个项目有很多地方都需要用到的时候,就可以采用这种逛窑子的方式来处理,想再逛一次窑子的可以上去重新看下逛窑子的gif图。
网友评论