livedata+ViewModel踩坑&原理分析

最近要提测的时候暴露了一些问题,凌晨提了一笔比较挫的提交进行了紧急修复;但是才发现我们的项目中用到的一些架构我们并没有完全理解,所以导致了错误;因此有必要自己进行下梳理;

前言

我们的项目中用的是Android Jetpack中的navigation+viewmodel+livedata+room这些框架; 这些框架大大方便了我们的开发,但是如果使用不正确,却会出现一些意想不到的问题;在这边文章中介绍下livedata+viewmodel的使用中需要注意的一个问题;顺便分析下相关源码

问题

ViewModel+livedata的使用造成了同一个错误数据多次处理; 解决方案SingleLiveEvent或者类似方案

ViewModel

首先什么是ViewModel,见官方文档

大致总结下自己理解的关键点

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModelclass allows data to survive configuration changes such as screen rotations.

其实是用来存储UI相关的数据的,但是不用使用者来关心UI的生命周期对数据造成的影响;从而时数据管理和UI显示的逻辑做到了隔离

ViewModel的生命周期

ViewModel objects are scoped to the Lifecycle passed to the ViewModelProvider when getting the ViewModel. The ViewModel remains in memory until the Lifecycle it's scoped to goes away permanently: in the case of an activity, when it finishes, while in the case of a fragment, when it's detached.

Activity或者fragment销毁的时候会将相应的ViewModel也销毁

ViewModel objects can contain LifecycleObservers, such as LiveData objects. However ViewModel objects must never observe changes to lifecycle-aware observables, such as LiveData objects.

注意viewmodel经常和livedata一起使用,但在其中不能监听livedata

If the ViewModel needs theApplication context, for example to find a system service, it can extend the AndroidViewModel class and have a constructor that receives the Application in the constructor, since Application class extends Context.

如果需要用Application的context来创建ViewModel对象,那么就继承AndroidViewModel

获取ViewModel:
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);

看看其中的源码:

ViewModelProviders

/**
 * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given
 * {@code fragment} is alive. More detailed explanation is in {@link ViewModel}.
 * <p>
 * It uses the given {@link Factory} to instantiate new ViewModels.
 *
 * @param fragment a fragment, in whose scope ViewModels should be retained
 * @param factory  a {@code Factory} to instantiate new ViewModels
 * @return a ViewModelProvider instance
 */
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
    Application application = checkApplication(checkActivity(fragment));
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); //如果Factory为空,那么只有一个class只有一个ViewModel;
    }
    return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}

如果没有定义Factory,就会默认使用AndroidViewModelFactory;这个Factory只能创建构造参数为空的ViewModel和AndroidViewModel(Subclasses must have a constructor which accepts {@link Application} as the only parameter)

    /**
     * Simple factory, which calls empty constructor on the give class.
     */
    public static class NewInstanceFactory implements Factory {

        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
    }
    /**
     * {@link Factory} which may create {@link AndroidViewModel} and
     * {@link ViewModel}, which have an empty constructor.
     */
    public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

        private static AndroidViewModelFactory sInstance;

        /**
         * Retrieve a singleton instance of AndroidViewModelFactory.
         *
         * @param application an application to pass in {@link AndroidViewModel}
         * @return A valid {@link AndroidViewModelFactory}
         */
        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

        private Application mApplication;

        /**
         * Creates a {@code AndroidViewModelFactory}
         *
         * @param application an application to pass in {@link AndroidViewModel}
         */
        public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }

        @NonNull
        @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);
        }
    }

ViewModelProvider

