美文网首页Android
Android全能拦截器Interceptor

Android全能拦截器Interceptor

作者: Android绝世小菜鸟 | 来源:发表于2017-01-02 15:10 被阅读2524次

    一.概述

    Interceptor拦截器,当我们在做一个项目的时候,会有很多需要重新使用的方法或者功能,当然,我们一般做的是封装,或者在基类里面实现,然后子类基础,就避免了很多重复的操作,但是有时候,这些也不是很简便,还是需要做一些额外的代码操作,例如,登录失效,需要跳到登录页面重新登录的时候,有很多页面都有这个需求,一个个的去实现,又太麻烦,增添很多不必要的代码,还有当项目很多页面需要定位功能的时候,又得重复的写,像这种需求的时候,我们就可以用到拦截器了,基于Java注解的形式来使用。

    先看两个人逛窑子
    第一个带了杜蕾斯的

    GIF.gif

    第2个没带杜蕾斯的

    GIF.gif

    二.基类的定义和思想

    1. 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图。

    相关文章

      网友评论

        本文标题:Android全能拦截器Interceptor

        本文链接:https://www.haomeiwen.com/subject/vuewvttx.html