LiveData 使用及原理解析

本文是基于 androidx.lifecycle:lifecycle-extensions:2.0.0 的源码进行分析

LiveData是一个类,将数据放在它里面我们可以观察数据的变化.但是它是江湖上那些妖艳贱货不一样的是它是lifecycle-aware(生命周期感知的).这个特性非常重要,我们可以用它来更新UI的数据,当且仅当activity、fragment或者Service是处于活动状态时。

LiveData一般用在ViewModel中,用于存放一些数据啥的,然后我们可以在Activity或者Fragment中观察其数据的变化(可能是访问数据库或者请求网络)展示数据到相应的UI上.这就是数据驱动视图,是MVVM模式的重要思想.

阅读本文需要读者了解Lifecycle原理,下面的很多东西都和Lifecycle的很多类相关,不清楚的朋友可以看我之前写的博客Lifecycle 使用及原理解析

其实谷歌出的Lifecycle和ViewModel,LiveData这些,都特别好用,设计得特别好,特别值得我们深入学习.

下面我将带大家走进LiveData的世界.

一、使用

1. 引入LiveData

//引入AndroidX吧,替换掉support包
implementation 'androidx.appcompat:appcompat:1.0.2'

def lifecycle_version = "2.0.0"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"

2. 简单使用起来

public class MainActivity extends AppCompatActivity {
    
    //1. 首先定义一个LiveData的实例  
    private MutableLiveData<String> mStringLiveData = new MutableLiveData<>();
    private TextView mContentTv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mContentTv = findViewById(R.id.tv_content);
        
        //2. 观察LiveData数据的变化,变化时将数据展示到TextView上
        mStringLiveData.observe(this, new Observer<String>() {
            @Override
            public void onChanged(String content) {
                //数据变化时会回调这个方法
                mContentTv.setText(content);
            }
        });
        
        findViewById(R.id.btn_test).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //3. 改变LiveData里面的数据 数据变化时,会回调上面的onChanged()方法
                mStringLiveData.postValue("新数据....");
            }
        });
    }
}
  1. 首先是定义一个LiveData的实例,LiveData是一个抽象类,MutableLiveData是它的一个子类.
  2. 然后调用LiveData的observe()方法,进行注册,开始观察数据,当我们的数据变化时,会回调onChanged()方法.
  3. LiveData有2个更新数据的方法
    • 一个是setValue(): 在主线程中调用,直接更新数据
    • 一个是postValue(): 在主线程或者子线程中调用都行,它最后其实是利用handler在主线程中调用setValue()方法实现的数据更新.

LiveData的使用非常简单.由于只是示例,所以我没有将LiveData放到ViewModel里面去.之前郭神写过一个MVVM的例子,大家可以看一看(地址是: https://github.com/guolindev/coolweatherjetpack ),里面将Jetpack的各种组件使用得淋漓尽致,让人直呼666.

下面是一小段优雅代码(想看更详细的,请到仓库查看):

viewModel.currentLevel.observe(this, Observer { level ->
    when (level) {
        LEVEL_PROVINCE -> {
            titleText.text = "中国"
            backButton.visibility = View.GONE
        }
        LEVEL_CITY -> {
            titleText.text = viewModel.selectedProvince?.provinceName
            backButton.visibility = View.VISIBLE
        }
        LEVEL_COUNTY -> {
            titleText.text = viewModel.selectedCity?.cityName
            backButton.visibility = View.VISIBLE
        }
    }
})
viewModel.dataChanged.observe(this, Observer {
    adapter.notifyDataSetChanged()
    listView.setSelection(0)
    closeProgressDialog()
})
viewModel.isLoading.observe(this, Observer { isLoading ->
    if (isLoading) showProgressDialog()
    else closeProgressDialog()
})

举上面这个例子,主要是让大家感受一下,其实LiveData真的用处很大,不仅可以拿来更新ListView,展示隐藏对话框,按钮展示隐藏等等.这些东西都是数据驱动的,当数据变化时,根本不需要另外多写代码,会回调observe()方法,数据就及时地更新到UI上了.简直天衣无缝啊.

二、源码分析

既然我们说了,LiveData这么牛逼,作为一个合格的开发人员.我们不能仅仅是API player,我们要知道其背后用的啥原理,日后深入使用时肯定会很有帮助.

从下面的代码开始入手:

mStringLiveData.observe(this, new Observer<String>() {
    @Override
    public void onChanged(String content) {
        mContentTv.setText(content);
    }
});

//调用上面的方法来到了LiveData的observe()方法
                        //1. 传入的是LifecycleOwner和Observer(观察者)
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    //2. 当前必须是在主线程  
    assertMainThread("observe");
    
    //3. 当前的生命周期如果是DESTROYED状态,那么不好意思,不能观察了
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    
    //4. 这里用装饰者模式将owner, observer封装起来
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    
    //5. 将观察者缓存起来
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    .....
    //6. 添加生命周期观察
    owner.getLifecycle().addObserver(wrapper);
}

