JetPack——ViewModel

ViewModel是什么?

ViewModel 类是一种业务逻辑或屏幕级状态容器。它用于将状态公开给界面,以及封装相关的业务逻辑。 它的主要优点是,它可以缓存状态,并可在配置更改后持久保留相应状态。这意味着在 activity 之间导航时或进行配置更改后(例如旋转屏幕时),界面将无需重新提取数据。

ViewModel解决什么问题?

如下是MVP架构中播放暂停按钮的实现

class MainActivity : AppCompatActivity(), PlayPresenter.PlayStateChangeListener {

    val TAG = "MainActivity"
    private lateinit var playBtn: Button
    private val playPresenter: PlayPresenter by lazy { PlayPresenter() }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        playPresenter.setOnPlayStateChangeListener(this)
        playBtn = findViewById<Button>(R.id.playBtn)
        playBtn.setOnClickListener {
            playPresenter.playOrStop()
        }
    }

    override fun onPlayStateChange(playState: PlayPresenter.PlayState) {
        when (playState) {
            PlayPresenter.PlayState.PLAYING -> {
                playBtn.text = "PLAYING"
            }

            PlayPresenter.PlayState.STOP -> {
                playBtn.text = "STOP"
            }
        }
    }
}
class PlayPresenter {

    private lateinit var listener: PlayStateChangeListener

    private var currentState = PlayState.STOP

    enum class PlayState {
        PLAYING, STOP
    }

    fun playOrStop() {
        when (currentState) {
            PlayState.STOP -> currentState = PlayState.PLAYING
            PlayState.PLAYING -> currentState = PlayState.STOP
        }
        listener.onPlayStateChange(currentState)
    }

    interface PlayStateChangeListener {
        fun onPlayStateChange(playState: PlayState)
    }

    fun setOnPlayStateChangeListener(listener: PlayStateChangeListener) {
        this.listener = listener
    }
}

利用LiveData可以省略监听和回调过程,而用ViewModel代替PlayPresenter,ViewModel主要优势实际上有两个方面:

  • 它允许您持久保留界面状态
  • 它可以提供对业务逻辑的访问权限

ViewModel实现

本文使用的版本如下,版本不一样需要更新AS及Gradle

在这里插入图片描述

class MainActivity : AppCompatActivity() {

    val TAG = "MainActivity"
    private lateinit var playBtn: Button

    private val playModel by lazy { ViewModelProvider(this).get(PlayModel::class.java) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        playModel.currentState.observe(this, Observer<Any?> {
            when (it) {
                PlayModel.PlayState.PLAYING -> {
                    playBtn.text = "PLAYING"
                }

                PlayModel.PlayState.STOP -> {
                    playBtn.text = "STOP"
                }
            }
        })
        playBtn = findViewById<Button>(R.id.playBtn)
        playBtn.setOnClickListener { playModel.playOrStop() }
    }
}
class PlayModel : ViewModel() {
    val currentState = MutableLiveData<PlayState>(PlayState.STOP)

    enum class PlayState {
        PLAYING, STOP
    }

    fun playOrStop() {
        when (currentState.value) {
            PlayState.STOP -> currentState.postValue(PlayState.PLAYING)
            PlayState.PLAYING -> currentState.postValue(PlayState.STOP)
            else -> {}
        }
    }
}

如果导入

implementation 'androidx.activity:activity-ktx:1.8.0'

则可以通过by viewModels()简化初始化,其是ComponentActivity的扩展函数,本质是通过ComponentActivity提供的默认工厂/参数创建ViewModel

private val playModel: PlayModel by viewModels()

ViewModel源码解析

ViewModelStoreOwner

实现类含有ViewModelStore

interface ViewModelStoreOwner {
    val viewModelStore: ViewModelStore
}

ViewModelStore

通过包含全限定类名的String从map中存取对应的ViewModel

open class ViewModelStore {

    private val map = mutableMapOf<String, ViewModel>()

    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    fun put(key: String, viewModel: ViewModel) {
        val oldViewModel = map.put(key, viewModel)
        oldViewModel?.onCleared()
    }

    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    operator fun get(key: String): ViewModel? {
        return map[key]
    }

    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    fun keys(): Set<String> {
        return HashSet(map.keys)
    }

    fun clear() {
        for (vm in map.values) {
            vm.clear()
        }
        map.clear()
    }
}

ViewModel.java

  • 通过map管理内部的协程
  • 通过Set管理内部的Closeable资源
  • Activity销毁前会回调onCleared()
public abstract class ViewModel {

    @Nullable
    private final Map<String, Object> mBagOfTags = new HashMap<>();
    @Nullable
    private final Set<Closeable> mCloseables = new LinkedHashSet<>();
    private volatile boolean mCleared = false;

    public ViewModel() {
    }

    public ViewModel(@NonNull Closeable... closeables) {
        mCloseables.addAll(Arrays.asList(closeables));
    }


