温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Android ViewModelScope怎么自动取消协程

发布时间:2022-07-04 09:37:57 来源:亿速云 阅读:158 作者:iii 栏目:开发技术

本文小编为大家详细介绍“Android ViewModelScope怎么自动取消协程”,内容详细,步骤清晰,细节处理妥当,希望这篇“Android ViewModelScope怎么自动取消协程”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

先看一下 ViewModel 中的 ViewModelScope 是何方神圣

val ViewModel.viewModelScope: CoroutineScope
        get() {
            val scope: CoroutineScope? = this.getTag(JOB_KEY)
            if (scope != null) {
                return scope
            }
            return setTagIfAbsent(JOB_KEY,
                CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
        }

可以看到这个是一个扩展方法,

再点击 setTagIfAbsent 方法进去

 <T> T setTagIfAbsent(String key, T newValue) {
        T previous;
        synchronized (mBagOfTags) {
            previous = (T) mBagOfTags.get(key);//第一次肯定为null
            if (previous == null) {
                mBagOfTags.put(key, newValue);//null 存储
            }
        }
        T result = previous == null ? newValue : previous;
        if (mCleared) {//判断是否已经clear了
            // It is possible that we'll call close() multiple times on the same object, but
            // Closeable interface requires close method to be idempotent:
            // "if the stream is already closed then invoking this method has no effect." (c)
            closeWithRuntimeException(result);
        }
        return result;
    }

可以看到 这边 会把 我们的 ViewModel 存储到 ViewModel 内的 mBagOfTags 中

这个 mBagOfTags 是

    private final Map<String, Object> mBagOfTags = new HashMap<>();

这个时候 我们 viewModel 就会持有 我们 viewModelScope 的协程 作用域了。那..这也只是 表述了 我们 viewModelScope 存在哪里而已,什么时候清除呢?

先看一下 ViewModel 的生命周期:

Android ViewModelScope怎么自动取消协程

可以看到 ViewModel 的生命周期 会在 Activity onDestory 之后会被调用。那...具体哪里调的?

翻看源码可以追溯到 ComponentActivity 的默认构造器内

 public ComponentActivity() {
     /*省略一些*/
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });
  }

可以看到内部会通对 Lifecycle 添加一个观察者,观察当前 Activity 的生命周期变更事件,如果走到了 Destory ,并且 本次 Destory 并非由于配置变更引起的,才会真正调用 ViewModelStore 的 clear 方法。

跟进 clear 方法看看:

public class ViewModelStore {
    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

可以看到这个 ViewModelStore 内部实现 用 HashMap 存储 ViewModel

于是在 clear 的时候,会逐个遍历调用 clear方法,再次跟进 ViewModel 的 clear 方法

 @MainThread
    final void clear() {
        mCleared = true;
        // Since clear() is final, this method is still called on mock objects
        // and in those cases, mBagOfTags is null. It'll always be empty though
        // because setTagIfAbsent and getTag are not final so we can skip
        // clearing it
        if (mBagOfTags != null) {
            synchronized (mBagOfTags) {
                for (Object value : mBagOfTags.values()) {
                    // see comment for the similar call in setTagIfAbsent
                    closeWithRuntimeException(value);
                }
            }
        }
        onCleared();
    }

可以发现我们最初 存放 viewmodelScope 的 mBagOfTags

这里面的逻辑 就是对 mBagOfTags 存储的数据 挨个提取出来并且调用 closeWithRuntimeException

跟进 closeWithRuntimeException:

 private static void closeWithRuntimeException(Object obj) {
        if (obj instanceof Closeable) {
            try {
                ((Closeable) obj).close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

该方法内会逐个判断 对象是否实现 Closeable 如果实现就会调用这个接口的 close 方法,

再回到最初 我们 viewModel 的扩展方法那边,看看我们 viewModelScope 的真正面目

internal class CloseableCoroutineScope(context: CoroutineContext) 
    : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context

    override fun close() {
        coroutineContext.cancel()
    }
}

可以明确的看到 我们的 ViewModelScope 实现了 Closeable 并且充写了 close 方法,

close 方法内的实现 会对 协程上下文进行 cancel。

至此我们 可以大致整理一下:

  • viewModelScope 是 ViewModel 的扩展成员,该对象是 CloseableCoroutineScope,并且实现了 Closeable 接口

  • ViewModelScope 存储在 ViewModel 的 名叫 mBagOfTags 的HashMap中 啊

  • ViewModel 存储在 Activity 的 ViewModelStore 中,并且会监听 Activity 的 Lifecycle 的状态变更,在ON_DESTROY 且 非配置变更引起的事件中 对 viewModelStore 进行清空

  • ViewModelStore 清空会对 ViewModelStore 内的所有 ViewModel 逐个调用 clear 方法。

  • ViewModel的clear方法会对 ViewModel的 mBagOfTags 内存储的对象进行调用 close 方法(该对象需实现Closeable 接口)

  • 最终会会调用 我们 ViewModelScope 的实现类 CloseableCoroutineScope 的 close 方法中。close 方法会对协程进行 cancel。

读到这里,这篇“Android ViewModelScope怎么自动取消协程”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI