[译] Architecture Components 之 LiveData

【目录】

1. Architecture Components 之 Guide to App Architecture

2. Architecture Components 之 Adding Components to your Project

3. Architecture Components 之 Handling Lifecycles

4. Architecture Components 之 LiveData

5. Architecture Components 之 ViewModel

6. Architecture Components 之 Room Persistence Library

示例代码连接


LiveData

LiveData 是一个数据持有者类,它持有一个值并容许观察该值。不一样于普通的可观察者,LiveData 遵照应用程序组件的生命周期,以便 Observer 能够指定一个其应该遵照的 Lifecyclejavascript

注:在 Android 项目中导入 LiveData,请参阅添加组件到项目中html

若是 ObserverLifecycle 处于 STARTED 或 RESUMED 状态,LiveData 会认为 Observer 处于活动状态。java

public class LocationLiveData extends LiveData<Location> {
    private LocationManager locationManager;

    private SimpleLocationListener listener = new SimpleLocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            setValue(location);
        }
    };

    public LocationLiveData(Context context) {
        locationManager = (LocationManager) context.getSystemService(
                Context.LOCATION_SERVICE);
    }

    @Override
    protected void onActive() {
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
    }

    @Override
    protected void onInactive() {
        locationManager.removeUpdates(listener);
    }
}复制代码

Location 监听的实现有 3 个重要部分:android

onActive()git

LiveData 有一个处于活动状态的观察者时该方法被调用,这意味着须要开始从设备观察位置更新。github

onInactive()app

LiveData 没有任何处于活动状态的观察者时该方法被调用。因为没有观察者在监听,因此没有理由保持与 LocationManager 的链接。这是很是重要的,由于保持链接会显著消耗电量而且没有任何好处。ide

setValue()post

调用该方法更新 LiveData 实例的值,并将此变动通知给处于活动状态的观察者。ui

能够像下面这样使用新的 LocationLiveData:

public class MyFragment extends LifecycleFragment {
    public void onActivityCreated (Bundle savedInstanceState) {
        LiveData<Location> myLocationListener = ...;
        Util.checkUserStatus(result -> {
            if (result) {
                myLocationListener.addObserver(this, location -> {
                    // 更新 UI
                });
            }
        });
    }
}复制代码

请注意,addObserver() 方法将 LifecycleOwner 做为第一个参数传递。这样作表示该观察者应该绑定到 Lifecycle,意思是:

  • 若是 Lifecycle 不处于活动状态(STARTED 或 RESUMED),即便该值发生变化也不会调用观察者。

  • 若是 Lifecycle 被销毁,那么自动移除观察者。

LiveData 是生命周期感知的事实给咱们提供了一个新的可能:能够在多个 activity,fragment 等之间共享它。为了保持实例简单,能够将其做为单例,以下所示:

public class LocationLiveData extends LiveData<Location> {
    private static LocationLiveData sInstance;
    private LocationManager locationManager;

    @MainThread
    public static LocationLiveData get(Context context) {
        if (sInstance == null) {
            sInstance = new LocationLiveData(context.getApplicationContext());
        }
        return sInstance;
    }

    private SimpleLocationListener listener = new SimpleLocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            setValue(location);
        }
    };

    private LocationLiveData(Context context) {
        locationManager = (LocationManager) context.getSystemService(
                Context.LOCATION_SERVICE);
    }

    @Override
    protected void onActive() {
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
    }

    @Override
    protected void onInactive() {
        locationManager.removeUpdates(listener);
    }
}复制代码

如今 fragment 能够像下面这样使用它:

public class MyFragment extends LifecycleFragment {
    public void onActivityCreated (Bundle savedInstanceState) {
        Util.checkUserStatus(result -> {
            if (result) {
                LocationLiveData.get(getActivity()).observe(this, location -> {
                   // update UI
                });
            }
        });
  }
}复制代码

可能会有多个 fragment 和 activity 在观察 MyLocationListener 实例,LiveData 能够规范的管理它们,以便只有当它们中的任何一个可见(即处于活动状态)时才链接到系统服务。

