读源码长知识 | 动态扩展类并绑定生命周期的新方式

不使用继承和组合,如何动态地扩展类?好比,如何给 Activity 扩展一个 String 属性,当 Activity 被销毁时,将其置空?web

在阅读viewModelScope源码时,发现了一种新的方式。算法

这是读源码长知识系列的第四篇,该系列的特色是将源码中的设计思想运用到真实项目之中,系列文章目录以下:缓存

  1. 读源码长知识 | 更好的RecyclerView点击监听器编辑器

  2. Android自定义控件 | 源码里有宝藏之自动换行控件ide

  3. Android自定义控件 | 小红点的三种实现(下)post

  4. 读源码长知识 | 动态扩展类并绑定生命周期的新方式this

协程需隶属于某CoroutineScope,以实现structured-concurrency,而CoroutineScope应该和某个生命周期组件相绑定,以便同步生命周期。url

ViewModel生命周期绑定的viewModelScope被定义成它的扩展属性。它是怎么作到和ViewModel生命周期绑定的:spa

val ViewModel.viewModelScope: CoroutineScope
 get() {  // 尝试根据 tag 获取 CoroutineScope  val scope: CoroutineScope? = this.getTag(JOB_KEY)  // 命中则直接返回  if (scope != null) {  return scope  }  // 若未命中则构建 CloseableCoroutineScope 并将其和 JOB_KEY 绑定  return setTagIfAbsent(JOB_KEY,  CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main))  }   复制代码

这和缓存的写法一摸同样。猜想CoroutineScope实例可能缓存在ViewModel的某个属性中,去ViewModel源码中确认一下:设计

public abstract class ViewModel {
 // 存放 Object对象 的 map  private final Map<String, Object> mBagOfTags = new HashMap<>();   // 取值方法  <T> T getTag(String key) {  synchronized (mBagOfTags) {  return (T) mBagOfTags.get(key);  }  }   // 设置方法  <T> T setTagIfAbsent(String key, T newValue) {  T previous;  synchronized (mBagOfTags) {  previous = (T) mBagOfTags.get(key);  if (previous == null) {  mBagOfTags.put(key, newValue);  }  }  T result = previous == null ? newValue : previous;  if (mCleared) {  closeWithRuntimeException(result);  }  return result;  }   // ViewModel 生命周期结束时释放资源  final void clear() {  mCleared = true;  // 遍历 map 清理其中的对象  if (mBagOfTags != null) {  synchronized (mBagOfTags) {  for (Object value : mBagOfTags.values()) {  // 清理单个对象  closeWithRuntimeException(value);  }  }  }  onCleared();  }   // 清理实现了 Closeable 接口的对象  private static void closeWithRuntimeException(Object obj) {  if (obj instanceof Closeable) {  try {  ((Closeable) obj).close();  } catch (IOException e) {  throw new RuntimeException(e);  }  }  } } 复制代码

ViewModel预留了后门,是存放Object对象的HashMap结构。这使得不修改ViewModel源码,就能为其动态扩展属性。

ViewModel在生命周期结束时,会清理后门中全部的Closeable对象。当扩展属性也是该类型时类,其生命周期自动和ViewModel同步。

Cloaseable接口定义以下:

public interface Closeable extends AutoCloseable {
 // 定义如何释放资源  public void close() throws IOException; } 复制代码

回到扩展属性viewModelScope的获取算法,从 Map 中获取viewModelScope失败后,会构建CloseableCoroutineScope对象,它实现了Closeable接口:

// 可自动取消的 CoroutineScope
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {  override val coroutineContext: CoroutineContext = context   // 将协程取消  override fun close() {  coroutineContext.cancel()  } } 复制代码

设计类的时候,也能够借用这个套路,预留一个存放Closeable接口的 Map 属性,公开取值和设置方法,而且在类生命周期结束时清理 map 中的对象,让其对扩展更加友好!

本文使用 mdnice 排版

相关文章
相关标签/搜索