android 开发程序员Android开发Android开发

Android拦截器 -- 打开页面自动登录并回来

2017-04-02  本文已影响1364人  生活简单些

在App项目中有一些Activity是需要登录成功后才能进去的,比如订单详情页,因为订单是跟账号挂钩的,登录的账号一般会有个ID,需要带着ID和订单号去查订单信息。
很多刚开始做App的同学会认为应该在进入订单详情页前先确保登录成功,也就是说把登录判断和发起登录都是进入订单详情页前搞定,的确这么做没有问题,但是恐怕体力活会很多吧,如果能理解拦截器的原理就可以简化我们的工作量。


@InterceptWith(LoginInterceptor.class)
public class OrderDetailActivity extends InterceptorActivity {
    private static final String EXTRA_ORDER_ID = "orderId";

    private TextView mOrderInfoText;
    private String mOrderId;

    public static void startActivity(Context context, String orderId) {
        Intent intent = new Intent(context, OrderDetailActivity.class);
        intent.putExtra(EXTRA_ORDER_ID, orderId);
        context.startActivity(intent);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_order_detail);

        mOrderId = getIntent().getStringExtra(EXTRA_ORDER_ID);
        mOrderInfoText = (TextView) findViewById(R.id.orderInfo);
    }

    @Override
    protected void invoked() {
        super.invoked();
        mOrderInfoText.setText("订单信息(order id: " + mOrderId + ")");
        // 根据orderId请求完整的订单信息
    }
}

看到以上订单详情页只要在Acitvity class之上加上一个登录校验的注解然后并在invoke()回调里执行跟登录相关的接口查询及初始化页面即可,这样就不用关注登录的实现细节,一定程度上解耦了你的业务实现。
下面我们说说实现方式,当然这里的Interceptor并不是纯粹利用Java的语言的动态代理,这只是一种模仿,依附Activity的生命周期完成的:

  1. 定义Annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface InterceptWith {

    /**
     * @return a Interceptor class array (must have a constructor without parameters inside)
     */
    Class<? extends Interceptor>[] value();
}

注:如果对Annotation不是很了解最好查阅下相关资料稍微了解下,这里就不过啰嗦了。

  1. 定义Interceptor父类的基本的结构:
public abstract class Interceptor {

    /**
     * Request code used to start activity for result.
     *
     * @return request code
     */
    public abstract int getRequestCode();

    /**
     * Check interceptor's condition is meet or no.
     *
     * @param context Android context
     * @return condition is meet or no
     */
    public abstract boolean isSatisfied(Context context);

    /**
     * if condition was not satisfied, it'll be called to acquire resource or permission and so on.
     *
     * @param activity see {@link Activity}
     */
    public abstract void process(Activity activity);
}

注:以上是每个拦截器需要实现的抽象父类,以下以登录校验的拦截器举例:

public class LoginInterceptor extends Interceptor {

    @Override
    public int getRequestCode() {
        return LoginActivity.REQUEST_CODE_LOGIN;
    }

    @Override
    public boolean isSatisfied(Context context) {
        return UserConfigCache.isLogin(context);
    }

    @Override
    public void process(Activity activity) {
        LoginActivity.startActivityForResult(activity, getRequestCode());
    }
}
  1. 有拦截检测功能的Activity:
public class InterceptorActivity extends AppCompatActivity {
    private List<Interceptor> mInterceptors = new ArrayList<>();

    /**
     * Called only when all interceptors verified OK,
     * so do your work here which all interceptors are passed.
     */
    protected void invoked() {
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (mInterceptors.size() == 0) {
            scanInterceptors();
            verifyInterceptors();
        }
    }

    private void scanInterceptors() {
        mInterceptors.clear();

        InterceptWith annotation = getClass().getAnnotation(InterceptWith.class);
        if (annotation != null) {
            Class<? extends Interceptor>[] classes = annotation.value();
            for (Class<? extends Interceptor> clazz : classes) {
                try {
                    mInterceptors.add(clazz.newInstance());
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void verifyInterceptors() {
        if (mInterceptors.isEmpty()) {
            return;
        }

        for (int i = 0; i < mInterceptors.size(); i++) {
            Interceptor interceptor = mInterceptors.get(i);
            if (interceptor.isSatisfied(this)) {
                if (i == mInterceptors.size() - 1) {
                    invoked();
                    break;
                }
            } else {
                interceptor.process(this);
                break;
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        for (Interceptor interceptor : mInterceptors) {
            if (interceptor.getRequestCode() == requestCode) {
                if (resultCode == RESULT_OK) {
                    verifyInterceptors();
                    break;
                } else if (resultCode == RESULT_CANCELED) {
                    finish();
                    break;
                }
            }
        }
    }
}

注:
主要通过在onStart()进行对配置的拦截器进行检查,一旦有拦截器不满足条件就跳转对应的页面(如登录页面)请求资源,当资源请求获取到后(登录成功后)到了ActivityResult再校验其他拦截器,如果配置了多个拦截器则当所有的拦截器都被满足条件后会触发invoked()回调函数执行, 所以需要类似登录成功才能执行的代码就放在invoked()里好了。
当然配置多个拦截器也很方便:

@InterceptWith({FirstInterceptor.class, SecondInterceptor.class, ThirdInterceptor.class})
public class XXXActivity extends InterceptorActivity
  1. 以上代码除了LoginInterceptor.java是按业务定义的拦截器,其余都可以作为common模块里的代码或者library中的代码,完整Demo源码请参考Android Interceptor.
上一篇下一篇

猜你喜欢

热点阅读