/**
 * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
 * an activity), associated with this {@code ViewModelProvider}.
 * <p>
 * The created ViewModel is associated with the given scope and will be retained
 * as long as the scope is alive (e.g. if it is an activity, until it is
 * finished or process is killed).
 *
 * @param key        The key to use to identify the ViewModel.
 * @param modelClass The class of the ViewModel to create an instance of it if it is not
 *                   present.
 * @param <T>        The type parameter for the ViewModel.
 * @return A ViewModel that is an instance of the given type {@code T}.
 */
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);

    if (modelClass.isInstance(viewModel)) {
        //noinspection unchecked
        return (T) viewModel;
    } else {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }

    viewModel = mFactory.create(modelClass);
    mViewModelStore.put(key, viewModel);
    //noinspection unchecked
    return (T) viewModel;
}

/**
 * Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
 * {@code Factory} and retain them in the given {@code store}.
 *
 * @param store   {@code ViewModelStore} where ViewModels will be stored.
 * @param factory factory a {@code Factory} which will be used to instantiate
 *                new {@code ViewModels}
 */
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    this.mViewModelStore = store;
}

可见ViewModelProvider有两部分组成 ViewModelStore和Factory; get的时候先看ViewModelStore是否有保存这个class对应的ViewModel实例,如果有直接返回;没有的话通过factory创建一个;每次ViewModelProviders.of的时候都会新建一个ViewModelProvider,但不代表ViewModel也会新创建实例; 而是根据ViewModelProvider中的ViewModelStore是否有存储ViewModel class类型的实例来判断是否要新建ViewModel实例

ViewModelStores

/**
 * Returns the {@link ViewModelStore} of the given activity.
 *
 * @param activity an activity whose {@code ViewModelStore} is requested
 * @return a {@code ViewModelStore}
 */
@NonNull
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
    if (activity instanceof ViewModelStoreOwner) {
        return ((ViewModelStoreOwner) activity).getViewModelStore();
    }
    return holderFragmentFor(activity).getViewModelStore();
}

/**
 * Returns the {@link ViewModelStore} of the given fragment.
 *
 * @param fragment a fragment whose {@code ViewModelStore} is requested
 * @return a {@code ViewModelStore}
 */
@NonNull
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
    if (fragment instanceof ViewModelStoreOwner) {
        return ((ViewModelStoreOwner) fragment).getViewModelStore();
    }
    return holderFragmentFor(fragment).getViewModelStore();
}

可见ViewModelStore是与HolderFragment一一对应,也就是与activity或者fragment一一对应; 即决定ViewModel实例对象的ViewModelStore与其宿主是强相关的;

HolderFragment holderFragmentFor(Fragment parentFragment) {
    FragmentManager fm = parentFragment.getChildFragmentManager();
    HolderFragment holder = findHolderFragment(fm);
    if (holder != null) {
        return holder;
    }
    holder = mNotCommittedFragmentHolders.get(parentFragment);
    if (holder != null) {
        return holder;
    }

    parentFragment.getFragmentManager()
            .registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
    holder = createHolderFragment(fm);
    mNotCommittedFragmentHolders.put(parentFragment, holder);
    return holder;
}

private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
    HolderFragment holder = new HolderFragment();
    fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
    return holder;
}

HolderFragment

The HolderFragment has a couple of ways of looking for the HolderFragment associated with your Fragment, and if not found, it will create new HolderFragment and add it on the FragmentManager of our own Fragment.

public HolderFragment() {

    setRetainInstance(true);

}

By setting retain instance to true and not providing a view, the HolderFragment becomes essentially a headless Fragment that is retained for as long as the Activity is not destroyed.

A HolderFragment is a headless Fragment (without UI) that is added to the Fragment stack with setRetainInstance(true).

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);
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}

注意一点ViewModel是与className绑定的,所以一般来说ViewModel是不会改变的,有点类似于单例;但是注意,ViewModel存储在ViewModelStore中,而ViewModelProvider中保存ViewModelStore;那么ViewModelProvider改变就会造成ViewModel的重建;而得到ViewModelProvider的方法有ViewModelProviders.of,一个参数和两个参数都有;第二个参数可能是自定义的factory;但是注意,ViewModel是否会重新生成只和ViewModelStore相关;而ViewModelStore是和activity或fragment(以及对应的HolderFragment绑定的,如果宿主不是androidx的activity或fragment);所以只要宿主中保存的mViewModelStore没有clear;那么ViewModel就会一直存在

