MVP安全调用View

MVP架构中Presenter处理业务逻辑后将数据传递给View,通过View将数据展示出来,而此时View可能已经销毁了,结果程序崩溃了。那Presenter如何才能安全地调用View对象的方法呢?
这种现象多发生在异常调用的场景,View已经销毁,而异步调用还未结束;当异步调用结束回调View时,View已经销毁。解决思路当然就是异常返回时不再调用View方法,而方案可能有多种,如

  • 使用RxJava,在异步调用时存储Disposable,页面结束后调用dispose()取消
  • 使用OkHttp,在异步调用时存储Call对象,页面结束后调用call.cancel()方法取消
  • 异步回调时判断View是否已经销毁,再调用View的方法

以上两个方案都可以解决此问题的,但使用起用都比较麻烦。前两种方案需要保存异常请求的对象,而后一种每个异步请求的处理都要添加判断语句。那有没有方案可以即不存储异步请求对象,也不写判断语句的方案,而又不会引用Crash的方案呢?当然是有的,方案就是使用View的代理。


使用代理View

通过View的代理来判断View是否已经销毁,如果未销毁则正常调用View的方法;当View已经销毁时,直接忽略调用。那代理View如何来实现呢?是否创建创建一个代理类并实现View的接口,在代理类时添加View是否销毁的判断呢?当然不是,这般操作只会让程序更复杂,这里可以利用Java语言特性,动态代理来实现,根据View接口生成一个View的动态代理类,实现是不是特别简单,即

Proxy.newInstance(view.getClassLoader(), new Class[IDetailView.class], new InvocationHandler(){});

但是每个Presenter都这样写的话,也是一件重复的工作,需要将这段逻辑下沉到BasePresenter层,这样Presenter的子类就不用处理代理逻辑了。这里有个难点就是如何获取View的接口类型,基类通过泛型定义的View,但并不清楚子类是如何定义的,如

class BasePresenter<T extends IView>{
    private T mView;
}

View类可能是这样定义的,它实现了多个接口,而我们需要拿到的是IDetailView接口,用它来生成View的代理对象

//IDetailView的实现类
class DetailActivity extends BaseView<IDetailPresenter> implements IDetailView,OnClickListener{
}
//View的接口类定义,继承了IView接口
interface IDetailView extends IView{
}

要想拿到IDetailView接口的类型,就需要解析泛型了。这里最关键的一步是通过getGenericInterfaces()方法获取对象实现的接口列表,将View接口解析出来后, 再使用Proxy生成一个代理对象,在动态代理的方法调用处进行判断是否断续调用View相关的方法。

//获取View的代理对象
private T getViewProxy(Object view){
    Class clazz = (Class)getViewType(view);
    Object proxy = Proxy.newProxyInstance(view.getClass().getClassLoader(), new Class[]{clazz}, new InvocationHandler(){
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //调用的可能是Object方法,此时并不需要进行代理,直接调用即可
            if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
            }
            Object result = null;
            //View没有销毁就进行调用
            if (view.isAlive()) 
               result = method.invoke(view, args);
            if (result == null)
                ;//忽略调用后需要返回一个正确类型的返回值
            return result;
        }
        return (T) proxy;
    }
}
//获取View的接口类型
private Type getViewType(Object view){
    Class clazz = view.getClass();
    while (clazz != null){
        //获取类型实现的接口列表
        Type[] types = clazz.getGenericInterfaces();
        if (types != null && types.length > 0){
            for(Type type : types){
                if (type instanceof ParameterizedType){
                    type = ((ParameterizedType) type).getRawType();
                }
                if (!(type instanceof Class)) {
                    continue;
                }
                //判断是否是IView的子类,以此在断定是定义的View接口
                if (IView.class.isAssignableFrom((Class<?>) type)) {
                    return type;
                }
            }
        }
        //获取View的父类。可能父类实现了接口,当前类只是继承了一下而已
        clazz = clazz.getSuperclass();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容