首先第1点,我们传入的是Activity,到里面却看到是LifecycleOwner.翻看AppCompatActivity源码…

public class AppCompatActivity extends FragmentActivity{}

public class FragmentActivity extends ComponentActivit{}

public class ComponentActivity extends Activity
        implements LifecycleOwner{}

原来AppCompatActivity的爷爷(ComponentActivity)实现了LifecycleOwner接口,而LifecycleOwner接口是为了标识标记类有Android的生命周期的,比如Activity和Fragment.

第2点,必须是主线程中.

第3点,如果生命周期是DESTROYED,那么不好意思,不能继续往下走了.选择忽略.

第4点,将owner, observer封装了起来,形成一个LifecycleBoundObserver对象.

public interface GenericLifecycleObserver extends LifecycleObserver {
    void onStateChanged(LifecycleOwner source, Lifecycle.Event event);
}

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

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

LifecycleBoundObserver实现了GenericLifecycleObserver,而GenericLifecycleObserver是实现了LifecycleObserver(标记一个类是生命周期观察者).

第5点我们看到有一个mObservers,它其实是LiveData里面的一个属性,是用来缓存所有的LiveData的观察者的.

private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
            new SafeIterableMap<>();

再来看第6点,这个在Lifecycle里面讲过,它是用来添加观察者,最终用来观察LifecycleOwner(生命周期拥有者)的生命周期的,比如Activity或者Fragment等.

当Activity的生命周期发生变化时,会回调上面GenericLifecycleObserver(也就是上面的LifecycleBoundObserver)对象的onStateChanged()方法.

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

public void removeObserver(@NonNull final Observer<? super T> observer) {
    assertMainThread("removeObserver");
    ObserverWrapper removed = mObservers.remove(observer);
    if (removed == null) {
        return;
    }
    removed.detachObserver();
    removed.activeStateChanged(false);
}

当生命周期处于DESTROYED时,调用removeObserver()方法,移除观察者,那么在Activity中就不会收到回调了.

如何得知数据已经更新

LiveData提供了2种方式,setValue()和postValue()来更新数据.

1. setValue()方式更新数据

来看LiveData的setValue()方法

private volatile Object mData = NOT_SET;
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

首先是当前必须是主线程,然后将值保存到了mData属性中.

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    ......
    //遍历所有的观察者执行considerNotify()方法
    for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
            mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
        considerNotify(iterator.next().getValue());
    }
    ......
}

private void considerNotify(ObserverWrapper observer) {
    ......
    observer.mObserver.onChanged((T) mData);
}

然后调用dispatchingValue()方法,遍历所有的观察者,并回调onChanged()方法,数据即得到了更新.

2. postValue()方式更新数据

这种方式一般适用于在子线程中更新数据,更新UI的数据

volatile Object mPendingData = NOT_SET;

private final Runnable mPostValueRunnable = new Runnable() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            //嘿嘿 原来mPendingData到了这里
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        //noinspection unchecked
        //最后还是调用的setValue()嘛
        setValue((T) newValue);
    }
};


protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

将value值赋值给mPendingData,然后通过ArchTaskExecutor的实例将mPostValueRunnable传入postToMainThread()方法.

ArchTaskExecutor是何方神圣

public static ArchTaskExecutor getInstance() {
    if (sInstance != null) {
        return sInstance;
    }
    synchronized (ArchTaskExecutor.class) {
        if (sInstance == null) {
            sInstance = new ArchTaskExecutor();
        }
    }
    return sInstance;
}

private ArchTaskExecutor() {
    mDefaultTaskExecutor = new DefaultTaskExecutor();
    mDelegate = mDefaultTaskExecutor;
}

public class DefaultTaskExecutor extends TaskExecutor {
    @Nullable
    private volatile Handler mMainHandler;
    
    @Override
    public void postToMainThread(Runnable runnable) {
        if (mMainHandler == null) {
            synchronized (mLock) {
                if (mMainHandler == null) {
                    mMainHandler = new Handler(Looper.getMainLooper());
                }
            }
        }
        //noinspection ConstantConditions
        mMainHandler.post(runnable);
    }
}

通过ArchTaskExecutor里面的DefaultTaskExecutor里面的postToMainThread()方法,其实将mPostValueRunnable交给了一个mMainHandler,这个mMainHandler有主线程的looper.可以方便的将Runnable搞到主线程. 所以最后mPostValueRunnable会到主线程中执行setValue(),毫无问题.

