前言
本文是对 Jetpack 中的 LiveData 的源码解析,需要对 LiveData 的使用,Lifecycle 有一定的了解。在正式阅读前先来看看下面几个问题:
- 把 LiveData 中的值原封不动地赋值给自己,会调用 Observer 的
onChange()吗? 如:liveData.setValue(liveData.getValue()) - 构造 LiveData 的时候 有参数 和 没参数 的差别是什么?
- LiveData 在什么情况下会丢失值
- Transformations 的
map()和switchMap()怎么实现的你知道吗?
在文章的最后我会对这些问题作出总结。
另外,为了阅读方便,把实现了 LifecycleOwner 的具有生命周期的组件用 Activity 代替。
observe()
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// 如果 Activity 已经被销毁,就直接返回
return;
}
// 把 LifecycleOwner 和 Observer 封装在一起
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// 把 LifecycleOwner 和 Observer 都存入一个 Map 中
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
// 多个不同的 Activity 共用同一个 Observer 的情况,直接抛出异常
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
// 同一个 Activity 和 Observer 且之前已经调用过了这个方法了
return;
}
// Activity 添加生命周期观察者
owner.getLifecycle().addObserver(wrapper);
}
先来看看这个 LifecycleBoundObserver 的实现:
LifecycleBoundObserver
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
......
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
...
}
//父类:
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;
ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
......
}
可以看到 LifecycleBoundObserver 的构造方法就是把传入的 LifecycleOwner 和 Observer 赋值给成员变量,而且实现了 LifecycleEventObserver 这个接口,重写了 onStateChanged()。
所以 LifecycleBoundObserver 这个类身肩重任,它具有 LifecycleOwner 和 Observer 的引用,又是一个 LifecycleEventObserver ,集生命周期组件,数据观察者,生命周期观察者于一身。
mObservers
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
mObservers 的具体类是 SafeIterableMap ,它名字看上去是一个 Map ,却没有实现 Map 接口,它的实际结构是一个双链表,具备的性质:新元素添加到尾部,支持正序遍历,逆序遍历,遍历时可添加元素。感兴趣的读者可以去看看它的源码。
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
如果之前没有存入这个 observer ,就存入成功,返回 null;如果之前存过,就返回之前的 value 。
总结一下 observe() 这个方法:
- 先看 Activity 是否销毁,如果已经销毁了就直接返回
- 把 Activity 和 Observer(数据观察者)封装在一个 LifecycleBoundObserver 实例中,LifecycleBoundObserver 实现了 LifecycleEventObserver 这个接口。
- 尝试把上面说的实例存储起来
- 之前存过,且当前 Activity 和之前存入的 Activity 不同,抛出异常。
- 之前存过,且 Activity 相同,直接返回
- Activity 添加生命周期观察者,这个观察者就是上面的 LifecycleBoundObserver 实例,此部分工作由 Lifecycle 完成。
setValue
private int mVersion;
private volatile Object mData;
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
- 该方法只能在主线程运行,否则会抛出异常
- 版本号 +1
- 缓存设置的值
- 调用
dispatchingValue(),把设置的值发给 Observer
dispatchingValue
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
// 正在把值发送给 Observer,新值要覆盖旧值,旧值的发送就无效了
mDispatchInvalidated = true;
return;
}
// 标记正在发送值
mDispatchingValue = true;
do {
// 标记发送的值有效
mDispatchInvalidated = false;
if (initiator != null) {
//单个 Activity 的生命周期变了,只把数据发给这个 Activity
considerNotify(initiator); //考虑发送值
initiator = null;
} else {
// 调用setValue(), postValue() 的情况,需要把数据发给所有 Activity 。
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue()); //考虑发送值
if (mDispatchInvalidated) {
//当前发送的值无效,直接跳出循环
break;
}
}
}
} while (mDispatchInvalidated); //循环条件为 当前发送的值无效(可以理解为过期)
mDispatchingValue = false; //标记发送结束
}
considerNotify
private void considerNotify(ObserverWrapper observer) {
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);
return;
}
if (observer.mLastVersion >= mVersion) {
// 观察者的版本号 >= LiveData 的版本号,不发送给观察者,直接返回
return;
}
// 到这里的情况:观察者的版本号 < LiveData 的版本号
observer.mLastVersion = mVersion; // 同步版本号
observer.mObserver.onChanged((T) mData); // 调用 Observer 的onChange(),值发送完毕
}
postValue
static final Object NOT_SET = new Object();
volatile Object mPendingData = NOT_SET; // 缓存默认没有值
protected void postValue(T value) {
boolean postTask;
// 加同步锁
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value; // 缓存要设置的值
}
if (!postTask) {
//之前 post 的过程还没完成
return;
}
//通过 Handler 把设置的值任务切换到主线程
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData; // 取出缓存的值
mPendingData = NOT_SET; // 重置缓存
}
setValue((T) newValue); //还是要调用 setValue() 。
}
};
注意:
postValue() 可能会丢失值。postValue() 的可以分为两个步骤:1.缓存要设置的值(mPendingData = value)。2. 通过Handler切换到主线程后调用setValue(),这两个步骤之间是存在延迟的。如果在这个延迟之间,同一个子线程(拿到锁的线程)又调用了 postValue() ,那么第一步还是会执行的(新值覆盖旧值),第二步不会执行。等主线程开始执行这个子线程发送的 Runnable 时,只会把最后一个 post 的值发送给 Observer(数据观察者)。
Activity 生命周期变化
前面说到,在 observe() 里,最后 Activity 添加了生命周期观察者,而这个生命周期观察者就是 LifecycleBoundObserver ,所以当 Activity 的生命周期发生变化时,就会调用它的 onStateChanged() ,来看看这个方法:
onStateChanged
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
......
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
// Activity 已经被销毁,移除Observer
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
}
再来看看 activeStateChanged() ,这个方法是写在父类里的:
private abstract class ObserverWrapper {
boolean mActive;
......
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
//之前的状态和现在的状态 都是活跃状态 或者 都不是活跃的状态 ,直接返回
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
// 到这里就是 活跃变为不活跃 或者 不活跃变为活跃 的情况
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
dispatchingValue(this);
}
}
}
再来看看 changeActiveCounter(),当不活跃变为活跃时,参数为1;当活跃变为不活跃时,参数为-1。
changeActiveCounter
int mActiveCount = 0; //表示活跃观察者的个数
@MainThread
void changeActiveCounter(int change) {
int previousActiveCount = mActiveCount;
mActiveCount += change; // +1 或者 -1
if (mChangingActiveState) {
return;
}
mChangingActiveState = true;
try {
while (previousActiveCount != mActiveCount) {
//之前的活跃观察者个数为0,现在大于0,表示由 没有观察者 变为 有观察者 的情况
boolean needToCallActive = previousActiveCount == 0 && mActiveCount > 0;
//之前的活跃观察者个数大于0,现在等于0,表示由 有观察者 变为 没有观察者 的情况
boolean needToCallInactive = previousActiveCount > 0 && mActiveCount == 0;
previousActiveCount = mActiveCount;
if (needToCallActive) {
//没有观察者 变为 有观察者 的逻辑,由 LiveData 的子类重写这个方法
onActive();
} else if (needToCallInactive) {
//有观察者 变为 无观察者 的逻辑,由 LiveData 的子类重写这个方法
onInactive();
}
}
} finally {
mChangingActiveState = false;
}
}
activeStateChanged() 中,如果由不活跃变为活跃,就会调用 dispatchingValue(),这个方法上面也讲过了。只不过上面讲的是 setValue() 中调用 : dispatchingValue(null); ,而这里是dispatchingValue(this);,二者的差别在参数上。
- 逻辑上:调用
setValue()应该把值发送给所有观察者,而 Activity 由不活跃变为活跃,只需要把值发给这个 Activity 即可。 - 代码上:参数为
null表示要发送给所有观察者;而参数为 this ,也就是LifecycleBoundObserver,其内部有数据观察者的引用,可以做到单独发送。
observeForever
observeForver() 顾名思义,就是一直观察,不考虑生命周期。那么就可以理解为:只要调用了这个方法,那么就认为 Observer(数据观察者)一直是活跃的,直到调用 removeObserver() 才变为不活跃。
@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
assertMainThread("observeForever");
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing instanceof LiveData.LifecycleBoundObserver) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
wrapper.activeStateChanged(true); //一开始就是由不活跃变为活跃
}
可以看到这个方法和 observe() 很像,先把 Observer 封装起来,再存到 mObservers 里,然后调用wrapper.activeStateChanged(true),表示由不活跃变为活跃,尝试发送值(有初始值的情况)。所以 mObservers 里的数据观察者可能有两种:考虑生命周期的,不考虑生命周期的。
内存泄漏
- 内存泄漏就是长生命周期对象 持有 短生命周期对象,阻碍了其回收。
- 在 LiveData 里,我们主要考虑的就是 Activity(具有生命周期的组件),Observer(数据观察者),LiveData。
- 先来确定生命周期的长短:我们一般是把 LiveData 放在 ViewModel 中的,而 ViewModel 的生命周期是比 Activity 长的,且 Observer 一般写在 Activity 里,所以生命周期如下:LiveData > Activity = Observer。
- 所以我们只考虑 LiveData 是否会泄漏 Activity 和 Observer 这种情况就好了。
- 在
LifecycleBoundObserver的onStateChanged()里,如果 Activity 已经销毁,就调用removeObserver()。
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
......
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
}
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer) {
assertMainThread("removeObserver");
//移除操作
ObserverWrapper removed = mObservers.remove(observer);
if (removed == null) {
return;
}
removed.detachObserver(); // Activity 移除生命周期观察者
removed.activeStateChanged(false);
}
在mObservers里,Activity 和 Observer 是封装好后存放的,所以移除时这两个是一起移除的。
MediatorLiveData
使用方法
public class MyViewModel extends ViewModel {
MutableLiveData<String> unit = new MutableLiveData<>("克");
MutableLiveData<String> num = new MutableLiveData<>("1");
MediatorLiveData<String> weight = new MediatorLiveData<>("1克");
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.weight.addSource(viewModel.unit, new Observer<String>() {
@Override
public void onChanged(String s) {
//TODO 1
}
});
viewModel.weight.addSource(viewModel.num, new Observer<String>() {
@Override
public void onChanged(String integer) {
//TODO 2
}
});
viewModel.weight.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
//TODO 3
}
});
}
}
注意:
addSource()之后要调用observe()才能真正观察,否则没有效果- 在上面的注释 TODO 1 和 TODO 2 处,不会自动调用 weight 的 Observer (就是 TODO 3 处),需要手动调用
viewModel.weight.setValue()或者viewModel.weight.postValue()。
流程分析
private SafeIterableMap<LiveData<?>, Source<?>> mSources = new SafeIterableMap<>();
@MainThread
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
//封装 LiveData 和 Observer。
Source<S> e = new Source<>(source, onChanged);
//存
Source<?> existing = mSources.putIfAbsent(source, e);
if (existing != null && existing.mObserver != onChanged) {
//同一个 子LiveData 不能有不同的 Observer(数据观察者)
throw new IllegalArgumentException(
"This source was already added with the different observer");
}
if (existing != null) {
// 已经添加过
return;
}
if (hasActiveObservers()) {
//如果活跃观察者的个数大于0的情况
e.plug();
}
}
接着看 Source 这个类:
private static class Source<V> implements Observer<V> {
final LiveData<V> mLiveData;
final Observer<? super V> mObserver;
int mVersion = START_VERSION;
Source(LiveData<V> liveData, final Observer<? super V> observer) {
mLiveData = liveData;
mObserver = observer;
}
void plug() {
//一直观察
mLiveData.observeForever(this);
}
void unplug() {
//移除观察者
mLiveData.removeObserver(this);
}
@Override
public void onChanged(@Nullable V v) {
if (mVersion != mLiveData.getVersion()) {
mVersion = mLiveData.getVersion();
//把值发给真正的数据观察者
mObserver.onChanged(v);
}
}
}
所以,使用 MediatorLiveData 的整个流程:
- 调用
addSource(),主要是把 LiveData 和 Observer 封装后,保存。 - 再调用
observe()(MediatorLiveData 继承 MutableLiveData), 其中为 Activity 添加生命周期观察者。 - 当 Activity 生命周期变化时,会调用
LifecycleBoundObserver的onStateChanged(),然后调用changeActiveCounter(),如果是由不活跃状态变为活跃状态,就会调用onActive()。 - 而
MediatorLiveData重写了onActive()。@CallSuper @Override protected void onActive() { for (Map.Entry<LiveData<?>, Source<?>> source : mSources) { source.getValue().plug(); } } onActive()就会调用所有 子LiveData 的observeForver(),到这里才开始正式观察 子LiveData。- 注意这里的写法
mLiveData.observeForever(this), 并没有直接把mObserver作为参数直接传入,而是传了个 this , Source 实现了 Observer 这个接口,重写的onChange()还是调用了mObserver的onChange()。 - 如果调用了
observer()后又调用addSource(),子LiveData的Observer就会直接开始观察。
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
......
if (hasActiveObservers()) { //此时这里返回结果为 true 。
e.plug();
}
}
Transformations.map()
使用场景
public class MyViewModel extends ViewModel {
MutableLiveData<User> userInfo = new MutableLiveData<>();
LiveData<String> name = Transformations.map(userInfo, user ->
// 把姓和名拼接起来获得全名
user.getFirstName()+" "+user.getLastName()
);
}
如上面的代码,我们有一个 User 类存储用户的各种信息,而我现在的一个需求是只需要用户的全名,这时就可以使用 Transformations.map() 。当我们切换到另外一个用户(LiveDaba不能感知对象内属性的变化),这个 name 也会跟着一起变化。
流程分析
@MainThread
public static <X, Y> LiveData<Y> map(
@NonNull LiveData<X> source,
@NonNull final Function<X, Y> mapFunction) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
@Override
public void onChanged(@Nullable X x) {
// 1
result.setValue(mapFunction.apply(x));
}
});
return result;
}
- 参数 source 为 源LiveData,参数 mapFunction 为转换函数。
- 构造一个 MediatorLiveData ,然后调用它的
addSource(),最后返回这个实例。 - 代码就几行,下面来分析
source(源LiveData)的值变化是如何引发result的值也一起变化的:- 我们拿到
map()返回的这个 MediatorLiveData ,也就是result,调用它的observe(),由上面对 MediatorLiveData 的分析知道,当 Activity 从不活跃状态到活跃状态时,就会调用source的observeForever()。 - 接着当我们调用
source的setValue()或者postValue()时,就会调用这个source对应的Observer的onChanged(),也就是注释 1 的地方。在这里先用转换函数获得要设置的值,再调用result的setValue(),最后就会调用result对应的 Observer 的onChanged(),值发送完毕。 - 整个思路了就是用
MediatorLivaData作为 源LiveData(source) 和Observer(Activity里的数据观察者)的中介。使用MediatorLiveData感知 源LiveData 的变化,MedaitorLiveData再把感知到的值发送给Observer。
- 我们拿到
Transformations.switchMap()
使用场景
public class MyViewModel extends ViewModel {
MutableLiveData<String> userIdLiveData = new MutableLiveData<>();
public LiveData<User> user = Transformations.switchMap(userIdLiveData, new Function<String, LiveData<User>>() {
@Override
public LiveData<User> apply(String userId) {
//TODO 根据 userId ,从网络或者数据库中全部的用户信息,封装成一个User,并放在一个LiveData内返回。
}
});
public void getUser(String userId){
userIdLiveData.setValue(userId);
}
}
我们在调用了 user 的 observer() 之后,如果我们想切换用户,只需要调用 getUser() ,user 对应的 Observer 的 onChanged() 就会被调用,我们就能获得新用户的信息。
流程分析
@MainThread
public static <X, Y> LiveData<Y> switchMap(
@NonNull LiveData<X> source,
@NonNull final Function<X, LiveData<Y>> switchMapFunction) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
LiveData<Y> mSource;
@Override
public void onChanged(@Nullable X x) {
// 1
LiveData<Y> newLiveData = switchMapFunction.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) {
// 2
result.setValue(y);
}
});
}
}
});
return result;
}
- 可以看到
switchMap()和map()的流程差不多,不同的地方就是在 Observer 的onChanged()实现上。 onChanged()的流程:- 跟
map()一样,当source的值变化最终会调用source对应的 Observer 的onChanged(),也就是注释 1 处。 - 调用转换函数后会返回一个新的 LiveData ,也就是代码中的
newLiveData。 mSource就是上一次保存的值,如果mSource不为null的话,那就将它从 MediatorLiveData 中移除。- 将
newLiveData缓存到mSource - 将
mSource添加到 MediatorLiveData 中。
- 跟
- 可以看到,
Transformations.switchMap()的返回是一个 MediatorLiveData ,所以虽然我们写的转换函数每次调用返回的都是一个新的 LiveData ,但是我们都会将 MediatorLiveData 里原先的 LiveData 移除,再添加这个新的 LiveData,而且因为 Activity 已经处于活跃状态,就会调用它自己的observerForever(),又因为newLiveData是有初始值的,所以它对应的 Observer 的onChanged()也会被调用,也就是注释 2 处。这里调用result的setValue(),最终result对应的 Observer 的onChanged()也会被调用。 - 整个流程比较绕,建议读者自行去看源码分析一下。
switchMap()的巧妙之处在于用一个MediatorLiveData“以不变应万变”,转换函数返回的新 LiveData 替换之前的值,并且在onChanged()中调用MediatorLiveData的setValue()来通知值的变化。
问题
-
把 LiveData 中的值原封不动地赋值给自己,会调用 Observer 的
onChanged()吗? 如:liveData.setValue(liveData.getValue())答:会。在 LiveData 的
considerNotify()中,只看 Activity 是否活跃 和 版本号。而每次调用setValue()都会让版本号+1,这是LiveData的版本大于Observer的版本,所以会调用 Observer 的onChanged()。 -
构造 LiveData 的时候 有参数 和 没参数 的差别是什么?
static final int START_VERSION = -1;
public LiveData(T value) {
mData = value;
mVersion = START_VERSION + 1;
}
public LiveData() {
mData = NOT_SET;
mVersion = START_VERSION;
}
可以看到不同就在于版本号上,有初始值的版本号为0,没初始值的版本号为-1,导致的结果是有初始值时,调用 observe() 后就会调用一次 Observer 的 onChanged() 。
- LiveData 在什么情况下会丢失值?
答: postValue() 的可以分为两个步骤:1.缓存要设置的值(mPendingData = value)。2. 通过Handler切换到主线程后调用setValue(),这两个步骤之间是存在延迟的。如果在这个延迟之间,同一个子线程(拿到锁的线程)又调用了 postValue() ,那么第一步还是会执行的(新值覆盖旧值),第二步不会执行。等主线程开始执行这个子线程发送的 Runnable 时,只会把最后一个 post 的值发送给 Observer(数据观察者)。
- Transformations 的
map()和switchMap()的实现方法你知道吗?
答:都是使用一个 MediatorLiveData 来作为 源LiveData 和 Observer 的中介。让 MediatorLiveData 感知 源LiveData 的变化,然后 MediatorLiveData 把变化的值发送给 Observer 。二者的差别在于 源LiveData 对应的 Observer 的 onChanged() 的实现上,map()不需要切换 子LiveData ,而 switchMap() 需要切换 子LiveData 。
至此,本文对 LiveData 的源码分析全部结束,如果本文对你有帮助还希望点赞支持!你的点赞对我意义重大!