JetPack架构---LiveDataViewModel搭建MVVM结构工程

Posted 战国剑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JetPack架构---LiveDataViewModel搭建MVVM结构工程相关的知识,希望对你有一定的参考价值。

 

一、MVVM与JetPack

MVVM,是Model-View-ViewModel的简写。Model:数据,View:UI相关,ViewModel:UI相关数据与控制。

JetPack提供的组件LiveData与ViewModel,十分契合该模式。

(1)LiveData作为数据驱动的核心,驱动与监听Model数据变化。

(2)ViewModel组件,作为页面数据管理的存储与控制中心。

(3)Activity或Fragment,也就是MVVM中的View。

MVVM的核心在于,通过数据驱动修改UI,实现上数据与UI解耦,方便切面测试,易于扩展,也让开发人员可独立开发数据或者UI相关。

二、JetPack为核心的MVVM架构实现

以下将以一个获取天气预报信息作为例子。从搭建结构到示例各模块的简单实现。

2-0:工程结构

新建工程结构如下:

(1)数据层model中包含本地数据、网络接口数据,以及管理这两个数据源的数据仓库Repository。

(2)UI层,放置Activity、Fragment等页面元素。UI层持有ViewModel的数据引用,监听ViewModel数据的变化。

(3)ViewModel层,作为model与view的中间连接层,持有对model层的引用。但ViewModel不会持有View的引用,避免内存泄漏。

2-1:ViewModel定义

定义一个PlaceViewModel,管理页面数据

public class PlaceViewModel extends ViewModel 

    //此处定义的LiveData,只在该类内部使用。
    //意义在于:通过触发该LiveData的数据改变,联动的驱动apiResponseLiveData从Repository获取数据
    private MutableLiveData<Object> queryPlacesLiveData = new MutableLiveData<>();

    //UI页面实际监听的LiveData
    public LiveData<ApiResponse<PlaceResponse>> apiResponseLiveData = Transformations.switchMap(queryPlacesLiveData, new Function<Object, LiveData<ApiResponse<PlaceResponse>>>() 
        @Override
        public LiveData<ApiResponse<PlaceResponse>> apply(Object input) 
            //从数据仓库中,获取LiveData格式的数据
            return Repository.getInstance().queryPlaces();
        
    );

    //UI页面调用该方法,作为查询数据驱动
    public void queryPlaces() 
        queryPlacesLiveData.setValue(queryPlacesLiveData.getValue());
    

2-2:UI页面对ViewModel的监听与调用

//获取ViewModel
final PlaceViewModel placeViewModel = new ViewModelProvider(MainActivity.this,new ViewModelProvider.androidViewModelFactory(getApplication())).get(PlaceViewModel.class);

//监听该ViewModel中apiResponseLiveData的数据变化
placeViewModel.apiResponseLiveData.observe(this, new Observer<ApiResponse<PlaceResponse>>() 
            @Override
            public void onChanged(ApiResponse<PlaceResponse> placeResponseApiResponse) 
                Log.e(TAG, "onChanged: ");
            
        
);

        
queryPlaces = findViewById(R.id.query_places);
queryPlaces.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                //调用方法,驱动数据变化
                placeViewModel.queryPlaces();
            
        );

2-3:数据仓库Repository的定义

Repository作为数据仓库,在工程中以单例的形式存在即可。该仓库中,可包含多个数据源,如本地数据库、本地缓存、网络接口数据等,该仓库也适合作为测试的切面使用。

获取数据库、缓存等较为简单,参照前文的Room数据库使用即可。此处只介绍如何合理的获取网络接口数据(以Retrofit为例)。

public class Repository 
    private static final Repository ourInstance = new Repository();

    public static Repository getInstance() 
        return ourInstance;
    

    private Repository() 
    


    public LiveData<ApiResponse<PlaceResponse>> queryPlaces() 
        //获取网络数据
        return ServiceManager.getInstance().create(PlaceService.class).queryPlaces();
    

 ServiceManager作为网络接口的管理类,以Retrofit为基础,操作接口。此处有别于通常的Retrofit的地方在于:采用了LiveDataCallAdapterFactory,作为接口数据转换的工厂,该类的作用在于把接口数据全部转为LiveData格式,当ViewModel调用接口方法时,直接能得到LiveData格式的数据。

