viewModelScope是如何做到自动解绑网络请求的

建议大家可以对着文章末尾的时序图(画的不咋的)一起看会比较好理解,下面开始吧

最近Kotlin的使用越来越普及,尤其是kotlin携程的使用,更是极大地简化了异步操作,尤其是网络请求,再也不怕线程切换了,从此告别接口回调,和RxJava,对是的,你没听错,暂时告别RxJava,只能说RxJava这把杀牛刀,被我们拿来杀鸡,确实有点大材小用。扯远了,下面开始进入正题,来看看Android的ViewModel的扩展变量,ViewModel.viewModelScope到底是如何在页面关闭的时候,自动关闭携程的(也就是我们的网络请求)。

一、初始化

我们在执行网络请求之前,一般都要先初始化ViewModel,代码如下

mViewModel = ViewModelProvider(this,ViewModelProvider.AndroidViewModelFactory.getInstance(application)).get(tClass) as VM
lifecycle.addObserver(mViewModel)

注意了,看似不起眼的2行代码,可是后面完成自动取消的关键,我们一句一句来分析,先看第一句

mViewModel = ViewModelProvider(this,ViewModelProvider.AndroidViewModelFactory.getInstance(application)).get(tClass) as VM

这里调用了ViewModelProvider的构造函数,我们点进去看看,这里也很重要

public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }

注意一点,此处调用了下面这行代码

owner.getViewModelStore()

owner即当前的Activity,的getViewModelStore方法,此处也很关键,我们点进去看一下,是ViewModelStoreOwner 接口

public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

看一下,我们的BaseActivity的继承关系

BaseActivity  > AppCompatActivity > FragmentActivity  >  ComponentActivity > ViewModelStoreOwner

我们点进去看一下就会发现,最终实现 getViewModelStore() 方法的是 ComponentActivity,如下

  @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

这里主要的作用是初始化了 ViewModelStore ,传递给ViewModelProvider ,这个也很重要,后面会用到。好的,现在我们回到刚才的 mViewModel初始化的地方,就是刚才的第一句代码,还没说完,我们接着看。

mViewModel = ViewModelProvider(this,ViewModelProvider.AndroidViewModelFactory.getInstance(application)).get(tClass) as VM

现在我们来看看,这个get方法

ViewModelProvider(x,x).get(tClass)

点进去以后

@NonNull
    @MainThread
    public  T get(@NonNull Class modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }

又调用了同名方法,继续往下看

@NonNull
    @MainThread
    public  T get(@NonNull String key, @NonNull Class modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
        //因为我们使用的是默认的Factory,所以这里就不看了,以后如果需要自定义Factory的时候,这里就是我们自定义的Factory的方法
            viewModel = (mFactory).create(modelClass);
        }
        //主要是这里,把viewModel存储在mViewModelStore里
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

上面的代码,主要干了一件事,就是实例化viewModel,并把viewModel存储在mViewModelStore里,这里我们顺便看一下ViewModelStore,这个类代码不多,也是后面自动解绑的关键

public class ViewModelStore {
	当前页面的所有ViewModel都存储在这个map里,可能一般只有一个ViewModel
    private final HashMap mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
    这个方法就厉害了,这里只是提一下,后面还会回来的^_^
     *  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();
    }
}

好的,至此我们的第一行初始化代码就讲完了,其实最主要的一件事情就是,把实例化的ViewMode放在了ViewModelStore的map里

下面我们开始分析第二句代码

lifecycle.addObserver(mViewModel)

这里的lifecycle实际上是调用的 FragmentActivity 的 getLifecycle() 方法

 @NonNull
        @Override
        public Lifecycle getLifecycle() { 
            return mFragmentLifecycleRegistry;
        }

mFragmentLifecycleRegistry在一开始就初始化了

final LifecycleRegistry mFragmentLifecycleRegistry = new LifecycleRegistry(this);

传的 this 即 继承了 LifecycleOwner 的 FragmentActivity ,我们再看,第二句代码的后面一句 addObserve()方法

lifecycle.addObserver(mViewModel)

点进去看看

@MainThread
public abstract void addObserver(@NonNull LifecycleObserver observer);

是一个抽象方法,他的实现类是刚才我们才见过的 LifecycleRegistry,
我们进去看看

@Override
    public void addObserver(@NonNull LifecycleObserver observer) {
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);

        if (previous != null) {
            return;
        }
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            // it is null we should be destroyed. Fallback quickly
            return;
        }

        boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
        State targetState = calculateTargetState(observer);
        mAddingObserverCounter++;
        while ((statefulObserver.mState.compareTo(targetState) < 0
                && mObserverMap.contains(observer))) {
            pushParentState(statefulObserver.mState);
            statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
            popParentState();
            // mState / subling may have been changed recalculate
            targetState = calculateTargetState(observer);
        }

        if (!isReentrance) {
            // we do sync only on the top level.
            sync();
        }
        mAddingObserverCounter--;
    }

其实这么多代码,只有一个地方,我们需要知道即可,就是把我们传进来的 observer 也就是ViewModel,放在了 mObserverMap 里面,这里后面马上也会说到。
至此,第二句代码也分析完了,主要是把ViewModel即观察者放在了mObserverMap里

二、开启携程,即开启我们的网络请求

一般我们的网络请求是在 ViewModel 里的,代码如下

block()即为我们的网络请求方法
viewModelScope.launch { block() }

viewModelScope 是 ViewModel 的扩展属性,这个是Google官方提供的,用它就对了,我们看一下这个扩展属性,看看它到底厉害在哪里

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))
        }

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

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