ViewModel在Fragement或者Activity调用onDestroy时会被销毁(调用ViewModelStore的clear)

但是特别注意一点;是所有的onDestroy都会造成销毁吗? 并不是

见package android.support.v4.app;

FragmentActivity 和Fragment

看其中的onDestroy,发现了吗?

FragmentActivity

protected void onDestroy() {
    super.onDestroy();
    if (this.mViewModelStore != null && !this.isChangingConfigurations()) {
        this.mViewModelStore.clear();
    }

    this.mFragments.dispatchDestroy();
}

Fragment

@CallSuper
public void onDestroy() {
    this.mCalled = true;
    FragmentActivity activity = this.getActivity();
    boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations();
    if (this.mViewModelStore != null && !isChangingConfigurations) {
        this.mViewModelStore.clear();
    }

}

可以看到宿主activity或者宿主fragment依赖的activity在config change造成的onDestroy时是不会造成ViewModel重构的;

原理应该在

@NonNull
public ViewModelStore getViewModelStore() {
    if (this.getApplication() == null) {
        throw new IllegalStateException("Your activity is not yet attached to the Application instance. You can't request ViewModel before onCreate call.");
    } else {
        if (this.mViewModelStore == null) {
            FragmentActivity.NonConfigurationInstances nc = (FragmentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance();
            if (nc != null) {
                this.mViewModelStore = nc.viewModelStore;
            }

            if (this.mViewModelStore == null) {
                this.mViewModelStore = new ViewModelStore();
            }
        }

        return this.mViewModelStore;
    }
}
/**
 * Retrieve the non-configuration instance data that was previously
 * returned by {@link #onRetainNonConfigurationInstance()}.  This will
 * be available from the initial {@link #onCreate} and
 * {@link #onStart} calls to the new instance, allowing you to extract
 * any useful dynamic state from the previous instance.
 *
 * <p>Note that the data you retrieve here should <em>only</em> be used
 * as an optimization for handling configuration changes.  You should always
 * be able to handle getting a null pointer back, and an activity must
 * still be able to restore itself to its previous state (through the
 * normal {@link #onSaveInstanceState(Bundle)} mechanism) even if this
 * function returns null.
 *
 * <p><strong>Note:</strong> For most cases you should use the {@link Fragment} API
 * {@link Fragment#setRetainInstance(boolean)} instead; this is also
 * available on older platforms through the Android support libraries.
 *
 * @return the object previously returned by {@link #onRetainNonConfigurationInstance()}
 */
@Nullable
public Object getLastNonConfigurationInstance() {
    return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
}

这样,config change引起的onDestroy不会造成数据丢失;这当然是很好的,这也正是ViewModel的一大卖点,config change时不用手动处理生命周期保存数据;

但是这往往也会造成某些意想不到的情况;当config change时数据没有清空;等到宿主recreate的时候,重新创建observer就会造成同一个数据多次处理的问题; 关于生命周期-感觉设计的非常巧妙,觉得有价值单开一篇进行梳理,见ViewModel相关生命周期的原理分析-之三

这是我们项目中使用viewmodel+livedata踩的一个坑;我们会在livedata中进行更进一步的讲解 参考文档

https://medium.com/the-lair/internals-of-android-architecture-components-part-i-the-viewmodel-d893e362a0d9

https://medium.com/google-developer-experts/viewmodels-under-the-hood-f8e286c4cc72

ViewModel总结:

利用ViewModel可以用来存储UI相关的数据,一般其中保存observerable数据(如livedata),可以无需对UI生命周期的管理

1.ViewModel其与ViewModelProviders,ViewModelProvider,ViewModelStores,ViewModelStore,HolderFragment有关联

2.ViewModelProvider 依赖ViewModelStore和Factory相关,Factory如果写的不好,一直创建新的,那么就会一直创建新的ViewModelProvider,但是注意,你能得到的ViewModel还是和你的ViewModel数组有关,不要想当然的以为Factory不同会创建新的ViewModel; ViewModel只有同一个class的上一个ViewModel销毁才会更新到下一个;

3.ViewModel的生命周期和其宿主(activity和fragment)一直,只有onDestroy时会被销毁; ViewModelStore,HolderFragment,ViewModel是一一对应的;对于非androidx宿主通过HolderFragment用来保存数据(如旋转屏幕时), 其会监听宿主component的销毁,销毁时将ViewModelStore clear;则将ViewModel销毁;注意,config change造成的宿主销毁不会造成对应的ViewModel销毁,这也正是ViewModel的一大卖点;不过我们要注意,这个特性往往也会造成意想不到的bug;请开发者特别注意;

4.Fragment也可以向Activity一样使用;但是这样一定需要确认你使用时是否fragment调用了onDestory,注意ViewModel的生命周期,需要根据业务情况进行处理;google官网的推荐方式是fragment使用ViewModel用来共享数据

  1. HolderFragment是为了兼容用的,会在单独的文章里分析这里面的原理;
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.

        });

    }

}

