ViewModel和LiveData的高效使用

ViewModel是谷歌的组建架构AAC(Android Architecture Components)中的组件。
ViewModel类是被设计用来以可感知生命周期的方式存储和管理 UI 相关数据,ViewModel中数据会一直存活(持久化)即使 activity configuration发生变化,比如横竖屏切换的时候。

我们来看看ViewModel的生命周期:


ViewModel的生命周期

由上图可知,ViewModel 生命周期是贯穿整个 Activity 生命周期,包括 Activity 因旋转造成的重新创建,直到 Activity 真正意义上销毁后才会结束。既然如此,用来存放数据再好不过了。

在使用过程中ViewModel一般都是结合LiveData来使用的,这样还能实现数据的异步回调(因为使用ViewModel不用去考虑生命周期,所以能很好地避免一些耗时的异步回调,判断Activity或者Fragment是否存在等问题)并解耦,由于它数据的持久化,可以实现多个fragment之间共享数据:
看一个官网的例子:

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}


public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

上面这是 一个Activity 与其内部的 fragment 可以共用一个ViewModel的案例。

总结:

  • Activity 不需要做任何操作,甚至不需要知道这次交互,完美解耦。
  • Fragment 只需要 与 ViewModel 交互,不需要知道对方 Fragment 的状态甚至是否存在,更不需要持有其引用。
  • Fragment 与 Fragment 的生命周期之间互不影响,即使对方 Fragment 销毁,也不影响自身任何工作。

来看一个简单的demo:

/**
 * 基础ViewModel类,管理LiveData
 */
public class BaseViewModel extends ViewModel {
    private Map<String, MutableLiveData> maps;

    /**
     * 构造函数(在ViewModelProvider里通过class.newInstance创建实例)
     */
    public BaseViewModel() {
        maps = new ConcurrentHashMap<>();  //初始化集合(线程安全)
    }

    /**
     * 通过指定的数据实体类获取对应的 LiveData 类
     */
    protected <T> MutableLiveData<T> get(Class<T> clazz) {
        return get(null, clazz);
    }

    /**
     * 通过指定的key或者数据实体类获取对应的 LiveData 类
     */
    protected <T> MutableLiveData<T> get(String key, Class<T> clazz) {
        String keyName;
        if (TextUtils.isEmpty(key)) {
            keyName = clazz.getCanonicalName();
        } else {
            keyName = key;
        }
        MutableLiveData<T> mutableLiveData = maps.get(keyName);
        // 判断集合是否已经存在 LiveData 对象,若存在就返回
        if (mutableLiveData != null) {
            return mutableLiveData;
        }
        // 如果 Map 集合中没有对应实体类的 LiveData 对象,就创建并添加至集合中
        mutableLiveData = new MutableLiveData<>();
        assert keyName != null;
        maps.put(keyName, mutableLiveData);
        return mutableLiveData;
    }

    /**
     * 在对应的FragmentActivity销毁之后调用
     */
    @SuppressWarnings("unchecked")
    @Override
    protected void onCleared() {
        super.onCleared();
        if (maps != null) {
            maps.clear();
        }
    }
}
  • ViewModel类中的操作:
public class DemoViewModel extends BaseViewModel {
    // 获取 Person 对象对应的 MutableLiveData
    MutableLiveData<Person> getPersonMutableLiveData() {
        return get(Person.class);
    }
}
  • 创建对应的ViewModel:
/**
     * 创建ViewModel对象
     * 在Activity或Fragment需要获取对应的 ViewModel 的时候调用
     * @param clazz 
     * @return 对应的 T 的 ViewModel
     * 泛型中的限定,必须是ViewModel的子类
     */
    public <T extends ViewModel> T get(Class<T> clazz) {
        return viewModelProvider.get(clazz);
    }

    /**
     * 初始化 ViewModelProvider 对象
     * 建议在Activity初始化的时候执行
     * @return ViewModelProvider
     */
    private ViewModelProvider getViewModelProvider() {
        return ViewModelProviders.of(this);
    }
  • 在Activity 或 Fragment 中通过 ViewModel 获取到创建好的 LiveData 观察数据的变化:
@Route(path = "/test/demo")
public class DemoActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        DemoViewModel demoViewModel = get(DemoViewModel.class);
        MutableLiveData<Person> personMutableLiveData = demoViewModel.getPersonMutableLiveData();
        // 1.添加数据更改监听器  监听数据的回调
        personMutableLiveData.observe(this, new Observer<Person>() {
            @Override
            public void onChanged(@Nullable Person person) {
                Log.e("DemoActivity = ", "DemoActivity中接收person:" + person.toString());
                Log.e("DemoActivity = ", "线程 = :" + Thread.currentThread().getName());  // 打印结果主线程
            }
        });
    }

    public void onClick(View view){
        new Thread(new Runnable() {
            @Override
            public void run() {
                getData();
                Log.e("DemoActivity = ", "线程 *****  &&&&& = :" + Thread.currentThread().getName());
            }
        }).start();
    }

    // 更改数据
    public void getData() {
        Person person = new Person();
        person.setName("Jack");
        person.setSex("男");
        DemoViewModel demoViewModel = get(DemoViewModel.class);
        MutableLiveData<Person> personMutableLiveData = demoViewModel.getPersonMutableLiveData();
        //同步更改setValue  ;  异步更改postValue
        personMutableLiveData.setValue(person);
//        personMutableLiveData.postValue(person);  // 添加数据
    }
}

通过上面的案例大家应该知道怎么使用了,接下来我们进阶使用一下:
先给大家画张图:


进阶使用图

先来创建几个base基础类:

  • BaseDataModel:
/**
 * 在生命周期的某个时刻取消订阅。
 * 一个很常见的模式就是使用CompositeSubscription来持有所有的Subscriptions,然后在onDestroy()或者onDestroyView()里取消所有的订阅
 */
public abstract class BaseDataModel {

    // 可以缓解Rx内存占用不能释放的问题
    private CompositeSubscription mCompositeSubscription;

    public BaseDataModel() {

    }

    // 添加订阅
    protected void addSubscribe(Subscription subscription) {
        if (mCompositeSubscription == null) {
            mCompositeSubscription = new CompositeSubscription();
        }
        mCompositeSubscription.add(subscription);
    }

    // 移除订阅
    public void unSubscribe() {
        if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
            mCompositeSubscription.clear();
        }
    }
}
  • BaseViewModel:
public class BaseViewModel<T extends BaseDataModel> extends AndroidViewModel {

    public MutableLiveData<String> loadState; // 网络加载状态的 LiveData

    public T mDataModel;

    public BaseViewModel(@NonNull Application application) {
        super(application);
        loadState = new MutableLiveData<>();
        mDataModel = DemoUtil.getNewInstance(this, 0);
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        if (mDataModel != null) {
            mDataModel.unSubscribe();  // 清除所有订阅 释放内存 (Rx))
        }
    }
}
  • BaseLifecycleFragment:
public abstract class BaseLifecycleFragment<T extends BaseViewModel> extends BaseFragment {

    protected T mViewModel;

    @Override
    public void initView() {
        mViewModel = createViewModel(this, (Class<T>) DemoUtil.getInstance(this, 0));
        if (null != mViewModel) {
            MutableLiveData loadState = mViewModel.loadState;
            loadState.observe(this, observer);
            dataObserver();
        }
    }

    /**
     * 创建 自定义的 ViewModel
     */
    protected <T extends ViewModel> T createViewModel(Fragment fragment, @NonNull Class<T> modelClass) {
        ViewModelProvider viewModelProvider = ViewModelProviders.of(fragment);
        return viewModelProvider.get(modelClass);
    }

    /**
     *  LiveData 观察者回调实现的方法
     */
    protected abstract void dataObserver();

    // lifecycle 中 liveData的监听者
    protected Observer<String> observer = new Observer<String>() {
        @Override
        public void onChanged(@Nullable String state) {
            if (!TextUtils.isEmpty(state)) {
                if (StateConstants.ERROR_STATE.equals(state)) {
                    showToast("加载错误");
                } else if (StateConstants.NET_WORK_STATE.equals(state)) {
                    showToast("网络不好,请稍后重试");
                } else if (StateConstants.LOADING_STATE.equals(state)) {
                    showToast("加载中");
                } else if (StateConstants.SUCCESS_STATE.equals(state)) {
                    showToast("加载成功");
                }
            }
        }
    };
}

工具类:

public class DemoUtil {
    public static <T> T getNewInstance(Object object, int i) {
        if(object!=null){
            try {
                return ((Class<T>) ((ParameterizedType) (object.getClass()
                        .getGenericSuperclass())).getActualTypeArguments()[i])
                        .newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassCastException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    // 获取泛型类型
    public static <T> T getInstance(Object object, int i) {
        if (object != null) {
            return (T) ((ParameterizedType) object.getClass()
                    .getGenericSuperclass())
                    .getActualTypeArguments()[i];
        }
        return null;
    }

    // 检查引用是否为空
    public static @NonNull
    <T> T checkNotNull(final T reference) {
        if (reference == null) {
            throw new NullPointerException();
        }
        return reference;
    }
}
  • 创建回调接口:
public interface CallBack<T> {
    // 没网络
    void onNoNetWork();
    // 成功
    void onNext(T t);
    // 失败
    void onError(String e);
}

准备工作做好了接下来看如何使用:

  • HomeDataModel:
/**
 * 网络请求和数据库的操作
 * ApiDataModel 这个类中主要创建 serviceApi 接口
 */
public class HomeDataModel extends ApiDataModel {

    public void requestNetWorHomekData(CallBack listener) {
        Observable<HomeResponse> homeResponseObservable = serviceApi.requestHomeData();
        addSubscribe(
                homeResponseObservable
                        .compose(RxSchedulers.io_main())
                        .subscribe(new RxSubscriber<Object>() {

                            @Override
                            protected void onNoNetWork() {
                                super.onNoNetWork();
                                listener.onNoNetWork();
                            }

                            @Override
                            public void onSuccess(Object o) {
                                listener.onNext(o);
                            }

                            @Override
                            public void onFailure(String msg) {
                                listener.onError(msg);
                            }
                        }));
    }
}
  • HomeViewModel:
/**
 * 业务逻辑处理
 */
public class HomeViewModel extends NetWorkBaseViewModel<HomeDataModel> {

    public HomeViewModel(@NonNull Application application) {
        super(application);
    }

    private MutableLiveData<HomeResponse> homeMutableLiveData;  // 存储首页数据的 MutableLiveData

    public MutableLiveData<HomeResponse> getHomeMutableLiveData(){
        if (homeMutableLiveData == null) {
            homeMutableLiveData = new MutableLiveData<>();
        }
        return homeMutableLiveData;
    }

    // 发起网络请求
    public void getRequestHomeData() {

        mDataModel.requestNetWorHomekData(new CallBack<Object>() {
            @Override
            public void onNoNetWork() {
                Log.e("HomeViewModel 错误 = ", "网络异常");
                loadState.postValue(StateConstants.NET_WORK_STATE);
            }

            @Override
            public void onNext(Object object) {
                if (object instanceof HomeResponse) {
                    HomeResponse homeResponse = (HomeResponse) object;
                    homeMutableLiveData.postValue(homeResponse);
                    loadState.postValue(StateConstants.SUCCESS_STATE);
                }
            }

            @Override
            public void onError(String e) {
                Log.e("HomeViewModel 错误 = ", e);
            }
        });
    }
}
  • HomeFragment展示给用户显示:
public class HomeFragment extends BaseLifecycleFragment<HomeViewModel> {
  
    public static HomeFragment newInstance() {
        return new HomeFragment();
    }

    @Override
    protected void dataObserver() {
        mViewModel.getHomeMutableLiveData().observe(this, new Observer<HomeResponse>() {
            @Override
            public void onChanged(@Nullable HomeResponse homeResponse) {
                showData(homeResponse);
            }
        });
    }

    /**
     * 列表展示数据
     */
    private void showData(HomeResponse homeResponse) {
        Log.e("HomeFragment = ",homeResponse.toString());
    }

    @Override
    protected View initLayout(LayoutInflater inflater, ViewGroup container) {
        return inflater.inflate(R.layout.activity_target, null);
    }

    @Override
    public void initView() {
        super.initView();
    }

    @Override
    protected void initData(Bundle savedInstanceState) {
        mViewModel.getRequestHomeData();
    }
}

运行起来打印数据:

数据

网络请求是用的 Retrofit + OkHttp,具体怎么使用:
Retrofit:https://www.jianshu.com/p/dac9a1c02525
OkHttp:https://www.jianshu.com/p/500abf06f447

这样只要DataModel中数据一变化,View中就可以立即监听到数据变化,做出相应操作。

对这个方法的具体解释:

public static <T> T getInstance(Object object, int i) {
        if (object != null) {
            return (T) ((ParameterizedType) object.getClass()
                    .getGenericSuperclass())
                    .getActualTypeArguments()[i];
        }
        return null;
    }

关于怎么获取泛型中参数类型的分析:https://www.jianshu.com/p/27772c32fa41

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341