关于LiveData全面详解(附事件总线)

前语:缤纷颜色闪出的美丽 是因它没有 分隔每种颜色

前语

MVVM 架构形式中,ViewModel 是不会持有宿主的信息,事务逻辑在 ViewModels 层中完结,而不是在 Activities 或 Fragments 中。LiveData 在里边担任数据驱动的效果:

关于LiveData全面详解(附事件总线)

以往咱们运用 Handler,EventBus,RxjavaBus 进行音讯通信,LiveData 也是一个种调查者形式,效果跟 RxJava 类似,是调查数据的类,相比 RxJava,一般配合 Jetpack 组件配合运用。它能够在 Activity、Fragment 和 Service 之中正确的处理生命周期。

一、什么是LiveData

1.介绍

LiveData 组件是 Jetpack 新推出的根据调查者的音讯订阅/分发组件,具有宿主(Activity/Fragment)生命周期感知才干。这种感知才干可保证 LiveData 仅分发音讯给与活泼状况的调查者,即只要处于活泼状况的调查者才干收到音讯。

LiveData 的音讯分发机制,是以往 Handler,EventBus,RxjavaBus 无法比拟的,它们不会顾及当时页面是否可见,一股脑的有音讯就转发。导致即便运用在后台,页面不可见,还在做一些无用的绘制,计算(微信的音讯列表是在可见状况下才会更新列表最新信息的)将有限的资源让给可见的页面运用。

活泼状况:Observer地点宿主处于STARTED,RESUMED状况。

本文需求 Lifecycle 的相关常识作为基础,假如有不了解的能够先看看《Android架构魂灵组件Lifecycle的生命周期机制详解》

根据生命周期,其实便是在当时宿主 LifecycleOnwer 注册一个 Observer,那么宿主每次生命周期的改变,都会回调给调查者的 onStateChange() 办法,即便是刚刚注册的调查者宿主也会回调 onStateChange() 办法,会有一个状况同步的进程,LiveData 也是利用这个才干,奇妙完结了当宿主毁掉的时分,自动移除注册进来的 Observer,从而避免了手动移除的费事。更不会造成内存走漏,这个也是它的中心思维。

2.LiveData的特点

运用 LiveData 具有以下几点优势:

  • 保证界面契合数据状况:LiveData 遵循调查者形式。当底层数据产生改变时,LiveData 会告诉Observer 目标。
  • 不会产生内存走漏:调查者会绑定到Lifecycle 目标,并在其相关的生命周期遭到毁掉后进行自我清理。
  • 不会因 Activity 停止而导致溃散:假如调查者的生命周期处于非活泼状况(如回来堆栈中的 Activity),它便不会接纳任何 LiveData 事情。
  • 不再需求手动处理生命周期:界面组件仅仅调查相关数据,不会停止或康复调查。LiveData 将自动办理一切这些操作,由于它在调查时能够感知相关的生命周期状况改变。
  • 数据一直保持最新状况:假如生命周期变为非活泼状况,它会在再次变为活泼状况时接纳最新的数据。
  • 恰当的装备更改:假如由于装备更改(如设备旋转)而从头创立了 Activity 或 Fragment,它会当即接纳最新的可用数据。
  • 共享资源:您能够运用单例形式扩展LiveData 目标以封装体系服务,以便在运用中共享它们。LiveData目标连接到体系服务一次,然后需求相应资源的任何调查者只需调查LiveData目标。

LiveData不足之处:

  • 粘性事情不支撑撤销(后边注册的调查者也能接纳数据,无法反注册,但有办法解决)。

3.LiveData中心办法

办法名 效果
observe(LifecycleOwner owner, Observer observer) 注册和宿主生命周期相关的调查者, owner当时生命周期的宿主,当宿主毁掉了observer能自动解除注册
observeForever(Observer observer) 注册调查者,不会反注册,需自行保护,没有owner无法办理宿主生命周期
setValue(T value) 发送数据,没有活泼的调查者时不分发,只能在主线程
postValue(T value) setValue一样,可是不受线程约束,内部也是经过handelr.post到主线程,最终仍是经过setValue来分发的
onActive() 当且仅当有一个活泼的调查者时会触发
onInactive() 不存在活泼的调查者时会触发

二、LiveData的几种用法

1.MutableLiveData

在运用 LiveData 做音讯分发的时分,需求运用这个子类,设计的原因是考虑到单一开闭原则,只要拿到 MutableLiveData 才能够发送音讯,LiveData 只能接纳音讯,避免拿到 LiveData 既能发送音讯又能接纳音讯的紊乱运用