像这种情况,两个fragment共享一个ViewModel;一个是observer,一共提供observable数据

LiveData

下面介绍LiveData
https://developer.android.com/topic/libraries/architecture/livedata

LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.

这段话说的很好了,livedata是一个observable的数据源,只不过其对生命周期是敏感的;也就是只有active的component才能接收到数据变化; 同时要注意,只有androidx的support activity/fragment可以注册对livedata的监听;否则就只能通过observeForever注册一直active的observer;在不需要时自己移除

public class ComponentActivity extends Activity implements LifecycleOwner 

原因是普通的activity并没有实现LifecycleOwner interface

使用方式

创建数据源

数据源LiveData<T> T是要保存的数据类型; LiveData<T> 一般作为一个对象放在一个ViewModel里面

public class NameViewModel extends ViewModel {

// Create a LiveData with a String
private MutableLiveData<String> currentName;

    public MutableLiveData<String> getCurrentName() {
        if (currentName == null) {
            currentName = new MutableLiveData<String>();
        }
        return currentName;
    }

// Rest of the ViewModel...
}

观察数据源

创建一个Observer,然后当observer的宿主在active状态时,数据发生变化时会收到通知

public class NameActivity extends AppCompatActivity {

    private NameViewModel model;

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

        // Other code to setup the activity...

        // Get the ViewModel.
        model = ViewModelProviders.of(this).get(NameViewModel.class);


        // Create the observer which updates the UI.
        final Observer<String> nameObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                // Update the UI, in this case, a TextView.
                nameTextView.setText(newName);
            }
        };
        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        model.getCurrentName().observe(this, nameObserver);
    }
}

After observe() is called with nameObserver passed as parameter, onChanged() is immediately invoked providing the most recent value stored in mCurrentName. If the LiveData object hasn't set a value in mCurrentName, onChanged() is not called.

更新数据源

button.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        String anotherName = "John Doe";
        model.getCurrentName().setValue(anotherName);
    }
});

通过setValue或者postValue来触发数据的更新

扩展使用

反向观察

LiveData considers an observer to be in an active state if the observer's lifecycle is in either the STARTED or RESUMED states The following sample code illustrates how to extend the LiveData class:
数据源可以反向观察当前是否有active observer

public class StockLiveData extends LiveData<BigDecimal> {
    private StockManager stockManager;

    private SimplePriceListener listener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };

    public StockLiveData(String symbol) {
        stockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
        stockManager.requestPriceUpdates(listener);
    }

    @Override
    protected void onInactive() {
        stockManager.removeUpdates(listener);
    }
}

livedata传递

这个用法有点类似于rxjava中的map;对数据源进行加工处理再传出去

private LiveData<User> getUser(String id) {
  ...;
}

LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );

The transformations aren't calculated unless an observer is watching the returned LiveData object. Because the transformations are calculated lazily, lifecycle-related behavior is implicitly passed down without requiring additional explicit calls or dependencies.

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    private final MutableLiveData<String> addressInput = new MutableLiveData();
    public final LiveData<String> postalCode =
            Transformations.switchMap(addressInput, (address) -> {
                return repository.getPostCode(address);
             });

  public MyViewModel(PostalCodeRepository repository) {
      this.repository = repository
  }

  private void setInput(String address) {
      addressInput.setValue(address);
  }
}

In this case, the postalCode field is defined as a transformation of the addressInput. As long as your app has an active observer associated with the postalCode field, the field's value is recalculated and retrieved wheneveraddressInput changes.

类似懒加载的机制,只有订阅了postalCode这个livedata,才会开始计算

switchMap源码分析

/**
 * Creates a LiveData, let's name it {@code swLiveData}, which follows next flow:
 * it reacts on changes of {@code trigger} LiveData, applies the given function to new value of
 * {@code trigger} LiveData and sets resulting LiveData as a "backing" LiveData
 * to {@code swLiveData}.
 * "Backing" LiveData means, that all events emitted by it will retransmitted
 * by {@code swLiveData}.
 * <p>
 * If the given function returns null, then {@code swLiveData} is not "backed" by any other
 * LiveData.
 *
 * <p>
 * The given function {@code func} will be executed on the main thread.
 *
 * <p>
 * Consider the case where you have a LiveData containing a user id. Every time there's a new
 * user id emitted, you want to trigger a request to get the user object corresponding to that
 * id, from a repository that also returns a LiveData.
 * <p>
 * The {@code userIdLiveData} is the trigger and the LiveData returned by the {@code
 * repository.getUserById} is the "backing" LiveData.
 * <p>
 * In a scenario where the repository contains User(1, "Jane") and User(2, "John"), when the
 * userIdLiveData value is set to "1", the {@code switchMap} will call {@code getUser(1)},
 * that will return a LiveData containing the value User(1, "Jane"). So now, the userLiveData
 * will emit User(1, "Jane"). When the user in the repository gets updated to User(1, "Sarah"),
 * the {@code userLiveData} gets automatically notified and will emit User(1, "Sarah").
 * <p>
 * When the {@code setUserId} method is called with userId = "2", the value of the {@code
 * userIdLiveData} changes and automatically triggers a request for getting the user with id
 * "2" from the repository. So, the {@code userLiveData} emits User(2, "John"). The LiveData
 * returned by {@code repository.getUserById(1)} is removed as a source.
 *
 * <pre>
 * MutableLiveData<String> userIdLiveData = ...;
 * LiveData<User> userLiveData = Transformations.switchMap(userIdLiveData, id ->
 *     repository.getUserById(id));
 *
 * void setUserId(String userId) {
 *      this.userIdLiveData.setValue(userId);
 * }
 * </pre>
 *
 * @param trigger a {@code LiveData} to listen to
 * @param func    a function which creates "backing" LiveData
 * @param <X>     a type of {@code source} LiveData
 * @param <Y>     a type of resulting LiveData
 */
@MainThread
public static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger,
        @NonNull final Function<X, LiveData<Y>> func) {
    final MediatorLiveData<Y> result = new MediatorLiveData<>();
    result.addSource(trigger, new Observer<X>() {
        LiveData<Y> mSource; 

        @Override
        public void onChanged(@Nullable X x) {
            LiveData<Y> newLiveData = func.apply(x); 
            if (mSource == newLiveData) {
                return;
            }
            if (mSource != null) {
                result.removeSource(mSource);
            }
            mSource = newLiveData;
            if (mSource != null) {
                result.addSource(mSource, new Observer<Y>() {
                    @Override
                    public void onChanged(@Nullable Y y) {
                        result.setValue(y);
                    }
                });
            }
        }
    }); //先针对trigger livedata x添加一个Observer;其会等到x的数据变化触发
    return result;
}