    public void addCloseable(@NonNull Closeable closeable) {

        if (mCloseables != null) {
            synchronized (mCloseables) {
                mCloseables.add(closeable);
            }
        }
    }

    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }

    @MainThread
    final void clear() {
        mCleared = true;

        if (mBagOfTags != null) {
            synchronized (mBagOfTags) {
                for (Object value : mBagOfTags.values()) {
                    // see comment for the similar call in setTagIfAbsent
                    closeWithRuntimeException(value);
                }
            }
        }
        // We need the same null check here
        if (mCloseables != null) {
            synchronized (mCloseables) {
                for (Closeable closeable : mCloseables) {
                    closeWithRuntimeException(closeable);
                }
            }
        }
        onCleared();
    }

    @SuppressWarnings("unchecked")
    <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;
    }

    @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
    <T> T getTag(String key) {
        if (mBagOfTags == null) {
            return null;
        }
        synchronized (mBagOfTags) {
            return (T) mBagOfTags.get(key);
        }
    }

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

ViewModel.kt

新增扩展属性viewModelScope,其是一个CoroutineScope

private const val JOB_KEY = "androidx.lifecycle.ViewModelCoroutineScope.JOB_KEY"

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

ViewModelProvider

  • 最终调用3参构造函数初始化ViewModelStore、Factory、CreationExtras
  • defaultFactory()/defaultCreationExtras()先判断当前ViewModelStore是否是HasDefaultViewModelProviderFactory,若是则调用提供的默认工厂/参数,否则使用NewInstanceFactory/CreationExtras.Empty
  • get()根据全限定类名判断ViewModelStore是否已经创建,若是则直接返回,否则通过Factory和CreationExtras创建并存储到ViewModelStore的map中
public open class ViewModelProvider

@JvmOverloads
constructor(
    private val store: ViewModelStore,
    private val factory: Factory,
    private val defaultCreationExtras: CreationExtras = CreationExtras.Empty,
) {
 
    public interface Factory {
     
        public fun <T : ViewModel> create(modelClass: Class<T>): T {
            throw UnsupportedOperationException(
                "Factory.create(String) is unsupported.  This Factory requires " +
                    "`CreationExtras` to be passed into `create` method."
            )
        }
        
        public fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T =
            create(modelClass)

        companion object {
           
            @JvmStatic
            fun from(vararg initializers: ViewModelInitializer<*>): Factory =
                InitializerViewModelFactory(*initializers)
        }
    }

    public constructor(
        owner: ViewModelStoreOwner
    ) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))

   
    public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
        owner.viewModelStore,
        factory,
        defaultCreationExtras(owner)
    )
    
    @MainThread
    public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
        val canonicalName = modelClass.canonicalName
            ?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
        return get("$DEFAULT_KEY:$canonicalName", modelClass)
    }

   
    @Suppress("UNCHECKED_CAST")
    @MainThread
    public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
        val viewModel = store[key]
        if (modelClass.isInstance(viewModel)) {
            
            return viewModel as T
        } else {
            @Suppress("ControlFlowWithEmptyBody")
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        val extras = MutableCreationExtras(defaultCreationExtras)
        extras[VIEW_MODEL_KEY] = key

        return try {
            factory.create(modelClass, extras)
        } catch (e: AbstractMethodError) {
            factory.create(modelClass)
        }.also { store.put(key, it) }
    }


    @Suppress("SingletonConstructor")
    public open class NewInstanceFactory : Factory {
        @Suppress("DocumentExceptions")
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return try {
                modelClass.getDeclaredConstructor().newInstance()
            } catch (e: NoSuchMethodException) {
                throw RuntimeException("Cannot create an instance of $modelClass", e)
            } catch (e: InstantiationException) {
                throw RuntimeException("Cannot create an instance of $modelClass", e)
            } catch (e: IllegalAccessException) {
                throw RuntimeException("Cannot create an instance of $modelClass", e)
            }
        }

        public companion object {
            private var sInstance: NewInstanceFactory? = null

            @JvmStatic
            public val instance: NewInstanceFactory
                @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
                get() {
                    if (sInstance == null) {
                        sInstance = NewInstanceFactory()
                    }
                    return sInstance!!
                }

            private object ViewModelKeyImpl : Key<String>

            @JvmField
            val VIEW_MODEL_KEY: Key<String> = ViewModelKeyImpl
        }
    }
}
internal fun defaultCreationExtras(owner: ViewModelStoreOwner): CreationExtras {
    return if (owner is HasDefaultViewModelProviderFactory) {
        owner.defaultViewModelCreationExtras
    } else CreationExtras.Empty
}

ViewModel 的生命周期

在这里插入图片描述
ViewModel不受Configuration改变影响生命周期,当ComponentActivity结束时会回调onCleared()

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();
                }
                .......
            }
        }
    });
    ......
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值