三、小结

LiveData主要是依赖Lifecycle可以感知生命周期,从而避免了内存泄露.然后可以观察里面数据的变化来驱动UI数据展示.

最近刚学安卓开发,目前在使用mvvm模式进行开发。我发现LiveData有一个很有意思的属性: (https://developer.android.com/topic/libraries/architecture/livedata?hl=zh-cn) 通常,LiveData 仅在数据发生更改时才发送更新,并且仅发送给活跃观察者。此行为的一种例外情况是,观察者从非活跃状态更改为活跃状态时也会收到更新。此外,如果观察者第二次从非活跃状态更改为活跃状态,则只有在自上次变为活跃状态以来值发生了更改时,它才会收到更新。 这给我造成了些麻烦。比如说,当我返回上一个fragment时, 这个fragment会马上运行observe(),观察到的数据则是上一次运行得到的数据。我在观察方程里有关于view组建的reference。当观察方程立刻被触发时,有时候这些组件还没initialize完毕而造成npe。 我自己的例子是这样的:我有一个登陆的界面,用户按下登陆键view就会把记录下的用户名和密码(用一个searchEvent的类表示)传给viewmodel, viewmodel会把用Transformations.switchMap()来调用repository里的retrofit API同时view里有一个观察这个活动的方程。点击登陆登陆成功后,在那个fragment(我称之为fragment B)如果我按返回返回这个登陆界面,他会马上登陆成功,再次回到fragmentB里 请问该如何防止这种情况呢? 这是我viewmodel的代码: ``` public class LoginViewModel extends BaseViewModel<LoginRepository> { private final MutableLiveData<LoginEvent> loginEventMutableLiveData = new MutableLiveData<>(); private final LiveData<RemoteResponse<UserInfo>> remoteResponseMutableLiveData = Transformations .switchMap(loginEventMutableLiveData, repository::login); private final MutableLiveData<String> errMsgMutableLiveData = new MutableLiveData<>(); LoginViewModel(LoginRepository baseRepository) { super(baseRepository); } public LiveData<RemoteResponse<UserInfo>> getRemoteResponseMutableLiveData() { return remoteResponseMutableLiveData; } public MutableLiveData<String> getErrMsgMutableLiveData() { return errMsgMutableLiveData; } public void login(LoginEvent loginEvent) { if (Utils.isNullOrEmpty(loginEvent.userId)) { errMsgMutableLiveData.setValue("Please enter a valid password!"); return; } if (Utils.isNullOrEmpty(loginEvent.password)) { errMsgMutableLiveData.setValue("Please enter a valid password!"); return; } loginEventMutableLiveData.setValue(loginEvent); } } ``` 这是我loginfragment的代码: ``` public class LoginFragment extends BaseFragment<LoginViewModel, LoginRepository> { private LoginFragmentBinding binding; private NavigationManager navigationManager; @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { binding = LoginFragmentBinding.inflate(inflater, container, false); return binding.getRoot(); } @Override public void onAttach(@NonNull Context context) { super.onAttach(context); navigationManager = (NavigationManager) context; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); binding.btnLogin.setOnClickListener( v -> { viewModel.login(new LoginEvent(binding.etUserIdLogin.getText().toString(), Utils.md5Encryption(binding.etPasswordLogin.getText().toString()))); // faker user info }); viewModel.getRemoteResponseMutableLiveData().observe(getViewLifecycleOwner(), it -> { if (it != null && it.status.equals("OK")) { Utils.constructToast(getContext(), "Login success!").show(); Config.userId = it.response.userId; Config.name = it.response.name; navigationManager.navigateTo(new HomeListFragment()); } else { Utils.constructToast(getContext(), it == null ? "Error !" : it.status).show(); } }); viewModel.getErrMsgMutableLiveData().observe(getViewLifecycleOwner(), it -> { Utils.constructToast(getContext(), it).show(); }); } @Override protected LoginViewModel getViewModel() { return new ViewModelProvider(requireActivity(), getFactory()).get(LoginViewModel.class); } @Override protected ViewModelProvider.Factory getFactory() { return new ViewModelProvider.Factory() { @NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { return (T) new LoginViewModel(getRepository()); } }; } @Override protected LoginRepository getRepository() { return new LoginRepository(); } } ``` 补充:我寻思Transformation.distinctUntilChange()可能有用,但是用了好像没效果,不知道是不是这么用 ``` private final LiveData<RemoteResponse<UserInfo>> remoteResponseMutableLiveData = Transformations.distinctUntilChange(Transformations .switchMap(loginEventMutableLiveData, repository::login))); ``` 我听说把viewmodel的东西手动清空也可以,但不知道怎么清空
©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页