前言:以后会更新自己对JetPack 的使用小心得很遇到的坑
LiveData 简介和优缺点
LiveData 是jetpack 组件中最核心的组件之一。可以这么说,如果使用JetPack 没有体验一下LiveData的话,那相当于白扯 ~
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
基于数据的观察,LiveData 是存在于Android 组件的生命周期中的, 所以,LiveData 好处就可以体现出以下几点:
- 优点1:确保界面符合数据状态
LiveData 遵循观察者模式。当底层数据发生变化时,LiveData 会通知 Observer 对象。可以整合代码以在这些 Observer 对象中更新界面。这样一来,无需在每次应用数据发生变化时更新界面,因为观察者会替您完成更新。
例如,当我们从服务器去更新数据后,通过LiveData 的postValue 和 observe 自动去更新UI界面,而不需要去管理和判断组件(Activity、Fragment)或其他组件是否存活。
- 优点2:不会发生内存泄露
由于观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
- 优点3:不会因 Activity 停止而导致崩溃
如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。
- 优点4:数据始终保持最新状态
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
相当于EventBus 的粘性事件,当页面从暂停状态恢复到活跃状态,会立即收到最新的数据!
- 缺点:
LiveData 是一个生命周期感知组件,最好在 View 和 ViewModel 层中使用它,如果在 Repositories 或者 DataSource 中使用会有几个问题
它不支持线程切换,其次不支持背压,也就是在一段时间内发送数据的速度 > 接受数据的速度,LiveData 无法正确的处理这些请求
使用 LiveData 的最大问题是所有数据转换都将在主线程上完成
Ps:可以用Kotlin 中的Flow 替代
在使用的过程中,个人觉得LiveData 唯一的缺点就是封装起来麻烦,内部逻辑不是太清晰。不过不影响他的好处,特别是引用了LiveData 减少了非常多的内存泄露问题!!
LiveData 源码分析
先看LiveData 数据监听机制流程图
先看postValue
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
ArchTaskExecutor 一个任务执行器,可以将任务划分为逻辑组,类似Spring 中的TaskExecutor
注意:这里使用LiveData 时候,有可能会遇到postValue 两次,第一次的时候,值丢失了,那么这里官方已经解释清除了
* If you called this method multiple times before a main thread executed a posted task, only
* the last value would be dispatched.
意思是如果你在主线程执行一个已发布的任务之前多次调用这个方法,只有
最后一个值将被发送。
LiveData 使用和封装
1.引用相关包
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:1.1.0"
// alternatively, just ViewModel
implementation "android.arch.lifecycle:viewmodel:1.1.0"
// alternatively, just LiveData
implementation "android.arch.lifecycle:livedata:1.1.0"
LiveData 使用方式有很多
第一种 :自定义LiveData
假设我们要监听网络变化,就可以这么使用
public class NetworkLiveData extends LiveData<NetworkInfo> {
private final Context mContext;
static NetworkLiveData mNetworkLiveData;
private NetworkReceiver mNetworkReceiver;
private final IntentFilter mIntentFilter;
private static final String TAG = "NetworkLiveData";
public NetworkLiveData(Context context) {
mContext = context.getApplicationContext();
mNetworkReceiver = new NetworkReceiver();
mIntentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
}
public static NetworkLiveData getInstance(Context context) {
if (mNetworkLiveData == null) {
mNetworkLiveData = new NetworkLiveData(context);
}
return mNetworkLiveData;
}
@Override
protected void onActive() {
super.onActive();
Log.d(TAG, "onActive:");
mContext.registerReceiver(mNetworkReceiver, mIntentFilter);
}
@Override
protected void onInactive() {
super.onInactive();
Log.d(TAG, "onInactive: ");
mContext.unregisterReceiver(mNetworkReceiver);
}
private static class NetworkReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager manager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = manager.getActiveNetworkInfo();
getInstance(context).setValue(activeNetwork);
}
}
}
当我们想监听网络变化时候,直接调用,简单快捷
NetworkLiveData.getInstance(this).observe(this, new Observer<NetworkInfo>() {
@Override
public void onChanged(@Nullable NetworkInfo networkInfo) {
Log.d(TAG, "onChanged: networkInfo=" +networkInfo);
}
});
第二种:单一事件发送
假设全局共用一个ViewModel 时,只要注册的观察者在前台就必定会收到这个数据。不利于我们单独处理数据,那么就可以使用google 大神封装的SingleLiveEvent!!!
其实就是用一个boolean 变量去控制发送数据的次数,当发射一次后,就设置为false 阻止第二次发送,达到单一Event 数据传输
/*
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.util.Log;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import java.util.concurrent.atomic.AtomicBoolean;
import io.reactivex.annotations.Nullable;
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
* <p>
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
* <p>
* Note that only one observer is going to be notified of changes.
*/
public class SingleLiveEvent<T> extends MutableLiveData<T> {
private static final String TAG = "SingleLiveEvent";
private final AtomicBoolean mPending = new AtomicBoolean(false);
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull final Observer<? super 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);
}
}
使用方式,加入要展示图片,在ViewModel 中定义 event
/**
* 展示图片
*/
public SingleLiveEvent<Bitmap> showBitmap = new SingleLiveEvent<>();
.......省略,在获取Bitmap 回调中使用
showBitmap.postValue(bitmap);
在Activity 或者Fragment onCreate 方法中 注册observe()观察者,接收数据回调
viewModel.showBitmap.observe(this, bitmap -> {
//绑定Bitmap 到ImageView 中
binding.iv.setImageBitmap(bitmap);
});