Android架构组件(4)ViewModel框架

ViewModel介绍

ViewModel框架介绍上是说被设计上用来在Activity或Fragment销毁、重建的时候保存它们的UI相关的数据。系统因为某种原因(屏幕旋转等)销毁,重新创建Activity的时候,存储在其中的任何临时性界面相关数据都会丢失,对于简单的数据,Activity 可以使用 onSaveInstanceState() 方法中的参数恢复其数据,但此方法仅适合可以序列化的少量数据,而不适合数量可能较大的数据。
在实践上,ViewModel还经常和LiveData配合使用,当Activity通过异步的方式从网络服务等地方获取数据的时候,可能会因为数据还未返回Activity就已经被退出了,从而造成内存泄漏和空指针异常,LiveData的优势就在于Activity已销毁的情况下会自动反注册,从而不存在上述风险。
从实现上来说,ViewModel框架很简单。因为它主要解决的是保存数据的问题,所以它的大部分组件都是用于构建通用的数据保存和访问的接口,以此保证不同的对象可以通过相同的方式访问到各自的数据。

源码分析

ViewModel框架最核心的当然是ViewModel类:

public abstract class ViewModel {
	...
	protected void onCleared() 
	@MainThread
    final void clear() {
    	...
    	onCleared() 
    }
}

ViewModel作为数据最终的保存类,没有定义任何数据存取的接口,这些都需要用户继承ViewModel自己实现。ViewModel提供了一个onCleared回调,这个方法会在它的持有类ViewModelStore清除数据的时候调用,用户可以在这个方法里面实现自己清理某些数据的逻辑。
ViewModelStore就是一个简单的保存ViewModel的数据结构:

public class ViewModelStore {
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
    final ViewModel get(String key) {
        return mMap.get(key);
    }
 	...
}

可以看到ViewModelStore内部就是一个简单的HashMap,就是起一个封装存取所有ViewModel操作的作用,而最终,它会被ViewModelStoreOwner持有:

public interface ViewModelStoreOwner {
    ViewModelStore getViewModelStore();
}

ViewModelStoreOwner是一个提供了获取ViewModelStore对象方法的接口类,这个类就是实现ViewModel框架的功能的关键类、
ViewModel框架的理念很简单:怎么保证Activity重建之后还能获得原来的数据?把这个数据存在存活时间比Activity更长的对象就可以了!所以Activity或Fragment的管理类会实现或者持有ViewModelStoreOwner的实现类,因为管理类肯定会比被管理的类存活时间更长。

除此之外,为了提供统一的访问接口,ViewModel框架定义了ViewModelProvider类和ViewModelProviders类:

public class ViewModelProvider {
	public interface Factory {
        <T extends ViewModel> T create(Class<T> modelClass);
    }
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) 
   	@MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
}

public class ViewModelProviders {
	@MainThread
    public static ViewModelProvider of(Fragment fragment,Factory factory) 
    @MainThread
    public static ViewModelProvider of(FragmentActivity activity,Factory factory)
}

ViewModelProviders提供了of工具方法,通过传入的fragment或者activity,获取到对应的ViewModelStoreOwner,然后生成一个新的ViewModelProviderViewModelProvider内部通过反射的方式,由用户传入的ViewModel类的Class对象从ViewModelStore中取出之前存入的对应的ViewModel。
ViewModelProvider将反射生成ViewModel的工厂方法声明成了Factory接口,并且提供了ViewModel构造参数为空的NewInstanceFactory类和构造参数为ApplicationAndroidViewModelFactory类两个默认Factory实现,当ViewModelProviderfactory参数为null(of方法有不需要Factory的重载方法,我上面没有写出)时,就会调用这两个默认实现。
因为AndroidViewModelFactory继承了NewInstanceFactory并且两者的实现类似,所以我下面只介绍一下它的create方法实现,可以看到它的create方法,就是简单的判断传入的class类是不是AndroidViewModel类(构造参数为Application)来反射获取ViewModel对象,不是的话就通过父类来调用无参构造方法,如果都失败的话就会直接抛出运行时异常:

        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InstantiationException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                }
            }
            return super.create(modelClass);
        }

Application持有数据

老实说,看了ViewModel框架的实现之后,我有一点失望,因为它只能在同一个activity,或者同一个activity下的不同fragment之间共享数据(由ViewModel框架的实现原理决定),而没有拥有(我所期望的)在不同Activity乃至不同线程之间的共享数据的功能。但是我转念一想,Google不做,我可以自己动手,Android里面天然就有一个可以所有类都可以访问到的共享对象:Application!所以我让自己的Application继承ViewModelStoreOwner,并且重载ViewModelProviders提供对应的访问接口,这样就实现了跨Activity的数据共享:

public class MyApplication extends Application implements ViewModelStoreOwner {
	   private ViewModelStore mViewModelStore;
	   @Override
	   public void onCreate() {
	       super.onCreate();
	       mViewModelStore = new ViewModelStore();
	   }
	
	   @Override
	   public ViewModelStore getViewModelStore() {
	       return mViewModelStore;
	   }
}

public class VMProviders extends ViewModelProviders {
    private static ViewModelStore checkApplicationAndGetStore(Application application) {
        if (!(application instanceof MyApplication) || application != MyApplication.getApplication()) {
            throw new IllegalArgumentException("Your Application is not true.");
        } else {
            return ((MyApplication) application).getViewModelStore();
        }
    }
    public static ViewModelProvider of(Application application) {
        return of(application, null);
    }
    public static ViewModelProvider of(Application application, Factory factory) {
        ViewModelStore store = checkApplicationAndGetStore(application);
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(store, factory);
    }
}

通过上述方式,我实现了不同Activity之间共享数据,但是要实现跨线程共享数据就没这么简单了,首先,我需要保证ViewModel是线程安全的,这就需要限制它的访问接口(原本是没有任何限制的),其次,我需要将ViewModelStore存储ViewModel的Map修改成线程安全的,但我能修改的地方就只有ApplicationViewModelStore,这样算来,我这种实现方案的缺陷其实很大。如果同学你有更好的方案,希望可以分享给我。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值