上述代码可以知道,在开启携程的时候,先创建了一个自定义的携程作用域 CloseableCoroutineScope 用于之后的取消携程,存储在ViewModel的 mBagOfTags里,记住这个 mBagOfTags,后面取消携程时,也是它。

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

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

注意上面这个自定义的 CloseableCoroutineScope,它实现了Closeable接口,这里也很关键,并在 close() 方法里关闭了携程。
至此,启动携程的代码我们就讲完了,其实启动携程很简单,是不是感觉还没准备好就结束了,好吧,让我们直接开始高潮部分,到底viewModelScope是如何在页面关闭的时候,自动取消携程的

三、viewModelScope在页面关闭的时候,自动取消携程

开始之前我们还是再看一眼Activity的继承关系,加深点印象

BaseActivity  > AppCompatActivity > FragmentActivity  >  ComponentActivity > ViewModelStoreOwner

我们直接进到 FragmentActivity 的 onDestroy 方法里看一下页面关闭的时候做了什么

@Override
    protected void onDestroy() {
        super.onDestroy();
        mFragments.dispatchDestroy();
        mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
    }

我们看到,最后一行代码 ,mFragmentLifecycleRegistry 还记得吧,之前初始化的时候可是提到过的,在 getLifecycle() 方法里返回的就是它,在一开始就初始化了的。

mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);

这里就开始了,我们接着点进去看一下 handleLifecycleEvent 方法

public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
        State next = getStateAfter(event);
        moveToState(next);
    }

调用了 getStateAfter 返回了一个 State = DESTROYED,主要是调用了 moveToState,我们点进去看看

private void moveToState(State next) {
        if (mState == next) {
            return;
        }
        mState = next;
        if (mHandlingEvent || mAddingObserverCounter != 0) {
            mNewEventOccurred = true;
            // we will figure out what to do on upper level.
            return;
        }
        mHandlingEvent = true;
        sync();
        mHandlingEvent = false;
    }

没什么好看的,继续往下看看 sync() 方法

private void sync() {
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            throw new IllegalStateException("LifecycleOwner of this LifecycleRegistry is already"
                    + "garbage collected. It is too late to change lifecycle state.");
        }
        while (!isSynced()) {
            mNewEventOccurred = false;
            // no need to check eldest for nullability, because isSynced does it for us.
            if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
                backwardPass(lifecycleOwner);
            }
            Entry newest = mObserverMap.newest();
            if (!mNewEventOccurred && newest != null
                    && mState.compareTo(newest.getValue().mState) > 0) {
                forwardPass(lifecycleOwner);
            }
        }
        mNewEventOccurred = false;
    }

这里会调用2个方法 backwardPass() 和 forwardPass() 其实这两个方法里面代码几乎一样,一个是从前往后找,一个是从后往前找,找什么,当然是找观察者了,LifecycleEventObserver,都存储在 mObserverMap 里,我们就看一个backwardPass() 方法

private void backwardPass(LifecycleOwner lifecycleOwner) {
        Iterator> descendingIterator =
                mObserverMap.descendingIterator();
        while (descendingIterator.hasNext() && !mNewEventOccurred) {
            Entry entry = descendingIterator.next();
            ObserverWithState observer = entry.getValue();
            while ((observer.mState.compareTo(mState) > 0 && !mNewEventOccurred
                    && mObserverMap.contains(entry.getKey()))) {
                Event event = downEvent(observer.mState);
                pushParentState(getStateAfter(event));
                observer.dispatchEvent(lifecycleOwner, event);
                popParentState();
            }
        }
    }

我们看到这行代码,在找到对应的观察者以后,开始回调了,event 是 DESTROYED

observer.dispatchEvent(lifecycleOwner, event);

那么,这个观察者 LifecycleEventObserver 到底在哪里注册的呢,之前初始化的时候,我们没说,因为这个不是我们初始化的,是系统代码初始化的,在 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();
                    }
                }
            }
        });

在onDestroy的时候会回调到这里,最主要的是调用了下面这句

getViewModelStore().clear();

还记得 getViewModelStore() 方法吗,刚才我们在初始化的时候可是见过啊,不记得的往上翻一下就看见了,所以这个时候 mViewModelStore 早已初始化过了,直接返回 mViewModelStore ,主要我们看一下 clear() 方法

public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }

是不是很熟悉,刚才初始化的时候,我们已经把ViewModel放在了,这个ViewModelStore 的 mMap里面,这个时候,在遍历ViewModel,并且调用其 vm.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();
    }

这里在遍历 mBagOfTags ,还记得刚开启携程的时候,在 viewModelScope 的扩展属性里,调用了 setTagIfAbsent 把当前的 CloseableCoroutineScope 传进来,存储在 mBagOfTags里吗,我们继续点进去看看 closeWithRuntimeException()方法

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

最后调用的是 ((Closeable) obj).close(),还记得刚才我们开启携程的时候,说的吗 CloseableCoroutineScope 实现了 Closeable 接口,所以这里的 close() 方法最终会走到 CloseableCoroutineScope 的 close() 方法里

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

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

最终在这里,关闭了携程 coroutineContext.cancel()。

至此,viewModelScope是如何做到自动解绑网络请求的,就分析完了,如有错误还请指正。
下面附上我自己画的时序图,没有用PowerDesign画的不是很好。
viewModelScope是如何做到自动解绑网络请求的_第1张图片

你可能感兴趣的:(Android)