LiveData 有如下优势:

  • 没有内存泄漏:由于 Observer 被绑定到它们本身的 Lifecycle 对象上,因此,当它们的 Lifecycle 被销毁时,它们能自动的被清理。

  • 不会由于 activity 中止而崩溃:若是 ObserverLifecycle 处于闲置状态(例如:activity 在后台时),它们不会收到变动事件。

  • 始终保持数据最新:若是 Lifecycle 从新启动(例如:activity 从后台返回到启动状态)将会收到最新的位置数据(除非尚未)。

  • 正确处理配置更改:若是 activity 或 fragment 因为配置更改(如:设备旋转)从新建立,将会当即收到最新的有效位置数据。

  • 资源共享:能够只保留一个 MyLocationListener 实例,只链接系统服务一次,而且可以正确的支持应用程序中的全部观察者。

  • 再也不手动管理生命周期你可能已经注意到,fragment 只是在须要的时候观察数据,不用担忧被中止或者在中止以后启动观察。因为 fragment 在观察数据时提供了其 Lifecycle,因此 LiveData 会自动管理这一切。

LiveData 的转换

有时候可能会须要在将 LiveData 发送到观察者以前改变它的值,或者须要更具另外一个 LiveData 返回一个不一样的 LiveData 实例。

Lifecycle 包提供了一个 Transformations 类包含对这些操做的帮助方法。

  • Transformations.map()

    LiveData 的值上应用一个方法,并将结果传递到下游。

    LiveData<User> userLiveData = ...;
      LiveData<String> userName = Transformations.map(userLiveData, user -> {
          user.name + " " + user.lastName
      });复制代码
  • Transformations.switchMap()

    map() 相似,将一个方法应用到 LiveData 的值并解包,而后将结果传递到下游。传递给 switchMap() 的方法必须返回一个 Lifecycle

    private LiveData<User> getUser(String id) {
          ...;
      }
    
      LiveData<String> userId = ...;
      LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );复制代码

使用这些转换容许在整个调用链中携带观察者的 Lifecycle 信息,以便只有在观察者观察到 LiveData 的返回时才运算这些转换。转换的这种惰性运算性质容许隐式的传递生命周期相关行为,而没必要添加显式的调用或依赖。

每当你认为在 ViewModel 中须要一个 Lifecycle 类时,转换多是解决方案。

例如:假设有一个 UI,用户输入一个地址而后会收到该地址的邮政编码。该 UI 简单的 ViewModel 可能像这样:

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    public MyViewModel(PostalCodeRepository repository) {
       this.repository = repository;
    }

    private LiveData<String> getPostalCode(String address) {
       // 不要这样作!!!
       return repository.getPostCode(address);
    }
}复制代码

若是是像这种实现,UI 须要先从以前的 LiveData 注销而且在每次调用 getPostalCode() 时从新注册到新的实例。此外,若是 UI 被从新建立,它将会触发新的 repository.getPostCode() 调用,而不是使用以前的调用结果。

不能使用那种方式,而应该实现将地址输入转换为邮政编码信息。

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);
  }
}复制代码

请注意,咱们甚至使 postalCode 字段为 public final,由于它永远不会改变。postalCode 被定义为 addressInput 的转换,因此当 addressInput 改变时,若是有处于活动状态的观察者,repository.getPostCode() 将会被调用。若是在调用时没有处于活动状态的观察者,在添加观察者以前不会进行任何运算。

该机制容许以较少的资源根据须要惰性运算来建立 LiveDataViewModel 能够轻松获取到 LiveData 并在它们上面定义转换规则。

建立新的转换

在应用程序中可能会用到十几种不一样的特定转换,可是默认是不提供的。可使用 MediatorLiveData 实现本身的转换,MediatorLiveData 是为了用来正确的监听其它 LiveData 实例并处理它们发出的事件而特别建立的。MediatorLiveData 须要特别注意正确的向源 LiveData 传递其处于活动/闲置状态。有关详细信息,请参阅 Transformations 类。

相关文章
相关标签/搜索