草庐IT

Android Jetpack系列之ViewModel的基本逻辑解析

夏之冰啤 2023-03-28 原文

如果把Activity比喻成一栋大房子,LiveData就好比一个个家丁,那ViewModel就是那个大管家,他管理着房子的大大小小的事务和每个家丁的日常工作。
ViewModel的设计目的可以概况成以下几点:

1.维护Activity的数据不随着Activity的各种生命异常而丢失,比如手机的横竖屏切换的时候;
2.承担起Activity控制层的作用,让Activity的代码不至于太膨胀,便于维护;
3.在作用域可控的情况下管理各种异步请求和数据的生命敏感事件。

这就是谷歌给开发者的一套标准的开发app的指导方案,很权威,很nice,用完你会感叹相见恨晚。
先来看看ViewModel的创建,继承ViewModel即可:

class MainViewModel : ViewModel() {
    // 维护了一个liveData
    private val testLiveData = MutableLiveData(0)
    // get方法只返回liveData,这样就实现了liveData的读写分离,在viewModel里更改,activity里读取
    fun getTestLiveData(): LiveData<Int> {
        return testLiveData
    }
}

怎么实例化呢,官方不推荐直接new出来,而是这样:

class MainActivity : AppCompatActivity() {
    private lateinit var mainViewModel: MainViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 官方推荐的实例化方式
        mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
   
    }
}

那就从ViewModelProvider开始入手,看看ViewModel是基于什么原理设计和维护的,先看看ViewModelProvider的构造方法和get方法:

public class ViewModelProvider {
    // 维护了两个成员变量,实际上也都是他们俩在做事
    private final Factory mFactory; // 创建ViewModel实例的工厂
    private final ViewModelStore mViewModelStore; // 字面意思就是ViewModel存储类
    //
    public interface Factory {
        /**
         * Creates a new instance of the given {@code Class}.
         * <p>
         *
         * @param modelClass a {@code Class} whose instance is requested
         * @param <T>        The type parameter for the ViewModel.
         * @return a newly created ViewModel
         */
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    } 
    // ViewModelStoreOwner是个接口,字面意思是ViewModelStore拥有者,根据谷歌jetpack代码命名规律也能猜到它的作用是能提供
   //ViewModelStore ,我们在上面的示例传入了自身Activity this,说明父类实现了ViewModelStoreOwner接口。
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        // ComponentActivity也实现了HasDefaultViewModelProviderFactory,下面我们把它相关方法也贴出来
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
    // 最终给两个成员变量完成了赋值
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }


     @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        // 检查类的合法性
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        //  key加了DEFAULT_KEY 拼装起来
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }


    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        // 先从mViewModelStore里取,这里猜测应该是个缓存
        ViewModel viewModel = mViewModelStore.get(key);
        // 如果是ViewModel的子类对象并且mFactory是OnRequeryFactory子类对象
        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.
            }
        }
        // 用相应的工厂创建ViewModel实例
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
        } else {
            viewModel = mFactory.create(modelClass);
        }
        // 放入缓存
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }
  ...略
}

ViewModelProvider的两个成员变量mViewModelStore 和mFactory 分别来自ComponentActivity的getViewModelStore()和getDefaultViewModelProviderFactory() 方法:

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ...
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner {

    @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.");
        }
        // 这个方法就是创建了一个ViewModelStore赋值给了mViewModelStore
        ensureViewModelStore();
        return mViewModelStore;
    }

    @NonNull
    @Override
    public final SavedStateRegistry getSavedStateRegistry() {
        return mSavedStateRegistryController.getSavedStateRegistry();
    }

    @NonNull
    @Override
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        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 (mDefaultFactory == null) {
            // 创建的是SavedStateViewModelFactory,看命名是保存状态的vm工厂,注意这个构造函数穿了些啥
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }
}

// ViewModelStore 专门用来存储ViewModel
public class ViewModelStore {
    // 熟悉的存储方式,map
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    // 存储
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        // 如果之前存储了相同的,执行一下onCleared方法
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
    // 取缓存
    final ViewModel get(String key) {
        return mMap.get(key);
    }
    
    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
     *  清除所有的viewModel
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        // 注意会执行viewModel的clear方法
        mMap.clear();
    }
}
注意:其实到这里你基本应该已经知道了ViewModel是怎么被创建和保存起来的了,以下内容只是探寻ViewModel究竟是怎么被实例化的,提前透露一下就是用类名通过newInstance()实例化的。
现在做个小总结:ViewModel如果通过ViewModelProvider的get方法实例化的话,同一个Activity(ViewModelStoreOwner)里只会实例化一个实例然后缓存到ViewModelStore里,每次获取的时候是会先取缓存,所以保证了同一个ViewModel的唯一性!

再来看SavedStateViewModelFactory:

public final class SavedStateViewModelFactory extends ViewModelProvider.KeyedFactory {
    private final Application mApplication;
    private final ViewModelProvider.Factory mFactory; // 实际是这个工厂在创建vm实例
    private final Bundle mDefaultArgs;
    private final Lifecycle mLifecycle; // 维护了一个lifecycle
    private final SavedStateRegistry mSavedStateRegistry; // 所谓的保存状态应该跟这个有关

   
    public SavedStateViewModelFactory(@Nullable  Application application,
            @NonNull SavedStateRegistryOwner owner) {
        this(application, owner, null);
    }
    @SuppressLint("LambdaLast")
    public SavedStateViewModelFactory(@Nullable Application application,
            @NonNull SavedStateRegistryOwner owner,
            @Nullable Bundle defaultArgs) {
        mSavedStateRegistry = owner.getSavedStateRegistry();
        mLifecycle = owner.getLifecycle();
        mDefaultArgs = defaultArgs;
        mApplication = application;
        // 前面调用这个构造函数是传下来了application的,所以这里执行的是
        // ViewModelProvider.AndroidViewModelFactory.getInstance(application)
        mFactory = application != null
                ? ViewModelProvider.AndroidViewModelFactory.getInstance(application)
                : ViewModelProvider.NewInstanceFactory.getInstance();
    }
    // 这里是ViewModelProvider里创建viewModel调用的方法
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
        // 我们创建一般的ViewModel,不是继承自AndroidViewModel,所以是false
        boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
        Constructor<T> constructor;
        if (isAndroidViewModel && mApplication != null) {
            constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
        } else {
            // 所以会命中这里,去找匹配的构造函数,这个方法在下面
            constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
        }
        // doesn't need SavedStateHandle 
       //不需要SavedStateHandle ,至于需要的情况那是另外的应用场景,可以单独去了解
        if (constructor == null) {
            // 绕了半天,其实还是执行了这个创建方法,我们继续看AndroidViewModelFactory
            return mFactory.create(modelClass);
        }
        // 这里的逻辑是关于SavedStateHandle ,暂不细看
        SavedStateHandleController controller = SavedStateHandleController.create(
                mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
        try {
            T viewmodel;
            if (isAndroidViewModel && mApplication != null) {
                viewmodel = constructor.newInstance(mApplication, controller.getHandle());
            } else {
                viewmodel = constructor.newInstance(controller.getHandle());
            }
            viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
            return viewmodel;
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Failed to access " + modelClass, e);
        } catch (InstantiationException e) {
            throw new RuntimeException("A " + modelClass + " cannot be instantiated.", e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException("An exception happened in constructor of "
                    + modelClass, e.getCause());
        }
    }
@NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        // ViewModelProvider calls correct create that support same modelClass with different keys
        // If a developer manually calls this method, there is no "key" in picture, so factory
        // simply uses classname internally as as key.
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return create(canonicalName, modelClass);
    }

    private static final Class<?>[] ANDROID_VIEWMODEL_SIGNATURE = new Class[]{Application.class,
            SavedStateHandle.class};
    private static final Class<?>[] VIEWMODEL_SIGNATURE = new Class[]{SavedStateHandle.class};

    @SuppressWarnings("unchecked")
    private static <T> Constructor<T> findMatchingConstructor(Class<T> modelClass,
            Class<?>[] signature) {
        for (Constructor<?> constructor : modelClass.getConstructors()) {
             // 这里会去找入参是SavedStateHandle类型的构造函数,显然我们一般的
            //ViewModel是没有这个构造函数的,这里会返回null,我们继续回到上面
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            if (Arrays.equals(signature, parameterTypes)) {
                return (Constructor<T>) constructor;
            }
        }
        return null;
    }

    @Override
    void onRequery(@NonNull ViewModel viewModel) {
        attachHandleIfNeeded(viewModel, mSavedStateRegistry, mLifecycle);
    }
}

 public open class AndroidViewModelFactory( private val application: Application) : NewInstanceFactory() {
       // 通过上面的分析我们知道是调用了这个方法来创建ViewModel的
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            // 上面我们也分析过我们的viewModel不是AndroidViewModel的子类
            return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
                try {
                    modelClass.getConstructor(Application::class.java).newInstance(application)
                } catch (e: NoSuchMethodException) {
                    throw RuntimeException("Cannot create an instance of $modelClass", e)
                } catch (e: IllegalAccessException) {
                    throw RuntimeException("Cannot create an instance of $modelClass", e)
                } catch (e: InstantiationException) {
                    throw RuntimeException("Cannot create an instance of $modelClass", e)
                } catch (e: InvocationTargetException) {
                    throw RuntimeException("Cannot create an instance of $modelClass", e)
                }
            // 所以最终是调用了父类的create方法,下面接着看父类NewInstanceFactory
            } else super.create(modelClass)
        }

    }

  public open class NewInstanceFactory : Factory {
        @Suppress("DocumentExceptions")
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return try {
                // 好嘛,最后就是简单的反射创建的
                modelClass.newInstance()
            } catch (e: InstantiationException) {
                throw RuntimeException("Cannot create an instance of $modelClass", e)
            } catch (e: IllegalAccessException) {
                throw RuntimeException("Cannot create an instance of $modelClass", e)
            }
        }
    ...略
}

有关Android Jetpack系列之ViewModel的基本逻辑解析的更多相关文章

  1. Ruby 解析字符串 - 2

    我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

  2. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 用逗号、双引号和编码解析 csv - 2

    我正在使用ruby​​1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\

  5. ruby-on-rails - 使用一系列等级计算字母等级 - 2

    这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

  6. ruby-on-rails - 我更新了 ruby​​ gems,现在到处都收到解析树错误和弃用警告! - 2

    简而言之错误:NOTE:Gem::SourceIndex#add_specisdeprecated,useSpecification.add_spec.Itwillberemovedonorafter2011-11-01.Gem::SourceIndex#add_speccalledfrom/opt/local/lib/ruby/site_ruby/1.8/rubygems/source_index.rb:91./opt/local/lib/ruby/gems/1.8/gems/rails-2.3.8/lib/rails/gem_dependency.rb:275:in`==':und

  7. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  8. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  9. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  10. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

随机推荐