public class MutableLiveData<T> extends LiveData<T> {
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }
    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

MutableLiveData 仅仅是把上面两个办法从父类的 protect 改为 public 罢了,由于在 LiveData 中无法调用 postValue()setValue()。所以咱们在运用 LiveData 做音讯分发的时分,咱们需求运用它的子类 MutableLiveData。

fun mediatorLiveData() {
    val liveData1 = MutableLiveData<String>()
    val liveData2 = MutableLiveData<String>()
    //在创立一个聚合类MediatorLiveData
    val mediatorLiveData = MediatorLiveData<String>()
    //分别把LiveData合并到mediatorLiveData中
    mediatorLiveData.addSource(liveData1, Observer { data ->
        mediatorLiveData.value = data
    })
    mediatorLiveData.addSource(liveData2, Observer { data ->
        mediatorLiveData.value = data
    })
    //数据监听,一旦liveData1或许LiveData2发送了数据,observer便能调查到,以便一致处理更新
    mediatorLiveData.observe(this, Observer { data ->
        LogUtil.e("mediatorLiveData:$data")
    })
    // 模仿发送数据
    liveData1.postValue("liveData1 苏火火苏火火")
    liveData2.postValue("liveData2 苏火火苏火火")
}

打印数据如下:

/com.sum.tea E/LogUtil: mediatorLiveData: liveData1 苏火火苏火火
/com.sum.tea E/LogUtil: mediatorLiveData: liveData2 苏火火苏火火

3.Transformations.map操作符

Transformations.map 能够对 LiveData 的数据在分发给调查者之前进行转化,而且回来一个新的 LiveData 目标。

fun transformationsMap() {
    val mapLiveData = MutableLiveData<Int>()
    //数据转化
    val transformLiveData: LiveData<Int> = Transformations.map(mapLiveData) { input ->
        input * 2
    }
    //运用转化后生成的transformLiveData去调查数据
    transformLiveData.observe(this) { output ->
        LogUtil.e("transformationsMap 数据转化改变:$output")
    }
    //运用原始的LiveData发送数据
    mapLiveData.value = 10
}

打印数据如下:

/com.sum.tea E/LogUtil: transformationsMap 数据转化改变:20

三、LiveData完结原理

1.粘性事情分发流程

先从 LiveData 注册调查者看起:

#LiveData.java
@MainThread
public void observe(LifecycleOwner owner, Observer<? super T> observer) {
    // 假如宿主是DESTROYED状况则直接退出
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        return;
    }
    // 把observer包装一个具有生命周期边界的调查者
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    // 存储到mObservers调集
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing != null) {
        return;
    }
    // 注册到宿主的生命周期里边
    owner.getLifecycle().addObserver(wrapper);
}
  1. 把 observer 包装成了 LifecycleBoundObserver,它是一个具有生命周期边界的调查者,它是 LifecycleEventObserver 的子类,
  2. 接着把 LifecycleBoundObserver 存储到 mObservers 调集傍边。
  3. 最终把 LifecycleBoundObserver 注册到宿主的生命周期里边。

所以 wrapper 就能接纳到宿主生命周期改变的事情,当第一次注册进去的时分也会触发状况的同步,也能接纳到完整的生命周期事情。

由于后边还要做数据的分发,订阅音讯便是把这个 Observer 包装成 LifecycleBoundObserver,然后存储到 mObservers 调集傍边,有音讯的时分就遍历这个调集去分发。

// 具有生命周期边界才干的Observer
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    @Override
    boolean shouldBeActive() {
        // 判别调查者是否处于活泼的状况
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }
    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        // 判别当时宿主的状况是否为destory
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            // 自动进行反注册,把Observer移除去
            removeObserver(mObserver);
            return;
        }
        // 状况改变
        activeStateChanged(shouldBeActive());
    }
}

宿主生命周期每一次事情的告诉都会回调到 LifecycleEventObserver 的 onStateChanged(),先判别当时宿主的状况是否为 DESTORYED,假如是则自动进行反注册,把 Observer 移除去。从而自动避免内存走漏的问题。

假如不是 DESTORYED,那就阐明宿主的状况产生了别的改变,触发 activeStateChanged(shouldBeActive()) 这个办法,会先判别调查者是否处于活泼的状况,只要处于活泼状况的调查者才干接纳到数据:

// 注册调查者,不会反注册,需自行保护,没有owner无法办理宿主生命周期
public void observeForever(Observer<? super T> observer) {
    //把`observer`包装成一个`AlwaysActiveObserver`目标
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
    // 将Observer存储到mObservers调集中
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    // 设置为tru则不管宿主是否处于可见状况,一向接纳数据
    wrapper.activeStateChanged(true);
}
  • observeForever():它会把 observer 包装成一个 AlwaysActiveObserver 目标, shouldBeActive() 永远为 true,不管你的宿主是否处于可见状况,这就意味着它能够一向接纳数据。

除了 observeForever() 这种状况外,调查者是否处于活泼状况其实就等于宿主是否处于活泼的状况。

假如场景是在后台要处理一些事情,能够运用 observeForever() 注册调查者,可是需求在宿主被毁掉的时分撤销注册,或许运用传统的 callback 形式。

void activeStateChanged(boolean newActive) {
    // 假如状况一致,则退出
    if (newActive == mActive) {
        return;
    }
    // 当即设置状况,就不会分发给非活泼状况
    mActive = newActive;
    // 非活泼状况,mActiveCount表明活泼状况数量
    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);
    }
}

首要判别 mActiveCount == 0,假如等于0阐明里边的调查者没有一个处于活泼的状况。在注册第一个调查者的时分,活泼调查者的数量肯定是等于0的,当注册了第一个调查者之后,它的状况就会产生改变,变成 mActive,此时就会触发 onActive() 办法,假如没有任何一个调查者就会触发 onInactive() 办法,假如 mActive == true,则阐明当时调查者处于活泼状况,它是能够接纳数据的。

// 分发数据
void dispatchingValue(ObserverWrapper initiator) {
    // 
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            // 1.把数据分发给自己
            considerNotify(initiator);
            initiator = null;
        } else {
            // 2.有新的数据,把mObservers调集傍边一切的调查者遍历分发数据
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}
// 设置数据
protected void setValue(T value) {
    mVersion++;
    mData = value;
    dispatchingValue(null);
}
  1. 假如 initiator 为空,本次分发数据就把数据分发给自己 considerNotify(initiator)
  2. 假如 initiator 不为空,一般是从 setVlue() 过来的,这时分就阐明有了新的数据,就会把 mObservers 调集傍边一切的调查者遍历分发数据。

无论是新注册的调查者仍是 setVlue() 触发的音讯分发,都会调用 considerNotify() 办法

// 预备告诉更新,宿主在康复活泼状况时也会履行到这儿
private void considerNotify(ObserverWrapper observer) {
    // observer非活泼状况直接退出
    if (!observer.mActive) {
        return;
    }
    // 也许它改变了状况,但咱们还没有得到事情
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    // observer的版别大于LiveData版别不会发送数据,避免多次重复发送 
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    // 同步调查者的Version数据
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);
}

这儿是数据最终发送的当地,先判别调查者是否处于活泼状况,假如说调查者不活泼,就会 return,当宿主康复活泼状况的时分就会触发 onStateChanged(),最终也会到这儿。

重点来了,LiveData 的 Version 字段和 Observer 的 Vierson 在刚开端创立的时分都是-1,假如 LiveData 现已发送数据了,它的 Version 字段就会加1,假如这个时分新注册了一个 Observer,那么在触发音讯分发的时分,这两个字段就不持平,所以 Observer 就能接纳到之前发送的音讯,在第一次注册的数据的时分(先发数据,后注册的 Observer 也会收到数据)

这便是粘性事情,意图是为了避免数据多次重复发送,由于每次生命周期的改变都会走到这儿。最终调用 observer.mObserver.onChanged() 回调数据。

关于LiveData全面详解(附事件总线)
这便是一个新的注册的 Observer 是如何接纳到之前发送的数据的流程。

2.一般音讯分发流程

一般音讯分发流程即调用 postValue()setValue() 才会触发音讯的分发。

postValue() 发送数据的流程:

#LiveData.java
// 不约束线程,主线程,子线程都能够调用
protected void postValue(T value) {
    boolean postTask;
    // 加锁
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    // 发送到主线程履行
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

由于需求 Handler 把这个音讯 post 到主线程里边,所以需求把传递进来的 value 保存 mPendingData,当这条音讯被履行的时分,就会触发 mPostValueRunnable,里边实际也是调用 setValue(),把上面存储的 mPendingData 传递进去。

private final Runnable mPostValueRunnable = new Runnable() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        // 履行setValue()
        setValue((T) newValue);
    }
};

无论是从哪里发送的数据,接纳的当地一直都会发送在主线程,每发送一条音讯 mVersion 增加了1,在音讯派发的时分就会和 Observer 的 version 进行对比,避免音讯重复发送的问题

就会调用 dispatchingValue(null) 传递了 null,根据上面的剖析,假如参数为 null,就会遍历 mObserver 调集中的调查者去逐个判别是否能把数据分发给他们。