先注册trigger livedata的observer (MediatorLiveData中的Observer)

MediatorLiveData observer触发路径.png

在Observer中通过function调用了网络接口,其结果作为一个livedata; 然后

result.addSource(mSource, new Observer<Y>() {

    @Override

    public void onChanged(@Nullable Y y) {

        result.setValue(y);

    }

});

然后对将要返回的result (MediatorLiveData);对这个新生成的livedata进行监听;等到网络请求返回后,会触发result中的observer; 调用result.setVaule(y) ,其中y是返回结果;从而使得result livedata得到了更新;

可以看出这很方法是很巧妙的; 返回的result livedata;即作为 MediatorLiveData 监听者;又作为livedata updater 数据源

public static LiveData<Y> switchMap (LiveData<X> source, 
                Function<X, LiveData<Y>> switchMapFunction)

There are a dozen different specific transformation that may be useful in your app, but they aren’t provided by default. To implement your own transformation you can you use the MediatorLiveData class, which listens to other LiveDataobjects and processes events emitted by them.
可以自创建Transformations; 相当于为你提供更多可能,比如Function返回值LiveData可以替换
如类似这种

public class TransformationsForSingleLiveEvent {

    @MainThread
    public static <X, Y> MediatorSingleLiveEvent<Y> switchMap(@NonNull LiveData<X> trigger,
                                                      @NonNull final Function<X, LiveData<Y>> func) {
        final MediatorSingleLiveEvent<Y> result = new MediatorSingleLiveEvent<>();
        result.addSource(trigger, new Observer<X>() {
            LiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                //trigger变化时得到的livedata
                LiveData<Y> newLiveData = func.apply(x); //由trigger livedata中的新数据
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }
}
public class MediatorSingleLiveEvent<T> extends MediatorLiveData<T> {

    private static final String TAG = "MediatorSingleLiveEvent";

    private final AtomicBoolean mPending = new AtomicBoolean(false);

    @MainThread
    public void observe(LifecycleOwner owner, final Observer<T> observer) {

        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
        }

        // Observe the internal MutableLiveData
        super.observe(owner, new Observer<T>() {
            @Override
            public void onChanged(@Nullable T t) {
                if (mPending.compareAndSet(true, false)) {
                    observer.onChanged(t);
                }
            }
        });
    }

    @MainThread
    public void setValue(@Nullable T t) {
        mPending.set(true);
        super.setValue(t);
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    public void call() {
        setValue(null);
    }
}

swicthMap返回结果此时包含SingleLiveEvent属性

MediatorLiveData

MediatorLiveData is a subclass of LiveData that allows you to merge multiple LiveData sources. Observers of MediatorLiveData objects are then triggered whenever any of the original LiveData source objects change.
For example, if you have a LiveData object in your UI that can be updated from a local database or a network, then you can add the following sources to the MediatorLiveData object
相当于Livedata数据源可以被多个地方更新会用到这里

更多使用的注意事项

参考文献
https://medium.com/androiddevelopers/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54

livedata源码分析

setValue

看下setValue之后是如何通知observer的

/**
 * Sets the value. If there are active observers, the value will be dispatched to them.
 * <p>
 * This method must be called from the main thread. If you need set a value from a background
 * thread, you can use {@link #postValue(Object)}
 * @param value The new value
 */
@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

看注释,set的时候回自动通知observer; 但是为什么observer重新生成的时候有数据也会调用?

dispatchingValue

可以看到处理value的时候有两种情况,

一种是setValue的时候;针对全部的active observer

还有一种情况是observer变成active的时候主动找它的

private void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) { //这里应该是initiator重新变为active observer,会重新触发观察者 
            considerNotify(initiator); 
            initiator = null;
        } else {
            for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}
private abstract class ObserverWrapper {
    final Observer<T> mObserver;
    boolean mActive;
    int mLastVersion = START_VERSION;