public class ServiceManager 

    private Retrofit retrofit;
    private PlaceService placeService;
    private String baseUrl = "https://api.caiyunapp.com/";

    private static final ServiceManager ourInstance = new ServiceManager();

    public static ServiceManager getInstance() 
        return ourInstance;
    

    private ServiceManager() 
        retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                //此处为LiveData格式转换的特殊处理
                .addCallAdapterFactory(new LiveDataCallAdapterFactory())
                .build();
    

    public <T> T create(Class<T> serviceClass) 
        return retrofit.create(serviceClass);
    

2-3-0:LiveDataCallAdapterFactory的定义

该类的定义,参照android系统本身的DefaultCallAdapterFactory即可。只需重写父类中的get方法。

public class LiveDataCallAdapterFactory extends CallAdapter.Factory 

    @Override
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) 
        //返回类型必定需要是LiveData
        if (getRawType(returnType) != LiveData.class) 
            return null;
         else if (!(returnType instanceof ParameterizedType)) 
            throw new IllegalArgumentException("type must be parameterized as Call<Foo> or Call<? extends Foo>");
         else 
            //首个参数
            final Type responseType = getParameterUpperBound(0, (ParameterizedType)returnType);
            //获取该参数的类
            Class<?> rawResponseType = getRawType(responseType);
            //rawResponseType 判断是否符合要求
            if(rawResponseType != ApiResponse.class)
                throw new IllegalArgumentException("type must be ApiResponse");
            
            return new LiveDataCallAdapter<>(responseType);
        
    

2-3-1:LiveDataCallAdapter的定义

//R为传入的的类对象
public class LiveDataCallAdapter<R> implements CallAdapter<R, LiveData<ApiResponse>> 
    private Type responseType;

    public LiveDataCallAdapter(Type responseType) 
        this.responseType = responseType;
    

    @Override
    public Type responseType() 
        return responseType;
    

    @Override
    public LiveData<ApiResponse> adapt(final Call<R> call) 
        //返回一个LiveData格式数据
        return new LiveData<ApiResponse>() 

            AtomicBoolean started = new AtomicBoolean(false);

            @Override
            protected void onActive() 
                super.onActive();
                //避免多次调用
                if (started.compareAndSet(false, true)) 
                    //方法入队,发起请求
                    call.enqueue(new Callback<R>() 
                        @Override
                        public void onResponse(Call<R> call, Response<R> response) 
                            ApiResponse responseData = new ApiResponse<>();
                            if (response.isSuccessful()) 
                                responseData = (ApiResponse) response.body();
                             else 
                                responseData.setData(null);
                                responseData.setCode(response.code());
                            
                            responseData.success = true;
                            responseData.code = response.code();
                            postValue(responseData);
                        

                        @Override
                        public void onFailure(Call<R> call, Throwable t) 
                            ApiResponse responseData = new ApiResponse<>();
                            responseData.code = -1;
                            responseData.data = null;
                            responseData.success = false;
                            responseData.message = Log.getStackTraceString(t);
                            postValue(responseData);
                        
                    );
                
            
        ;
    

最后,再看下retrofit的接口定义:

public interface PlaceService 

    @GET("v2.5/"+TOKEN+"/121.6544,25.1552/weather.json")
    LiveData<ApiResponse<PlaceResponse>> queryPlaces();

至此,已经完成了一个基于JetPack中的 LiveData与ViewModel的MVVM框架。该框架根据LiveData与ViewModel的特性,十分简单的实现了数据驱动,UI响应数据驱动发生变化。组件本身的生命周期自我控制的特性,也减少了我们工程中可能发生的问题。

三、JetPack趋势

JetPack系列在android上的作用,十分类似Vue,React等在前端的作用。

官方提供组件,实现数据响应式框架。

目前,android-11的beta版本也对JetPack系列做了一系列的升级与更新。后续该系列将会越来趋于完善。

JetPack预计后续将成为android上的主流框架,android也将对这套组件框架做更多的升级与完善。

以上是关于JetPack架构---LiveDataViewModel搭建MVVM结构工程的主要内容,如果未能解决你的问题,请参考以下文章

Android Jetpack架构组件——什么是Jetpack?

Android Jetpack架构组件——什么是Jetpack?

Jetpack笔记

Jetpack 组成没有片段的导航架构?

Jetpack架构组件从入门到精通

Android高级Jetpack架构组件+Jetpack compose强化实战