@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    // 分发数据
    dispatchingValue(null);
}

setValue() 里边调用 dispatchingValue(null) 进行分发数据,就又回到了上面的流程。

关于LiveData全面详解(附事件总线)

四、总结

1.粘性事情分发流程

  1. 经过 observe(owner,observer) 向 LiveData 注册调查者,而且把 observer 包装成一个 LifecycleBoundObserver,它是一个具有生命周期边界的调查者,由于这个调查者只要当宿主处于 STARTED 或许 RESUMED 状况的它才会接纳数据,其他时分它是不会接纳数据的。

  2. 把包装好的 Observer 注册到 Lifecycle 傍边,handlerLifecycleEvent(event) 利用 Lifecycle 才干,它能感知宿主生命周期才干的要害当地。注册时和宿主每次生命周期改变都会回调 onStateChanged() 办法,刚进去的时分会触发办法的同步。

  3. 会判别这个事情宿主是否被毁掉了,从而自动地把 Observer 从 LiveData 中移除去,流程结束。假如不是 DESTORY,阐明宿主当时的状况产生了改变,它会触发 activeStateChanged(boolean newActive) 办法,它会判别当时 Observer 是否处于活泼的状况,假如宿主的状况为 STARTED,RESUMED 则会分发最新数据到每个调查者。

  4. 从而调用 dispatchingValue(ObserverWrapper) 分发数据,假如 ObserverWrapper 为空则分发数据给 liveData 中存储的一切调查者,假如不为空,则分发数据给该 Observer。

  5. considerNotify(ObserverWrapper) 中先判别调查者地点的宿主不活泼,则不分发;接着假如 observer 的 mLastVersion 大于或等于 LiveData 的 mVersion 则不分发,避免重复发送数据;最终经过 observer.mObserver.onChanged((T) mData) 分发数据,同步 mVersion 数据。

那么 LiveData 先发送数据,后注册的 Observer 能接纳到数据吗? 答案是能够的。

2.一般音讯发送流程

  1. postValue() 发送一条数据,它能够在任意线程运用的,里边实际运用了 Handler.post 先把这个事情发送到主线程,然后在调用 setValue() 发送数据;

  2. setValue() 代表着 LiveData 发送数据,每发送一次 mVersion++,别的 LifecycleBoundObserver 中也有一个,它代表这个 Observer 接纳了几次数据,在分发数据的时分,这两个 version 会进行比对,避免数据重复发送;

  3. setValue() 里边也会触发 dispatchingValue(ObserverWrapper),ObserverWrapper 为 null,dispatchingValue() 它会遍历 Observer 调集里边一切调查者,然后逐个调用 considerNotify(ObserverWrapper) 去做音讯的分发。

五、运用LiveData打造音讯总线

根据 LiveData 打造一款不会内存走漏不用反注册的音讯总线,且支撑粘性事情。

// 事情总线
private fun liveDataBus() {
    LiveDataBus.with<String?>("eventName").observeSticky(this, { data ->
        mBinding.tvUserInfo.text = data
        LogUtil.e("事情总线 数据改变:$data")
    }, true)
}
  • 问题:LiveData 默认是支撑粘性事情的,而且无法撤销。
private void considerNotify(ObserverWrapper observer) {
    //调查者没有处于活泼状况,则不分发
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    //调查者接受的音讯的次数>=liveData发送音讯的次数,不分发
    //假如之前现已发送过数据,新注册的Observer也能接受到最终一条数据
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    //根本原因在于ObserverWrapper的version字段在创立时=-1,没有自动和LiveData字段的mVersion字段对齐
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);
}

满意上面两个条件才会触发 observer.mObserver.onChanged() 事情的分发:

  1. 首要判别 observer.shouldBeActive() 是否处于活泼状况,假如 observer 是根据 observer() 注册的,这个调查者是否处于活泼就等于它的宿主是否处于活泼状况,假如是用 observerForever() 注册的,它就一向处于活泼状况。这儿不需求操控它,能很好完结与宿主生命周期相相关。

  2. 要害在于 observer.mLastVersion >= mVersion,mLastVersion 和 mVersion 默认值都是为-1,在首次注册流程分发的时分,假如 LiveData 之前发送过数据,mVersion 就会为1,而新注册进来的 Observer 的 mLastVersion 仍是-1,上就会往下履行音讯分发。这样就导致了新注册的 Observer 也能够接纳到之前发送音讯的最终一条数据。

  3. 可是假如咱们在新注册 Observer 的时分自动和 LiveData 的 mVersion 对齐,保持一致,那么就不会持续分发音讯,这是根本原因。在于 Observer 的 mLastVersion 的值,可是 LifecycleBoundObserver 这个类咱们是拜访不到的,咱们也不能直接操控 mLastVersion 的值,可是咱们能够从头包装 Observer。

//包装StickyObserver,有新的音讯会回调onChanged办法,从这儿判别是否要分发这条音讯
//这仅仅完结StickyObserver的包装,用于操控事情的分发,可是事情的发送仍是依托LiveData来完结的
internal class StickyObserver<T>(
    liveData: StickyLiveData<T>,
    observer: Observer<T>,
    sticky: Boolean
) : Observer<T> {
    private val mLiveData: StickyLiveData<T>
    private val mObserver: Observer<T>
    //是否敞开粘性事情,为false则只能接受到注册之后发送的音讯,假如需求接受粘性事情则传true
    private val mSticky: Boolean
    //符号该Observer现已接纳几次数据了,过滤老数据避免重复接纳
    private var mLastVersion = 0
    init {
        //比如先运用StickLiveData发送了一条数据,StickLiveData#version=1
        //那么当创立WrapperObserver注册进去的时分,需求把它的version和StickLiveData的version保持一致
        mLastVersion = liveData.mVersion
        mLiveData = liveData
        mSticky = sticky
        mObserver = observer
    }
    override fun onChanged(t: T) {
        if (mLastVersion >= mLiveData.mVersion) { //假如持平则阐明没有更新的数据要发送
            //可是假如当时Observer是联系粘性事情的,则分发给他
            if (mSticky && mLiveData.mStickyData != null) {
                mObserver.onChanged(mLiveData.mStickyData)
            }
            return
        }
        mLastVersion = mLiveData.mVersion
        mObserver.onChanged(t)
    }
}

扩展 LiveData,支撑粘性事情的订阅,分发的 StickyLiveData:

//扩展LiveData,支撑粘性事情的订阅,分发的StickyLiveData
class StickyLiveData<T>(private val mEventName: String) : LiveData<T>() {
    var mStickyData: T? = null
    // 版别符号
    var mVersion = 0
    // 事情存储调集
    var mHashMap: ConcurrentHashMap<String, StickyLiveData<T>>? = null
     //调用mVersion++
     //注册一个Observer的时分,把它包装一下,意图是为了让Observer的version和LiveData的version对齐
     //可是LiveData的version字段拿不到,所以需求办理version,在对齐的时分
    override fun setValue(value: T?) {
        mVersion++
        super.setValue(value)
    }
    override fun postValue(value: T?) {
        mVersion++
        super.postValue(value)
    }
    //发送粘性事情,只能在主线程发送数据
    fun setStickData(stickyData: T) {
        mStickyData = stickyData
        value = stickyData
    }
    //发送粘性事情,不受线程约束
    fun postStickData(stickyData: T) {
        mStickyData = stickyData
        postValue(stickyData)
    }
    override fun observe(owner: LifecycleOwner, observer: Observer<in T?>) {
        observeSticky(owner, observer, false)
    }
     //暴露办法,是否关心之前发送的数据,再往宿主上面添加一个addObserver监听生命周期事情,假如是DESTORYED则自动移除LiveData
     //sticky  是否为粘性事情,sticky=true,假如之前存在现已发送数据,那么Observer就会收到之前的粘性事情音讯
    fun observeSticky(owner: LifecycleOwner, observer: Observer<in T>, sticky: Boolean) {
        owner.lifecycle.addObserver(LifecycleEventObserver { source, event ->
            if (event == Lifecycle.Event.ON_DESTROY) {
                mHashMap?.remove(mEventName)
            }
        })
        super.observe(owner, StickyObserver(this, observer as Observer<T>, sticky))
    }
}

这个还有别的一种处理方式便是经过反射,获取 LiveData 中的 mVersion 字段,来操控粘性事情的分发。

一个大型的 Android 项目架构最佳实践,根据Jetpack组件 + MVVM架构形式,加入 组件化模块化协程Flow短视频。项目地址:github.com/suming77/Su…

点关注,不迷路


好了各位,以上便是这篇文章的全部内容了,很感谢您阅读这篇文章。我是suming,感谢各位的支撑和认可,您的点赞便是我创造的最大动力。山水有相逢,咱们下篇文章见!

本人水平有限,文章难免会有错误,请批评指正,不胜感激 !

参阅链接:

  • LiveData官网

希望咱们能成为朋友,在 Github、 上一同分享常识,一同共勉!Keep Moving!