    ObserverWrapper(Observer<T> observer) {
        mObserver = observer;
    }

    abstract boolean shouldBeActive();

    boolean isAttachedTo(LifecycleOwner owner) {
        return false;
    }

    void detachObserver() {
    }

    void activeStateChanged(boolean newActive) {
        if (newActive == mActive) {
            return;
        }
        // immediately set active state, so we'd never dispatch anything to inactive
        // owner
        mActive = newActive;
        boolean wasInactive = LiveData.this.mActiveCount == 0;
        LiveData.this.mActiveCount += mActive ? 1 : -1;
        if (wasInactive && mActive) {
            onActive();
        }
        if (LiveData.this.mActiveCount == 0 && !mActive) {
            onInactive();
        }
        if (mActive) {
            dispatchingValue(this); //当observer的owner 的activeStateChanged,变为active的时候;ViewModel保存的数据还在;因此还会调用onChanged 
        }
    }
}
private void considerNotify(ObserverWrapper observer) {
//observer不是active的,直接return
    if (!observer.mActive) {
        return;
    }
    // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
    //
    // we still first check observer.active to keep it as the entrance for events. So even if
    // the observer moved to an active state, if we've not received that event, we better not
    // notify for a more predictable notification order.
    if (!observer.shouldBeActive()) {
       //相当于处理特殊情况
        observer.activeStateChanged(false); //将observer置位unactive
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    //noinspection unchecked
    observer.mObserver.onChanged((T) mData); //调用Observer 的onChanged
}

//如何判断observer是否是active

@Override
boolean shouldBeActive() {
    return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); //根据mOwner;即LifecycleOwner的lifecycle来决定是否应该active 
}
/**
 * A class that has an Android lifecycle. These events can be used by custom components to
 * handle lifecycle changes without implementing any code inside the Activity or the Fragment.
 *
 * @see Lifecycle
 */
@SuppressWarnings({"WeakerAccess", "unused"})
public interface LifecycleOwner {
    /**
     * Returns the Lifecycle of the provider.
     *
     * @return The lifecycle of the provider.
     */
    @NonNull
    Lifecycle getLifecycle();
}
/**
 * Lifecycle states. You can consider the states as the nodes in a graph and
 * {@link Event}s as the edges between these nodes.
 */
@SuppressWarnings("WeakerAccess")
public enum State {
    /**
     * Destroyed state for a LifecycleOwner. After this event, this Lifecycle will not dispatch
     * any more events. For instance, for an {@link android.app.Activity}, this state is reached
     * <b>right before</b> Activity's {@link android.app.Activity#onDestroy() onDestroy} call.
     */
    DESTROYED,

    /**
     * Initialized state for a LifecycleOwner. For an {@link android.app.Activity}, this is
     * the state when it is constructed but has not received
     * {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} yet.
     */
    INITIALIZED,

    /**
     * Created state for a LifecycleOwner. For an {@link android.app.Activity}, this state
     * is reached in two cases:
     * <ul>
     *     <li>after {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} call;
     *     <li><b>right before</b> {@link android.app.Activity#onStop() onStop} call.
     * </ul>
     */
    CREATED,

    /**
     * Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
     * is reached in two cases:
     * <ul>
     *     <li>after {@link android.app.Activity#onStart() onStart} call;
     *     <li><b>right before</b> {@link android.app.Activity#onPause() onPause} call.
     * </ul>
     */
    STARTED,

    /**
     * Resumed state for a LifecycleOwner. For an {@link android.app.Activity}, this state
     * is reached after {@link android.app.Activity#onResume() onResume} is called.
     */
    RESUMED;

    /**
     * Compares if this State is greater or equal to the given {@code state}.
     *
     * @param state State to compare with
     * @return true if this State is greater or equal to the given {@code state}
     */
    public boolean isAtLeast(@NonNull State state) {
        return compareTo(state) >= 0;
    }
}

注意这个顺序

也就是observer的active状态其实由activeStateChanged来赋值(started或者resumed)

class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
    @NonNull final LifecycleOwner mOwner;

    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {
        super(observer);
        mOwner = owner;
    }

    @Override
    boolean shouldBeActive() {
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        activeStateChanged(shouldBeActive()); //这里赋值
    }

    @Override
    boolean isAttachedTo(LifecycleOwner owner) {
        return mOwner == owner;
    }

    @Override
    void detachObserver() {
        mOwner.getLifecycle().removeObserver(this);
    }
}

最终会调用onStateChanged来修改state

代码的位置LifecycleRegistry里面修改state的状态

static class ObserverWithState {
    State mState;
    GenericLifecycleObserver mLifecycleObserver;

    ObserverWithState(LifecycleObserver observer, State initialState) {
        mLifecycleObserver = Lifecycling.getCallback(observer);
        mState = initialState;
    }

    void dispatchEvent(LifecycleOwner owner, Event event) {
        State newState = getStateAfter(event);
        mState = min(mState, newState);
        mLifecycleObserver.onStateChanged(owner, event);
        mState = newState;
    }
}

也就是每个LiveData的observer (ObserverWrapper) 其实由两部分组成; 一部分是真正的observer ;一个是管理生命周期,observer是否active

同时,注意这里

if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;

这里保证了旧的observer,就算其宿主重新active,也不会产生触发新的onChanged;因此只有新的observer注册或者livedata重新setValue时会触发onChanged;

那么如何注册一个observer

/**
 * Adds the given observer to the observers list within the lifespan of the given
 * owner. The events are dispatched on the main thread. If LiveData already has data
 * set, it will be delivered to the observer.
 * <p>
 * The observer will only receive events if the owner is in {@link Lifecycle.State#STARTED}
 * or {@link Lifecycle.State#RESUMED} state (active).
 * <p>
 * If the owner moves to the {@link Lifecycle.State#DESTROYED} state, the observer will
 * automatically be removed.
 * <p>
 * When data changes while the {@code owner} is not active, it will not receive any updates.
 * If it becomes active again, it will receive the last available data automatically.
 * <p>
 * LiveData keeps a strong reference to the observer and the owner as long as the
 * given LifecycleOwner is not destroyed. When it is destroyed, LiveData removes references to
 * the observer &amp; the owner.
 * <p>
 * If the given owner is already in {@link Lifecycle.State#DESTROYED} state, LiveData
 * ignores the call.
 * <p>
 * If the given owner, observer tuple is already in the list, the call is ignored.
 * If the observer is already in the list with another owner, LiveData throws an
 * {@link IllegalArgumentException}.
 *
 * @param owner    The LifecycleOwner which controls the observer
 * @param observer The observer that will receive the events
 */
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    owner.getLifecycle().addObserver(wrapper);
}

obersver宿主destory时会将observer注销

if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
    removeObserver(mObserver); /宿主owner在destroy状态时observer会自动move 
    return;
} 

也就是说数据源livedata通过setValue变化时会通知所有observer;新注册一个active observer时及时数据没变,也会主动触发onChanged;想一想也是符合常理的

总结

就我的理解;如果你要保存的数据是错误数据,observer进行处理,且处理一次; ViewModel+livedata的方式其实并不能体现出其优势。如果需要用这个框架,可能参考SingleLiveEvent是更好的,需要考虑到ViewModel,livedata的宿主对他们的生命周期造成的影响,否则可能就会出现多次处理的问题

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